#!/usr/bin/python2 -s
# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright 2017 Lenovo
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


import os
import signal
import optparse
import sys

try:
    signal.signal(signal.SIGPIPE, signal.SIG_DFL)
except AttributeError:
    pass
path = os.path.dirname(os.path.realpath(__file__))
path = os.path.realpath(os.path.join(path, '..', 'lib', 'python'))
if path.startswith('/opt'):
    sys.path.append(path)

import confluent.client as client

class NullOpt(object):
    blame = None
    clear = None


def bailout(msg, code=1):
    sys.stderr.write(msg + '\n')
    sys.exit(code)


argparser = optparse.OptionParser()
argparser.add_option('-c', '--comparedefault', dest='comparedefault',
                     action='store_true', default=False,
                     help='Compare given settings to default or list settings '
                          'that are non default')
argparser.add_option('-d', '--detail', dest='detail',
                     action='store_true', default=False,
                     help='Provide verbose information as available, such as '
                          'help text and possible valid values')
argparser.add_option('-x', '--exclude', dest='exclude',
                     action='store_true', default=False,
                     help='Treat positional arguments as items to not '
                          'examine, compare, or restore default')
(options, args) = argparser.parse_args()

cfgpaths = {
    'bmc.ipv4_address': (
        'configuration/management_controller/net_interfaces/management',
        'ipv4_address'),
    'bmc.ipv4_method': (
        'configuration/management_controller/net_interfaces/management',
        'ipv4_configuration'),
    'bmc.ipv4_gateway': (
        'configuration/management_controller/net_interfaces/management',
        'ipv4_gateway'),
}

autodeps = {
    'bmc.ipv4_address': (('bmc.ipv4_method', 'static'),)
}

try:
    noderange = args[0]
except IndexError:
    argparser.print_help()
    sys.exit(1)
client.check_globbing(noderange)
setmode = None
assignment = {}
queryparms = {}
printsys = []
setsys = {}
forceset = False
needval = None

if len(args) == 1 or options.exclude:
    if not options.exclude:
        printsys = 'all'
    for candidate in cfgpaths:
        path, attrib = cfgpaths[candidate]
        path = '/noderange/{0}/{1}'.format(noderange, path)
        if path not in queryparms:
            queryparms[path] = {}
        queryparms[path][attrib] = candidate


def _assign_value():
    if key not in cfgpaths:
        setsys[key] = value
    for depkey, depval in autodeps.get(key, []):
        assignment[depkey] = depval
    assignment[key] = value


for param in args[1:]:
    if param == 'show':
        continue  # forgive muscle memory of pasu users
    if param == 'set':
        setmode = True
        forceset = True
        continue
    if needval:
        key = needval
        value = param
        _assign_value()
        continue
    if '=' in param or param[-1] == ':' or forceset:
        if setmode is None:
            setmode = True
        if setmode != True:
            bailout('Cannot do set and query in same command')
        if '=' in param:
            key, _, value = param.partition('=')
            _assign_value()
        elif param[-1] == ':':
            needval = param[:-1]
        else:
            needval = param
    else:
        if setmode is None:
            setmode = False
        if setmode != False:
            bailout('Cannot do set and query in same command')
        if '.' not in param:
            matchedparms = False
            for candidate in cfgpaths:
                if candidate.startswith('{0}.'.format(param)):
                    matchedparms = True
                    if not options.exclude:
                        path, attrib = cfgpaths[candidate]
                        path = '/noderange/{0}/{1}'.format(noderange, path)
                        if path not in queryparms:
                            queryparms[path] = {}
                        queryparms[path][attrib] = candidate
                    else:
                        try:
                            del queryparms[path]
                        except KeyError:
                            pass
            if not matchedparms:
                printsys.append(param)
        elif param not in cfgpaths:
            printsys.append(param)
        else:
            path, attrib = cfgpaths[param]
            path = '/noderange/{0}/{1}'.format(noderange, path)
            if path not in queryparms:
                queryparms[path] = {}
            queryparms[path][attrib] = param
session = client.Command()
rcode = 0
if setmode:
    if options.exclude:
        sys.stderr.write('Cannot use exclude and assign at the same time\n')
        sys.exit(1)
    updatebypath = {}
    attrnamebypath = {}
    for key in assignment:
        if key not in cfgpaths:
            path = 'configuration/system/all'
            attrib = key
        else:
            path, attrib = cfgpaths[key]
        if path not in updatebypath:
            updatebypath[path] = {}
            attrnamebypath[path] = {}
        updatebypath[path][attrib] = assignment[key]
        attrnamebypath[path][attrib] = key
    # well, we want to expand things..
    # check ipv4, if requested change method to static
    for path in updatebypath:
        for fr in session.update('/noderange/{0}/{1}'.format(noderange, path),
                                updatebypath[path]):
            for node in fr['databynode']:
                r = fr['databynode'][node]
                if 'error' in r:
                    sys.stderr.write(node + ': ' + r['error'] + '\n')
                    if 'errorcode' in r:
                        rcode |= r['errorcode']
                if 'value' not in r:
                    continue
                keyval = r['value']
                key, val = keyval.split('=')
                if key in attrnamebypath[path]:
                    key = attrnamebypath[path][key]
                print('{0}: {1}: {2}'.format(node, key, val))
else:
    for path in queryparms:
        if options.comparedefault:
            continue
        client.print_attrib_path(path, session, list(queryparms[path]),
                                 NullOpt(), queryparms[path])
    if printsys or options.exclude:
        if printsys == 'all':
            printsys = []
        path = '/noderange/{0}/configuration/system/all'.format(noderange)
        client.print_attrib_path(path, session, printsys,
                                 options)
sys.exit(rcode)