def connect(self, callback): self.datacallback = callback # we provide a weak reference to pyghmi as otherwise we'd # have a circular reference and reference counting would never get # out... try: self.solconnection = console.Console(bmc=self.bmc, port=self.port, userid=self.username, password=self.password, kg=self.kg, force=True, iohandler=self.handle_data) while not self.solconnection.connected and not self.broken: w = eventlet.event.Event() _ipmiwaiters.append(w) w.wait() if self.broken: break if self.broken: if (self.error.startswith('Incorrect password') or self.error.startswith('Unauthorized name')): raise exc.TargetEndpointBadCredentials else: raise exc.TargetEndpointUnreachable(self.error) self.connected = True except socket.gaierror as err: raise exc.TargetEndpointUnreachable(str(err))
def __init__(self, operation, node, element, cfd, inputdata, cfg, output): self.sensormap = {} self.invmap = {} self.output = output self.sensorcategory = None self.broken = False self.error = None eventlet.sleep(0) self.cfg = cfd[node] self.loggedin = False self.node = node self.element = element self.op = operation connparams = get_conn_params(node, self.cfg) self.ipmicmd = None self.inputdata = inputdata self.tenant = cfg.tenant tenant = cfg.tenant if ((node, tenant) not in persistent_ipmicmds or not persistent_ipmicmds[(node, tenant)].ipmi_session.logged): try: persistent_ipmicmds[(node, tenant)].close_confluent() except KeyError: # was no previous session pass try: persistent_ipmicmds[(node, tenant)] = IpmiCommandWrapper( node, cfg, bmc=connparams['bmc'], userid=connparams['username'], password=connparams['passphrase'], kg=connparams['kg'], port=connparams['port'], onlogon=self.logged) ipmisess = persistent_ipmicmds[(node, tenant)].ipmi_session begin = util.monotonic_time() while ((not (self.broken or self.loggedin)) and (util.monotonic_time() - begin) < 180): ipmisess.wait_for_rsp(180) if not (self.broken or self.loggedin): raise exc.TargetEndpointUnreachable("Login process to " + connparams['bmc'] + " died") except socket.gaierror as ge: if ge[0] == -2: raise exc.TargetEndpointUnreachable(ge[1]) raise self.ipmicmd = persistent_ipmicmds[(node, tenant)]
def __init__(self, operation, node, element, cfd, inputdata, cfg, output): self.sensormap = {} self.invmap = {} self.output = output self.sensorcategory = None self.broken = False self.error = None eventlet.sleep(0) self.cfg = cfd[node] self.loggedin = False self.node = node self.element = element self.op = operation connparams = get_conn_params(node, self.cfg) self.ipmicmd = None self.inputdata = inputdata tenant = cfg.tenant self._logevt = None if ((node, tenant) not in persistent_ipmicmds or not persistent_ipmicmds[(node, tenant)].ipmi_session.logged): self._logevt = threading.Event() try: persistent_ipmicmds[(node, tenant)].close_confluent() except KeyError: # was no previous session pass try: persistent_ipmicmds[(node, tenant)] = IpmiCommandWrapper( node, cfg, bmc=connparams['bmc'], userid=connparams['username'], password=connparams['passphrase'], kg=connparams['kg'], port=connparams['port'], onlogon=self.logged) except socket.gaierror as ge: if ge[0] == -2: raise exc.TargetEndpointUnreachable(ge[1]) self.ipmicmd = persistent_ipmicmds[(node, tenant)]
def walk(self, oid): """Walk over children of a given OID This is roughly equivalent to snmpwalk. It will automatically try to be a snmpbulkwalk if possible. :param oid: The SNMP object identifier """ # SNMP is a complicated mess of things. Will endeavor to shield caller # from as much as possible, assuming reasonable defaults when possible. # there may come a time where we add more parameters to override the # automatic behavior (e.g. DES is weak, so it's likely to be # overriden, but some devices only support DES) tp = _get_transport(self.server) ctx = snmp.ContextData(self.context) resolvemib = False if '::' in oid: resolvemib = True mib, field = oid.split('::') obj = snmp.ObjectType(snmp.ObjectIdentity(mib, field)) else: obj = snmp.ObjectType(snmp.ObjectIdentity(oid)) walking = snmp.bulkCmd(self.eng, self.authdata, tp, ctx, 0, 10, obj, lexicographicMode=False, lookupMib=resolvemib) try: for rsp in walking: errstr, errnum, erridx, answers = rsp if errstr: errstr = str(errstr) finerr = errstr + ' while trying to connect to ' \ '{0}'.format(self.server) if errstr in ('Unknown USM user', 'unknownUserName', 'wrongDigest', 'Wrong SNMP PDU digest'): raise exc.TargetEndpointBadCredentials(finerr) # need to do bad credential versus timeout raise exc.TargetEndpointUnreachable(finerr) elif errnum: raise exc.ConfluentException(errnum.prettyPrint() + ' while trying to connect to ' '{0}'.format(self.server)) for ans in answers: if not obj[0].isPrefixOf(ans[0]): # PySNMP returns leftovers in a bulk command # filter out such leftovers break yield ans except snmperr.WrongValueError: raise exc.TargetEndpointBadCredentials('Invalid SNMPv3 password')
def walk(self, oid): """Walk over children of a given OID This is roughly equivalent to snmpwalk. It will automatically try to be a snmpbulkwalk if possible. :param oid: The SNMP object identifier """ # SNMP is a complicated mess of things. Will endeavor to shield caller # from as much as possible, assuming reasonable defaults when possible. # there may come a time where we add more parameters to override the # automatic behavior (e.g. DES is weak, so it's likely to be # overriden, but some devices only support DES) tp = _get_transport(self.server) ctx = snmp.ContextData(self.context) if '::' in oid: mib, field = oid.split('::') obj = snmp.ObjectType(snmp.ObjectIdentity(mib, field)) else: obj = snmp.ObjectType(snmp.ObjectIdentity(oid)) walking = snmp.bulkCmd(self.eng, self.authdata, tp, ctx, 0, 10, obj, lexicographicMode=False) for rsp in walking: errstr, errnum, erridx, answers = rsp if errstr: raise exc.TargetEndpointUnreachable(str(errstr)) elif errnum: raise exc.ConfluentException(errnum.prettyPrint()) for ans in answers: yield ans
def strip_node(self, node): raise exc.TargetEndpointUnreachable(self.error)
def _bmcconfig(self, nodename, reset=False, customconfig=None, vc=None): # 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. creds = self.configmanager.get_node_attributes(nodename, [ 'secret.hardwaremanagementuser', 'secret.hardwaremanagementpassword' ], decrypt=True) user = creds.get(nodename, {}).get('secret.hardwaremanagementuser', {}).get('value', None) passwd = creds.get(nodename, {}).get('secret.hardwaremanagementpassword', {}).get('value', None) try: ic = self._get_ipmicmd() passwd = self.DEFAULT_PASS except pygexc.IpmiException as pi: havecustomcreds = False if user is not None and user != self.DEFAULT_USER: havecustomcreds = True else: user = self.DEFAULT_USER if passwd is not None and passwd != self.DEFAULT_PASS: havecustomcreds = True else: passwd = self.DEFAULT_PASS if havecustomcreds: ic = self._get_ipmicmd(user, passwd) else: raise if vc: ic.register_key_handler(vc) currusers = ic.get_users() lanchan = ic.get_network_channel() userdata = ic.xraw_command(netfn=6, command=0x44, data=(lanchan, 1)) userdata = bytearray(userdata['data']) maxusers = userdata[0] & 0b111111 enabledusers = userdata[1] & 0b111111 lockedusers = userdata[2] & 0b111111 cfg = self.configmanager cd = cfg.get_node_attributes(nodename, [ 'secret.hardwaremanagementuser', 'secret.hardwaremanagementpassword', 'hardwaremanagement.manager' ], True) cd = cd.get(nodename, {}) if ('secret.hardwaremanagementuser' not in cd or 'secret.hardwaremanagementpassword' not in cd): raise exc.TargetEndpointBadCredentials( 'secret.hardwaremanagementuser and/or ' 'secret.hardwaremanagementpassword was not configured') newuser = cd['secret.hardwaremanagementuser']['value'] newpass = cd['secret.hardwaremanagementpassword']['value'] for uid in currusers: if currusers[uid]['name'] == newuser: # Use existing account that has been created newuserslot = uid if newpass != passwd: # don't mess with existing if no change ic.set_user_password(newuserslot, password=newpass) ic = self._get_ipmicmd(user, passwd) if vc: ic.register_key_handler(vc) break else: newuserslot = lockedusers + 1 if newuserslot < 2: newuserslot = 2 if newpass != passwd: # don't mess with existing if no change ic.set_user_password(newuserslot, password=newpass) ic.set_user_name(newuserslot, newuser) if havecustomcreds: ic = self._get_ipmicmd(user, passwd) if vc: ic.register_key_handler(vc) #We are remote operating on the account we are #using, no need to try to set user access #ic.set_user_access(newuserslot, lanchan, # privilege_level='administrator') # Now to zap others for uid in currusers: if uid != newuserslot: if uid <= lockedusers: # we cannot delete, settle for disable ic.disable_user(uid, 'disable') else: # lead with the most critical thing, removing user access ic.set_user_access(uid, channel=None, callback=False, link_auth=False, ipmi_msg=False, privilege_level='no_access') # next, try to disable the password ic.set_user_password(uid, mode='disable', password=None) # ok, now we can be less paranoid try: ic.user_delete(uid) except pygexc.IpmiException as ie: if ie.ipmicode != 0xd5: # some response to the 0xff # name... # the user will remain, but that is life raise if customconfig: customconfig(ic) 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] # This getaddrinfo is repeated in get_nic_config, could be # optimized, albeit with a more convoluted api.. newip = newipinfo[-1][0] if ':' in newip: raise exc.NotImplementedException('IPv6 remote config TODO') netconfig = netutil.get_nic_config(cfg, nodename, ip=newip) plen = netconfig['prefix'] newip = '{0}/{1}'.format(newip, plen) currcfg = ic.get_net_configuration() if currcfg['ipv4_address'] != newip: # do not change the ipv4_config if the current config looks # like it is already accurate ic.set_net_configuration( ipv4_address=newip, ipv4_configuration='static', ipv4_gateway=netconfig['ipv4_gateway']) elif self.ipaddr.startswith('fe80::'): cfg.set_node_attributes( {nodename: { 'hardwaremanagement.manager': self.ipaddr }}) else: raise exc.TargetEndpointUnreachable( 'hardwaremanagement.manager must be set to desired address') if reset: ic.reset_bmc() return ic
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 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, wc) if self._atdefaultcreds: if isdefault and self.tmppasswd: raise Exception( 'Request to use default credentials, but refused by target after it has been changed to {0}'.format(self.tmppasswd)) 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}})