def _webconfignet(self, wc, nodename): cfg = self.configmanager cd = cfg.get_node_attributes(nodename, ['hardwaremanagement.manager']) smmip = cd.get(nodename, {}).get('hardwaremanagement.manager', {}).get('value', None) if smmip and ':' not in smmip: smmip = getaddrinfo(smmip, 0)[0] smmip = smmip[-1][0] if smmip and ':' in smmip: raise exc.NotImplementedException('IPv6 not supported') netconfig = netutil.get_nic_config(cfg, nodename, ip=smmip) netmask = netutil.cidr_to_mask(netconfig['prefix']) setdata = 'set=ifIndex:0,v4DHCPEnabled:0,v4IPAddr:{0},v4NetMask:{1}'.format( smmip, netmask) gateway = netconfig.get('ipv4_gateway', None) if gateway: setdata += ',v4Gateway:{0}'.format(gateway) wc.request('POST', '/data', setdata) rsp = wc.getresponse() rspdata = rsp.read() if '<statusCode>0' not in rspdata: raise Exception("Error configuring SMM Network") return if smmip and ':' in smmip and not smmip.startswith('fe80::'): raise exc.NotImplementedException('IPv6 configuration TODO') if self.ipaddr.startswith('fe80::'): cfg.set_node_attributes( {nodename: { 'hardwaremanagement.manager': self.ipaddr }})
def _webconfignet(self, wc, nodename): cfg = self.configmanager if 'service:lenovo-smm2' in self.info.get('services', []): # need to enable ipmi for now.. wc.request('POST', '/data', 'set=DoCmd(0x06,0x40,0x01,0x82,0x84)') rsp = wc.getresponse() rsp.read() wc.request('POST', '/data', 'set=DoCmd(0x06,0x40,0x01,0x42,0x44)') rsp = wc.getresponse() rsp.read() cd = cfg.get_node_attributes(nodename, ['hardwaremanagement.manager']) smmip = cd.get(nodename, {}).get('hardwaremanagement.manager', {}).get('value', None) if smmip and ':' not in smmip: smmip = getaddrinfo(smmip, 0)[0] smmip = smmip[-1][0] if smmip and ':' in smmip: raise exc.NotImplementedException('IPv6 not supported') wc.request('POST', '/data', 'get=hostname') rsp = wc.getresponse() rspdata = fromstring(util.stringify(rsp.read())) currip = rspdata.find('netConfig').find('ifConfigEntries').find( 'ifConfig').find('v4IPAddr').text if currip == smmip: return netconfig = netutil.get_nic_config(cfg, nodename, ip=smmip) netmask = netutil.cidr_to_mask(netconfig['prefix']) setdata = 'set=ifIndex:0,v4DHCPEnabled:0,v4IPAddr:{0},v4NetMask:{1}'.format( smmip, netmask) gateway = netconfig.get('ipv4_gateway', None) if gateway: setdata += ',v4Gateway:{0}'.format(gateway) wc.request('POST', '/data', setdata) rsp = wc.getresponse() rspdata = util.stringify(rsp.read()) if '<statusCode>0' not in rspdata: raise Exception("Error configuring SMM Network") return if smmip and ':' in smmip and not smmip.startswith('fe80::'): raise exc.NotImplementedException('IPv6 configuration TODO') if self.ipaddr.startswith('fe80::'): cfg.set_node_attributes( {nodename: { 'hardwaremanagement.manager': self.ipaddr }})
def config(self, nodename, reset=False): self.nodename = nodename # TODO(jjohnson2): set ip parameters, user/pass, alert cfg maybe # In general, try to use https automation, to make it consistent # between hypothetical secure path and today. dpp = self.configmanager.get_node_attributes( nodename, 'discovery.passwordrules') strruleset = dpp.get(nodename, {}).get( 'discovery.passwordrules', {}).get('value', '') wc = self.wc creds = self.configmanager.get_node_attributes( self.nodename, ['secret.hardwaremanagementuser', 'secret.hardwaremanagementpassword'], decrypt=True) user, passwd, isdefault = self.get_node_credentials(nodename, creds, 'USERID', 'PASSW0RD') self.set_password_policy(strruleset) if self._atdefaultcreds: if not isdefault: self._setup_xcc_account(user, passwd, wc) self._convert_sha256account(user, passwd, wc) cd = self.configmanager.get_node_attributes( nodename, ['secret.hardwaremanagementuser', 'secret.hardwaremanagementpassword', 'hardwaremanagement.manager', 'hardwaremanagement.method', 'console.method'], True) cd = cd.get(nodename, {}) if (cd.get('hardwaremanagement.method', {}).get('value', 'ipmi') != 'redfish' or cd.get('console.method', {}).get('value', None) == 'ipmi'): nwc = wc.dupe() nwc.set_basic_credentials(self._currcreds[0], self._currcreds[1]) rsp = nwc.grab_json_response('/redfish/v1/Managers/1/NetworkProtocol') if not rsp.get('IPMI', {}).get('ProtocolEnabled', True): # User has indicated IPMI support, but XCC is currently disabled # change XCC to be consistent _, _ = nwc.grab_json_response_with_status( '/redfish/v1/Managers/1/NetworkProtocol', {'IPMI': {'ProtocolEnabled': True}}, method='PATCH') if ('hardwaremanagement.manager' in cd and cd['hardwaremanagement.manager']['value'] and not cd['hardwaremanagement.manager']['value'].startswith( 'fe80::')): newip = cd['hardwaremanagement.manager']['value'] newipinfo = getaddrinfo(newip, 0)[0] newip = newipinfo[-1][0] if ':' in newip: raise exc.NotImplementedException('IPv6 remote config TODO') netconfig = netutil.get_nic_config(self.configmanager, nodename, ip=newip) newmask = netutil.cidr_to_mask(netconfig['prefix']) currinfo = wc.grab_json_response('/api/providers/logoninfo') currip = currinfo.get('items', [{}])[0].get('ipv4_address', '') # do not change the ipv4_config if the current config looks right already if currip != newip: statargs = { 'ENET_IPv4Ena': '1', 'ENET_IPv4AddrSource': '0', 'ENET_IPv4StaticIPAddr': newip, 'ENET_IPv4StaticIPNetMask': newmask } if netconfig['ipv4_gateway']: statargs['ENET_IPv4GatewayIPAddr'] = netconfig['ipv4_gateway'] wc.grab_json_response('/api/dataset', statargs) elif self.ipaddr.startswith('fe80::'): self.configmanager.set_node_attributes( {nodename: {'hardwaremanagement.manager': self.ipaddr}}) else: raise exc.TargetEndpointUnreachable( 'hardwaremanagement.manager must be set to desired address (No IPv6 Link Local detected)') wc.grab_json_response('/api/providers/logout') ff = self.info.get('attributes', {}).get('enclosure-form-factor', '') if ff not in ('dense-computing', [u'dense-computing']): return enclosureuuid = self.info.get('attributes', {}).get('chassis-uuid', [None])[0] if enclosureuuid: enclosureuuid = enclosureuuid.lower() em = self.configmanager.get_node_attributes(nodename, 'enclosure.manager') em = em.get(nodename, {}).get('enclosure.manager', {}).get( 'value', None) # ok, set the uuid of the manager... if em: self.configmanager.set_node_attributes( {em: {'id.uuid': enclosureuuid}})
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'
def config(self, nodename): self.nodename = nodename creds = self.configmanager.get_node_attributes( nodename, ['secret.hardwaremanagementuser', 'secret.hardwaremanagementpassword', 'hardwaremanagement.manager', 'hardwaremanagement.method', 'console.method'], True) cd = creds.get(nodename, {}) user, passwd, _ = self.get_node_credentials( nodename, creds, self.DEFAULT_USER, self.DEFAULT_PASS) user = util.stringify(user) passwd = util.stringify(passwd) self.targuser = user self.targpass = passwd wc = self._get_wc() wc.set_header('X-CSRFTOKEN', self.csrftok) curruserinfo = {} authupdate = False wc.set_header('Content-Type', 'application/json') if user != self.curruser: authupdate = True if not curruserinfo: curruserinfo = wc.grab_json_response('/api/settings/users') authchg = curruserinfo[1] authchg['name'] = user if passwd != self.currpass: authupdate = True if not curruserinfo: curruserinfo = wc.grab_json_response('/api/settings/users') authchg = curruserinfo[1] authchg['changepassword'] = 0 authchg['password_size'] = 'bytes_20' authchg['password'] = passwd authchg['confirm_password'] = passwd if authupdate: rsp, status = wc.grab_json_response_with_status('/api/settings/users/2', authchg, method='PUT') if (cd.get('hardwaremanagement.method', {}).get('value', 'ipmi') != 'redfish' or cd.get('console.method', {}).get('value', None) == 'ipmi'): # IPMI must be enabled per user config wc.grab_json_response('/api/settings/ipmilanconfig', { 'ipv4_enable': 1, 'ipv6_enable': 1, 'uncheckedipv4lanEnable': 0, 'uncheckedipv6lanEnable': 0, 'checkedipv4lanEnable': 1, 'checkedipv6lanEnable': 1}) if ('hardwaremanagement.manager' in cd and cd['hardwaremanagement.manager']['value'] and not cd['hardwaremanagement.manager']['value'].startswith( 'fe80::')): newip = cd['hardwaremanagement.manager']['value'] newipinfo = getaddrinfo(newip, 0)[0] newip = newipinfo[-1][0] if ':' in newip: raise exc.NotImplementedException('IPv6 remote config TODO') currnet = wc.grab_json_response('/api/settings/network') for net in currnet: if net['channel_number'] == self.channel and net['lan_enable'] == 0: # ignore false indication and switch to 8 (dedicated) self.channel = 8 if net['channel_number'] == self.channel: # we have found the interface to potentially manipulate if net['ipv4_address'] != newip: netconfig = netutil.get_nic_config(self.configmanager, nodename, ip=newip) newmask = netutil.cidr_to_mask(netconfig['prefix']) net['ipv4_address'] = newip net['ipv4_subnet'] = newmask if netconfig['ipv4_gateway']: net['ipv4_gateway'] = netconfig['ipv4_gateway'] net['ipv4_dhcp_enable'] = 0 rsp, status = wc.grab_json_response_with_status( '/api/settings/network/{0}'.format(net['id']), net, method='PUT') break elif self.ipaddr.startswith('fe80::'): self.configmanager.set_node_attributes( {nodename: {'hardwaremanagement.manager': self.ipaddr}}) else: raise exc.TargetEndpointUnreachable( 'hardwaremanagement.manager must be set to desired address (No IPv6 Link Local detected)') rsp, status = wc.grab_json_response_with_status('/api/session', method='DELETE')
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'