Exemplo n.º 1
0
def get_cluster_list(nodename=None, cfg=None):
    if cfg is None:
        cfg = configmanager.ConfigManager(None)
    nodes = None
    if nodename is not None:
        sshpeers = cfg.get_node_attributes(nodename, 'ssh.trustnodes')
        sshpeers = sshpeers.get(nodename, {}).get('ssh.trustnodes',
                                                  {}).get('value', None)
        if sshpeers:
            nodes = noderange.NodeRange(sshpeers, cfg).nodes
    autonodes = False
    if nodes is None:
        autonodes = True
        nodes = set(cfg.list_nodes())
    domain = None
    for node in list(util.natural_sort(nodes)):
        if domain is None:
            domaininfo = cfg.get_node_attributes(node, 'dns.domain')
            domain = domaininfo.get(node, {}).get('dns.domain',
                                                  {}).get('value', None)
        for extraname in get_extra_names(node, cfg):
            nodes.add(extraname)
    if autonodes:
        for mgr in configmanager.list_collective():
            nodes.add(mgr)
            if domain and domain not in mgr:
                nodes.add('{0}.{1}'.format(mgr, domain))
        myname = collective.get_myname()
        nodes.add(myname)
        if domain and domain not in myname:
            nodes.add('{0}.{1}'.format(myname, domain))
    return nodes, domain
Exemplo n.º 2
0
def list_info(parms, requestedparameter):
    #{u'by-switch': u'r8e1', u'by-port': u'e'}
    #by-peerport
    suffix = '/' if requestedparameter in multi_selectors else ''
    results = set([])
    requestedparameter = requestedparameter.replace('by-', '')
    for info in _neighbypeerid:
        if info == '!!vintage':
            continue
        info = _neighbypeerid[info]
        for mk in parms:
            mk = mk.replace('by-', '')
            if mk not in info:
                continue
            if (not close_enough(parms['by-' + mk], info[mk])
                    or requestedparameter not in info):
                break
        else:
            candidate = info[requestedparameter]
            candidate = candidate.strip()
            if candidate != '':
                results.add(_api_sanitize_string(candidate))
    return [
        msg.ChildCollection(x + suffix) for x in util.natural_sort(results)
    ]
Exemplo n.º 3
0
def retrieve_nodes(nodes, element, configmanager, inputdata):
    attributes = configmanager.get_node_attributes(nodes)
    if element[-1] == 'all':
        for node in util.natural_sort(nodes):
            theattrs = set(allattributes.node).union(set(attributes[node]))
            for attribute in sorted(theattrs):
                if attribute in attributes[node]:  # have a setting for it
                    val = attributes[node][attribute]
                elif attribute == 'groups':  # no setting, provide a blank
                    val = []
                else:  # no setting, provide a blank
                    val = {'value': None}
                if attribute.startswith('secret.') or attribute.startswith(
                        'crypted.'):
                    yield msg.CryptedAttributes(
                        node, {attribute: val},
                        allattributes.node.get(attribute,
                                               {}).get('description', ''))
                elif isinstance(val, list):
                    yield msg.ListAttributes(
                        node, {attribute: val},
                        allattributes.node.get(attribute,
                                               {}).get('description', ''))
                else:
                    yield msg.Attributes(
                        node, {attribute: val},
                        allattributes.node.get(attribute,
                                               {}).get('description', ''))
    elif element[-1] == 'current':
        for node in util.natural_sort(list(attributes)):
            for attribute in sorted(attributes[node]):
                currattr = attributes[node][attribute]
                try:
                    desc = allattributes.node[attribute]['description']
                except KeyError:
                    desc = ''
                if 'value' in currattr or 'expression' in currattr:
                    yield msg.Attributes(node, {attribute: currattr}, desc)
                elif 'cryptvalue' in currattr or 'hashvalue' in currattr:
                    yield msg.CryptedAttributes(node, {attribute: currattr},
                                                desc)
                elif isinstance(currattr, list):
                    yield msg.ListAttributes(node, {attribute: currattr}, desc)
                else:
                    print(attribute)
                    print(repr(currattr))
                    raise Exception("BUGGY ATTRIBUTE FOR NODE")
Exemplo n.º 4
0
def list_switches(configmanager):
    nodelocations = configmanager.get_node_attributes(
        configmanager.list_nodes(), ('net*.switch', 'net*.switchport'))
    switches = set([])
    for node in nodelocations:
        cfg = nodelocations[node]
        for attr in cfg:
            if not attr.endswith('.switch') or 'value' not in cfg[attr]:
                continue
            curswitch = cfg[attr].get('value', None)
            if not curswitch:
                continue
            switches.add(curswitch)
    return util.natural_sort(switches)
Exemplo n.º 5
0
def _expand_expression(nodes, configmanager, inputdata):
    expression = inputdata.get_attributes(list(nodes)[0])
    if type(expression) is dict:
        expression = expression['expression']
    if type(expression) is dict:
        expression = expression['expression']
    pernodeexpressions = {}
    try:
        for expanded in configmanager.expand_attrib_expression(
                nodes, expression):
            pernodeexpressions[expanded[0]] = expanded[1]
        for node in util.natural_sort(pernodeexpressions):
            yield msg.KeyValueData({'value': pernodeexpressions[node]}, node)
    except ValueError as e:
        raise exc.InvalidArgumentException(str(e))
Exemplo n.º 6
0
def _expand_expression(nodes, configmanager, inputdata):
    expression = inputdata.get_attributes(list(nodes)[0])
    if type(expression) is dict:
        expression = expression['expression']
    if type(expression) is dict:
        expression = expression['expression']
    pernodeexpressions = {}
    try:
        for expanded in configmanager.expand_attrib_expression(
                nodes, expression):
            pernodeexpressions[expanded[0]] = expanded[1]
        for node in util.natural_sort(pernodeexpressions):
            yield msg.KeyValueData({'value': pernodeexpressions[node]}, node)
    except (SyntaxError, ValueError) as e:
        raise exc.InvalidArgumentException(
            'Bad confluent expression syntax (must use "{{" and "}}" if not '
            'desiring confluent expansion): ' + str(e))
Exemplo n.º 7
0
def handle_read_api_request(pathcomponents, configmanager):
    # TODO(jjohnson2): discovery core.py api handler design, apply it here
    # to make this a less tangled mess as it gets extended
    if len(pathcomponents) == 1:
        return [
            msg.ChildCollection('macs/'),
            msg.ChildCollection('neighbors/')
        ]
    elif pathcomponents[1] == 'neighbors':
        if len(pathcomponents) == 3 and pathcomponents[-1] == 'by-switch':
            return [
                msg.ChildCollection(x + '/')
                for x in list_switches(configmanager)
            ]
        else:
            return _handle_neighbor_query(pathcomponents[2:], configmanager)
    elif len(pathcomponents) == 2:
        if pathcomponents[-1] == 'macs':
            return [
                msg.ChildCollection(x) for x in (  # 'by-node/',
                    'by-mac/', 'by-switch/', 'rescan')
            ]
        elif pathcomponents[-1] == 'neighbors':
            return [msg.ChildCollection('by-switch/')]
        else:
            raise exc.NotFoundException(
                'Unknown networking resource {0}'.format(pathcomponents[-1]))
    if False and pathcomponents[2] == 'by-node':
        # TODO: should be list of node names, and then under that 'by-mac'
        if len(pathcomponents) == 3:
            return [
                msg.ChildCollection(x.replace(':', '-'))
                for x in util.natural_sort(list(_nodesbymac))
            ]
        elif len(pathcomponents) == 4:
            macaddr = pathcomponents[-1].replace('-', ':')
            return dump_macinfo(macaddr)
    elif pathcomponents[2] == 'by-mac':
        if len(pathcomponents) == 3:
            return [
                msg.ChildCollection(x.replace(':', '-'))
                for x in sorted(list(_macmap))
            ]
        elif len(pathcomponents) == 4:
            return dump_macinfo(pathcomponents[-1])
    elif pathcomponents[2] == 'by-switch':
        if len(pathcomponents) == 3:
            return [
                msg.ChildCollection(x + '/')
                for x in list_switches(configmanager)
            ]
        if len(pathcomponents) == 4:
            return [msg.ChildCollection('by-port/')]
        if len(pathcomponents) == 5:
            switchname = pathcomponents[-2]
            if switchname not in _macsbyswitch:
                raise exc.NotFoundException(
                    'No known macs for switch {0}'.format(switchname))
            return [
                msg.ChildCollection(x.replace('/', '-') + '/')
                for x in util.natural_sort(list(_macsbyswitch[switchname]))
            ]
        if len(pathcomponents) == 6:
            return [msg.ChildCollection('by-mac/')]
        if len(pathcomponents) == 7:
            switchname = pathcomponents[-4]
            portname = pathcomponents[-2]
            try:
                if portname not in _macsbyswitch[switchname]:
                    portname = portname.replace('-', '/')
                maclist = _macsbyswitch[switchname][portname]
            except KeyError:
                raise exc.NotFoundException('No known macs for switch {0} '
                                            'port {1}'.format(
                                                switchname, portname))
            return [
                msg.ChildCollection(x.replace(':', '-'))
                for x in sorted(maclist)
            ]
        if len(pathcomponents) == 8:
            return dump_macinfo(pathcomponents[-1])
    raise exc.NotFoundException('Unrecognized path {0}'.format(
        '/'.join(pathcomponents)))
Exemplo n.º 8
0
def _list_interfaces(switchname, configmanager):
    switchcreds = get_switchcreds(configmanager, (switchname,))
    switchcreds = switchcreds[0]
    conn = snmp.Session(*switchcreds)
    ifnames = netutil.get_portnamemap(conn)
    return util.natural_sort(ifnames.values())
Exemplo n.º 9
0
def handle_read_api_request(pathcomponents, configmanager):
    # TODO(jjohnson2): discovery core.py api handler design, apply it here
    # to make this a less tangled mess as it gets extended
    if len(pathcomponents) == 1:
        return [
            msg.ChildCollection('macs/'),
            msg.ChildCollection('neighbors/')
        ]
    elif pathcomponents[1] == 'neighbors':
        if len(pathcomponents) == 3 and pathcomponents[-1] == 'by-switch':
            return [
                msg.ChildCollection(x + '/')
                for x in list_switches(configmanager)
            ]
        else:
            return _handle_neighbor_query(pathcomponents[2:], configmanager)
    elif len(pathcomponents) == 2:
        if pathcomponents[-1] == 'macs':
            return [
                msg.ChildCollection(x) for x in (  # 'by-node/',
                    'by-mac/', 'by-switch/', 'rescan')
            ]
        elif pathcomponents[-1] == 'neighbors':
            return [msg.ChildCollection('by-switch/')]
        else:
            raise exc.NotFoundException(
                'Unknown networking resource {0}'.format(pathcomponents[-1]))
    if False and pathcomponents[2] == 'by-node':
        # TODO: should be list of node names, and then under that 'by-mac'
        if len(pathcomponents) == 3:
            return [
                msg.ChildCollection(x.replace(':', '-'))
                for x in util.natural_sort(list(_nodesbymac))
            ]
        elif len(pathcomponents) == 4:
            macaddr = pathcomponents[-1].replace('-', ':')
            return dump_macinfo(macaddr)
    elif pathcomponents[2] == 'by-mac':
        if len(pathcomponents) == 3:
            return [
                msg.ChildCollection(x.replace(':', '-'))
                for x in sorted(list(_apimacmap))
            ]
        elif len(pathcomponents) == 4:
            return dump_macinfo(pathcomponents[-1])
    elif pathcomponents[2] == 'by-switch':
        if len(pathcomponents) == 3:
            return [
                msg.ChildCollection(x + '/')
                for x in list_switches(configmanager)
            ]
        if len(pathcomponents) == 4:
            return [msg.ChildCollection('by-port/')]
        if len(pathcomponents) == 5:
            switchname = pathcomponents[-2]
            if switchname not in _macsbyswitch:
                raise exc.NotFoundException(
                    'No known macs for switch {0}'.format(switchname))
            return [
                msg.ChildCollection(x.replace('/', '-') + '/')
                for x in util.natural_sort(list(_macsbyswitch[switchname]))
            ]
        if len(pathcomponents) == 6:
            return [msg.ChildCollection('by-mac/')]
        if len(pathcomponents) == 7:
            switchname = pathcomponents[-4]
            portname = pathcomponents[-2]
            try:
                if portname not in _macsbyswitch[switchname]:
                    portname = portname.replace('-', '/')
                maclist = _macsbyswitch[switchname][portname]
            except KeyError:
                foundsomemacs = False
                if switchname in _macsbyswitch:
                    try:
                        matcher = re.compile(portname)
                    except Exception:
                        raise exc.InvalidArgumentException(
                            'Invalid regular expression specified')
                    maclist = []
                    for actualport in _macsbyswitch[switchname]:
                        if bool(matcher.match(actualport)):
                            foundsomemacs = True
                            maclist = maclist + _macsbyswitch[switchname][
                                actualport]
                if not foundsomemacs:
                    raise exc.NotFoundException('No known macs for switch {0} '
                                                'port {1}'.format(
                                                    switchname, portname))
            return [
                msg.ChildCollection(x.replace(':', '-'))
                for x in sorted(maclist)
            ]
        if len(pathcomponents) == 8:
            return dump_macinfo(pathcomponents[-1])
    elif pathcomponents[2] == 'rescan':
        return [msg.KeyValueData({'scanning': mapupdating.locked()})]
    raise exc.NotFoundException('Unrecognized path {0}'.format(
        '/'.join(pathcomponents)))
Exemplo n.º 10
0
def handle_request(env, start_response):
    global currtz
    global keymap
    global currlocale
    global currtzvintage
    configmanager.check_quorum()
    nodename = env.get('HTTP_CONFLUENT_NODENAME', None)
    apikey = env.get('HTTP_CONFLUENT_APIKEY', None)
    if not (nodename and apikey):
        start_response('401 Unauthorized', [])
        yield 'Unauthorized'
        return
    cfg = configmanager.ConfigManager(None)
    ea = cfg.get_node_attributes(nodename,
                                 ['crypted.selfapikey', 'deployment.apiarmed'])
    eak = ea.get(nodename, {}).get('crypted.selfapikey',
                                   {}).get('hashvalue', None)
    if not eak:
        start_response('401 Unauthorized', [])
        yield 'Unauthorized'
        return
    salt = '$'.join(eak.split('$', 3)[:-1]) + '$'
    if crypt.crypt(apikey, salt) != eak:
        start_response('401 Unauthorized', [])
        yield 'Unauthorized'
        return
    if ea.get(nodename, {}).get('deployment.apiarmed', {}).get('value',
                                                               None) == 'once':
        cfg.set_node_attributes({nodename: {'deployment.apiarmed': ''}})
    retype = env.get('HTTP_ACCEPT', 'application/yaml')
    isgeneric = False
    if retype == '*/*':
        isgeneric = True
        retype = 'application/yaml'
    if retype == 'application/yaml':
        dumper = yamldump
    elif retype == 'application/json':
        dumper = json.dumps
    else:
        start_response('406 Not supported', [])
        yield 'Unsupported content type in ACCEPT: ' + retype
        return
    operation = env['REQUEST_METHOD']
    if operation not in ('HEAD', 'GET') and 'CONTENT_LENGTH' in env and int(
            env['CONTENT_LENGTH']) > 0:
        reqbody = env['wsgi.input'].read(int(env['CONTENT_LENGTH']))
    if env['PATH_INFO'] == '/self/bmcconfig':
        hmattr = cfg.get_node_attributes(nodename, 'hardwaremanagement.*')
        hmattr = hmattr.get(nodename, {})
        res = {}
        port = hmattr.get('hardwaremanagement.port', {}).get('value', None)
        if port is not None:
            res['bmcport'] = port
        vlan = hmattr.get('hardwaremanagement.vlan', {}).get('value', None)
        if vlan is not None:
            res['bmcvlan'] = vlan
        bmcaddr = hmattr.get('hardwaremanagement.manager',
                             {}).get('value', None)
        bmcaddr = socket.getaddrinfo(bmcaddr, 0)[0]
        bmcaddr = bmcaddr[-1][0]
        if '.' in bmcaddr:  # ipv4 is allowed
            netconfig = netutil.get_nic_config(cfg, nodename, ip=bmcaddr)
            res['bmcipv4'] = bmcaddr
            res['prefixv4'] = netconfig['prefix']
            res['bmcgw'] = netconfig.get('ipv4_gateway', None)
        # credential security results in user/password having to be deferred
        start_response('200 OK', (('Content-Type', retype), ))
        yield dumper(res)
    elif env['PATH_INFO'] == '/self/deploycfg':
        if 'HTTP_CONFLUENT_MGTIFACE' in env:
            ncfg = netutil.get_nic_config(cfg,
                                          nodename,
                                          ifidx=env['HTTP_CONFLUENT_MGTIFACE'])
        else:
            myip = env.get('HTTP_X_FORWARDED_HOST', None)
            if ']' in myip:
                myip = myip.split(']', 1)[0]
            else:
                myip = myip.split(':', 1)[0]
            myip = myip.replace('[', '').replace(']', '')
            ncfg = netutil.get_nic_config(cfg, nodename, serverip=myip)
        if ncfg['prefix']:
            ncfg['ipv4_netmask'] = netutil.cidr_to_mask(ncfg['prefix'])
        if ncfg['ipv4_method'] == 'firmwaredhcp':
            ncfg['ipv4_method'] = 'static'
        deployinfo = cfg.get_node_attributes(
            nodename,
            ('deployment.*', 'console.method', 'crypted.*', 'dns.*', 'ntp.*'))
        deployinfo = deployinfo.get(nodename, {})
        profile = deployinfo.get('deployment.pendingprofile',
                                 {}).get('value', '')
        ncfg['encryptboot'] = deployinfo.get('deployment.encryptboot',
                                             {}).get('value', None)
        if ncfg['encryptboot'] in ('', 'none'):
            ncfg['encryptboot'] = None
        ncfg['profile'] = profile
        protocol = deployinfo.get('deployment.useinsecureprotocols',
                                  {}).get('value', 'never')
        ncfg['textconsole'] = bool(
            deployinfo.get('console.method', {}).get('value', None))
        if protocol == 'always':
            ncfg['protocol'] = 'http'
        else:
            ncfg['protocol'] = 'https'
        ncfg['rootpassword'] = deployinfo.get('crypted.rootpassword',
                                              {}).get('hashvalue', None)
        ncfg['grubpassword'] = deployinfo.get('crypted.grubpassword',
                                              {}).get('grubhashvalue', None)
        if currtzvintage and currtzvintage > (time.time() - 30.0):
            ncfg['timezone'] = currtz
        else:
            langinfo = subprocess.check_output(['localectl',
                                                'status']).split(b'\n')
            for line in langinfo:
                line = line.strip()
                if line.startswith(b'System Locale:'):
                    ccurrlocale = line.split(b'=')[-1]
                    if not ccurrlocale:
                        continue
                    if not isinstance(ccurrlocale, str):
                        ccurrlocale = ccurrlocale.decode('utf8')
                    if ccurrlocale == 'n/a':
                        continue
                    currlocale = ccurrlocale
                elif line.startswith(b'VC Keymap:'):
                    ckeymap = line.split(b':')[-1]
                    ckeymap = ckeymap.strip()
                    if not ckeymap:
                        continue
                    if not isinstance(ckeymap, str):
                        ckeymap = ckeymap.decode('utf8')
                    if ckeymap == 'n/a':
                        continue
                    keymap = ckeymap
            tdc = subprocess.check_output(['timedatectl']).split(b'\n')
            for ent in tdc:
                ent = ent.strip()
                if ent.startswith(b'Time zone:'):
                    currtz = ent.split(b': ', 1)[1].split(b'(', 1)[0].strip()
                    if not isinstance(currtz, str):
                        currtz = currtz.decode('utf8')
                    currtzvintage = time.time()
                    ncfg['timezone'] = currtz
                    break
        ncfg['locale'] = currlocale
        ncfg['keymap'] = keymap
        ncfg['nameservers'] = []
        for dns in deployinfo.get('dns.servers', {}).get('value',
                                                         '').split(','):
            ncfg['nameservers'].append(dns)
        ntpsrvs = deployinfo.get('ntp.servers', {}).get('value', '')
        if ntpsrvs:
            ntpsrvs = ntpsrvs.split(',')
        if ntpsrvs:
            ncfg['ntpservers'] = []
            for ntpsrv in ntpsrvs:
                ncfg['ntpservers'].append(ntpsrv)
        dnsdomain = deployinfo.get('dns.domain', {}).get('value', None)
        ncfg['dnsdomain'] = dnsdomain
        start_response('200 OK', (('Content-Type', retype), ))
        yield dumper(ncfg)
    elif env['PATH_INFO'] == '/self/sshcert' and reqbody:
        if not sshutil.ca_exists():
            start_response('500 Unconfigured', ())
            yield 'CA is not configured on this system (run ...)'
            return
        pals = get_extra_names(nodename, cfg)
        cert = sshutil.sign_host_key(reqbody, nodename, pals)
        start_response('200 OK', (('Content-Type', 'text/plain'), ))
        yield cert
    elif env['PATH_INFO'] == '/self/nodelist':
        nodes, _ = get_cluster_list(nodename, cfg)
        if isgeneric:
            start_response('200 OK', (('Content-Type', 'text/plain'), ))
            for node in util.natural_sort(nodes):
                yield node + '\n'
        else:
            start_response('200 OK', (('Content-Type', retype), ))
            yield dumper(sorted(nodes))
    elif env['PATH_INFO'] == '/self/remoteconfigbmc' and reqbody:
        try:
            reqbody = yaml.safe_load(reqbody)
        except Exception:
            reqbody = None
        cfgmod = reqbody.get('configmod', 'unspecified')
        if cfgmod == 'xcc':
            xcc.remote_nodecfg(nodename, cfg)
        elif cfgmod == 'tsm':
            tsm.remote_nodecfg(nodename, cfg)
        else:
            start_response('500 unsupported configmod', ())
            yield 'Unsupported configmod "{}"'.format(cfgmod)
        start_response('200 Ok', ())
        yield 'complete'
    elif env['PATH_INFO'] == '/self/updatestatus' and reqbody:
        update = yaml.safe_load(reqbody)
        if update['status'] == 'staged':
            targattr = 'deployment.stagedprofile'
        elif update['status'] == 'complete':
            targattr = 'deployment.profile'
        else:
            raise Exception('Unknown update status request')
        currattr = cfg.get_node_attributes(nodename,
                                           'deployment.*').get(nodename, {})
        pending = None
        if targattr == 'deployment.profile':
            pending = currattr.get('deployment.stagedprofile',
                                   {}).get('value', '')
        if not pending:
            pending = currattr.get('deployment.pendingprofile',
                                   {}).get('value', '')
        updates = {}
        if pending:
            updates['deployment.pendingprofile'] = {'value': ''}
            if targattr == 'deployment.profile':
                updates['deployment.stagedprofile'] = {'value': ''}
            currprof = currattr.get(targattr, {}).get('value', '')
            if currprof != pending:
                updates[targattr] = {'value': pending}
            cfg.set_node_attributes({nodename: updates})
            start_response('200 OK', (('Content-Type', 'text/plain'), ))
            yield 'OK'
        else:
            start_response('500 Error', (('Content-Type', 'text/plain'), ))
            yield 'No pending profile detected, unable to accept status update'
    elif env['PATH_INFO'] == '/self/saveapikey' and reqbody:
        if not isinstance(reqbody, str):
            reqbody = reqbody.decode('utf8')
        cfg.set_node_attributes(
            {nodename: {
                'deployment.sealedapikey': {
                    'value': reqbody
                }
            }})
        start_response('200 OK', ())
        yield ''
    elif env['PATH_INFO'].startswith(
            '/self/remoteconfig/') and 'POST' == operation:
        scriptcat = env['PATH_INFO'].replace('/self/remoteconfig/', '')
        slist, profile = get_scriptlist(
            scriptcat, cfg, nodename,
            '/var/lib/confluent/public/os/{0}/ansible/{1}')
        playlist = []
        dirname = '/var/lib/confluent/public/os/{0}/ansible/{1}/'.format(
            profile, scriptcat)
        if not os.path.isdir(dirname):
            dirname = '/var/lib/confluent/public/os/{0}/ansible/{1}.d/'.format(
                profile, scriptcat)
        for filename in slist:
            if filename.endswith('.yaml') or filename.endswith('.yml'):
                playlist.append(os.path.join(dirname, filename))
        if playlist:
            runansible.run_playbooks(playlist, [nodename])
            start_response('202 Queued', ())
            yield ''
        else:
            start_response('200 OK', ())
            yield ''
            return
    elif env['PATH_INFO'].startswith('/self/remotesyncfiles'):
        if 'POST' == operation:
            result = syncfiles.start_syncfiles(nodename, cfg,
                                               json.loads(reqbody))
            start_response(result, ())
            yield ''
            return
        if 'GET' == operation:
            status, output = syncfiles.get_syncresult(nodename)
            start_response(status, ())
            yield output
            return
    elif env['PATH_INFO'].startswith('/self/remoteconfig/status'):
        rst = runansible.running_status.get(nodename, None)
        if not rst:
            start_response('204 Not Running', (('Content-Length', '0'), ))
            yield ''
            return
        start_response('200 OK', ())
        if rst.complete:
            del runansible.running_status[nodename]
        yield rst.dump_text()
        return
    elif env['PATH_INFO'].startswith('/self/scriptlist/'):
        scriptcat = env['PATH_INFO'].replace('/self/scriptlist/', '')
        slist, _ = get_scriptlist(
            scriptcat, cfg, nodename,
            '/var/lib/confluent/public/os/{0}/scripts/{1}')
        if slist:
            start_response('200 OK', (('Content-Type', 'application/yaml'), ))
            yield yaml.safe_dump(util.natural_sort(slist),
                                 default_flow_style=False)
        else:
            start_response('200 OK', ())
            yield ''
    else:
        start_response('404 Not Found', ())
        yield 'Not found'
Exemplo n.º 11
0
def handle_request(env, start_response):
    global currtz
    global keymap
    global currlocale
    global currtzvintage
    configmanager.check_quorum()
    nodename = env.get('HTTP_CONFLUENT_NODENAME', None)
    apikey = env.get('HTTP_CONFLUENT_APIKEY', None)
    if not (nodename and apikey):
        start_response('401 Unauthorized', [])
        yield 'Unauthorized'
        return
    cfg = configmanager.ConfigManager(None)
    eak = cfg.get_node_attributes(nodename, 'crypted.selfapikey').get(
        nodename, {}).get('crypted.selfapikey', {}).get('hashvalue', None)
    if not eak:
        start_response('401 Unauthorized', [])
        yield 'Unauthorized'
        return
    salt = '$'.join(eak.split('$', 3)[:-1]) + '$'
    if crypt.crypt(apikey, salt) != eak:
        start_response('401 Unauthorized', [])
        yield 'Unauthorized'
        return
    retype = env.get('HTTP_ACCEPT', 'application/yaml')
    isgeneric = False
    if retype == '*/*':
        isgeneric = True
        retype = 'application/yaml'
    if retype == 'application/yaml':
        dumper = yamldump
    elif retype == 'application/json':
        dumper = json.dumps
    else:
        start_response('406 Not supported', [])
        yield 'Unsupported content type in ACCEPT: ' + retype
        return
    if env['REQUEST_METHOD'] not in (
            'HEAD', 'GET') and 'CONTENT_LENGTH' in env and int(
                env['CONTENT_LENGTH']) > 0:
        reqbody = env['wsgi.input'].read(int(env['CONTENT_LENGTH']))
    if env['PATH_INFO'] == '/self/deploycfg':
        if 'HTTP_CONFLUENT_MGTIFACE' in env:
            ncfg = netutil.get_nic_config(cfg,
                                          nodename,
                                          ifidx=env['HTTP_CONFLUENT_MGTIFACE'])
        else:
            myip = env.get('HTTP_X_FORWARDED_HOST', None)
            if ']' in myip:
                myip = myip.split(']', 1)[0]
            else:
                myip = myip.split(':', 1)[0]
            myip = myip.replace('[', '').replace(']', '')
            ncfg = netutil.get_nic_config(cfg, nodename, serverip=myip)
        if ncfg['prefix']:
            ncfg['ipv4_netmask'] = netutil.cidr_to_mask(ncfg['prefix'])
        deployinfo = cfg.get_node_attributes(
            nodename, ('deployment.*', 'console.method', 'crypted.*', 'dns.*'))
        deployinfo = deployinfo.get(nodename, {})
        profile = deployinfo.get('deployment.pendingprofile',
                                 {}).get('value', '')
        ncfg['encryptboot'] = deployinfo.get('deployment.encryptboot',
                                             {}).get('value', None)
        if ncfg['encryptboot'] in ('', 'none'):
            ncfg['encryptboot'] = None
        ncfg['profile'] = profile
        protocol = deployinfo.get('deployment.useinsecureprotocols',
                                  {}).get('value', 'never')
        ncfg['textconsole'] = bool(
            deployinfo.get('console.method', {}).get('value', None))
        if protocol == 'always':
            ncfg['protocol'] = 'http'
        else:
            ncfg['protocol'] = 'https'
        ncfg['rootpassword'] = deployinfo.get('crypted.rootpassword',
                                              {}).get('hashvalue', None)
        ncfg['grubpassword'] = deployinfo.get('crypted.grubpassword',
                                              {}).get('grubhashvalue', None)
        if currtzvintage and currtzvintage > (time.time() - 30.0):
            ncfg['timezone'] = currtz
        else:
            langinfo = subprocess.check_output(['localectl',
                                                'status']).split(b'\n')
            for line in langinfo:
                line = line.strip()
                if line.startswith(b'System Locale:'):
                    ccurrlocale = line.split(b'=')[-1]
                    if not ccurrlocale:
                        continue
                    if not isinstance(ccurrlocale, str):
                        ccurrlocale = ccurrlocale.decode('utf8')
                    if ccurrlocale == 'n/a':
                        continue
                    currlocale = ccurrlocale
                elif line.startswith(b'VC Keymap:'):
                    ckeymap = line.split(b':')[-1]
                    ckeymap = ckeymap.strip()
                    if not ckeymap:
                        continue
                    if not isinstance(ckeymap, str):
                        ckeymap = ckeymap.decode('utf8')
                    if ckeymap == 'n/a':
                        continue
                    keymap = ckeymap
            tdc = subprocess.check_output(['timedatectl']).split(b'\n')
            for ent in tdc:
                ent = ent.strip()
                if ent.startswith(b'Time zone:'):
                    currtz = ent.split(b': ', 1)[1].split(b'(', 1)[0].strip()
                    if not isinstance(currtz, str):
                        currtz = currtz.decode('utf8')
                    currtzvintage = time.time()
                    ncfg['timezone'] = currtz
                    break
        ncfg['locale'] = currlocale
        ncfg['keymap'] = keymap
        ncfg['nameservers'] = []
        for dns in deployinfo.get('dns.servers', {}).get('value',
                                                         '').split(','):
            ncfg['nameservers'].append(dns)
        dnsdomain = deployinfo.get('dns.domain', {}).get('value', None)
        ncfg['dnsdomain'] = dnsdomain
        start_response('200 OK', (('Content-Type', retype), ))
        yield dumper(ncfg)
    elif env['PATH_INFO'] == '/self/sshcert':
        if not sshutil.ca_exists():
            start_response('500 Unconfigured', ())
            yield 'CA is not configured on this system (run ...)'
            return
        dnsinfo = cfg.get_node_attributes(nodename, ('dns.*'))
        dnsinfo = dnsinfo.get(nodename, {}).get('dns.domain',
                                                {}).get('value', None)
        if dnsinfo in nodename:
            dnsinfo = ''
        cert = sshutil.sign_host_key(reqbody, nodename, [dnsinfo])
        start_response('200 OK', (('Content-Type', 'text/plain'), ))
        yield cert
    elif env['PATH_INFO'] == '/self/nodelist':
        nodes = set(cfg.list_nodes())
        domaininfo = cfg.get_node_attributes(nodes, 'dns.domain')
        for node in list(util.natural_sort(nodes)):
            domain = domaininfo.get(node, {}).get('dns.domain',
                                                  {}).get('value', None)
            if domain and domain not in node:
                nodes.add('{0}.{1}'.format(node, domain))
        for mgr in configmanager.list_collective():
            nodes.add(mgr)
            if domain and domain not in mgr:
                nodes.add('{0}.{1}'.format(mgr, domain))
        myname = collective.get_myname()
        nodes.add(myname)
        if domain and domain not in myname:
            nodes.add('{0}.{1}'.format(myname, domain))
        if isgeneric:
            start_response('200 OK', (('Content-Type', 'text/plain'), ))
            for node in util.natural_sort(nodes):
                yield node + '\n'
        else:
            start_response('200 OK', (('Content-Type', retype), ))
            yield dumper(sorted(nodes))
    elif env['PATH_INFO'] == '/self/updatestatus':
        update = yaml.safe_load(reqbody)
        if update['status'] == 'staged':
            targattr = 'deployment.stagedprofile'
        elif update['status'] == 'complete':
            targattr = 'deployment.profile'
        else:
            raise Exception('Unknown update status request')
        currattr = cfg.get_node_attributes(nodename,
                                           'deployment.*').get(nodename, {})
        pending = None
        if targattr == 'deployment.profile':
            pending = currattr.get('deployment.stagedprofile',
                                   {}).get('value', '')
        if not pending:
            pending = currattr.get('deployment.pendingprofile',
                                   {}).get('value', '')
        updates = {}
        if pending:
            updates['deployment.pendingprofile'] = {'value': ''}
            if targattr == 'deployment.profile':
                updates['deployment.stagedprofile'] = {'value': ''}
            currprof = currattr.get(targattr, {}).get('value', '')
            if currprof != pending:
                updates[targattr] = {'value': pending}
            cfg.set_node_attributes({nodename: updates})
            start_response('200 OK', (('Content-Type', 'text/plain'), ))
            yield 'OK'
        else:
            start_response('500 Error', (('Content-Type', 'text/plain'), ))
            yield 'No pending profile detected, unable to accept status update'
    else:
        start_response('404 Not Found', ())
        yield 'Not found'