diff --git a/confluent_server/confluent/config/attributes.py b/confluent_server/confluent/config/attributes.py index 6e8d0bb0..55e449bc 100644 --- a/confluent_server/confluent/config/attributes.py +++ b/confluent_server/confluent/config/attributes.py @@ -156,6 +156,12 @@ node = { 'indicates candidate managers, either for ' 'high availability or load balancing purposes.') }, + 'discovery.passwordrules': { + 'description': 'Any specified rules shall be configured on the BMC ' + 'upon discovery. "expiration=no,loginfailures=no" ' + 'would disable password expiration and login failures ' + 'triggering a lockout.' + }, 'discovery.policy': { 'description': 'Policy to use for auto-configuration of discovered ' 'and identified nodes. Valid values are "manual", ' diff --git a/confluent_server/confluent/config/configmanager.py b/confluent_server/confluent/config/configmanager.py index 756021c6..a1431ad9 100644 --- a/confluent_server/confluent/config/configmanager.py +++ b/confluent_server/confluent/config/configmanager.py @@ -94,7 +94,9 @@ _hasquorum = True _attraliases = { 'bmc': 'hardwaremanagement.manager', 'bmcuser': 'secret.hardwaremanagementuser', + 'switchuser': 'secret.hardwaremanagementuser', 'bmcpass': 'secret.hardwaremanagementpassword', + 'switchpass': 'secret.hardwaremanagementpassword', } def _mkpath(pathname): diff --git a/confluent_server/confluent/discovery/handlers/bmc.py b/confluent_server/confluent/discovery/handlers/bmc.py index 4f26a88a..5f053716 100644 --- a/confluent_server/confluent/discovery/handlers/bmc.py +++ b/confluent_server/confluent/discovery/handlers/bmc.py @@ -45,7 +45,7 @@ class NodeHandler(generic.NodeHandler): def config(self, nodename, reset=False): self._bmcconfig(nodename, reset) - def _bmcconfig(self, nodename, reset=False): + def _bmcconfig(self, nodename, reset=False, customconfig=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. @@ -74,6 +74,8 @@ class NodeHandler(generic.NodeHandler): ic = self._get_ipmicmd(user, passwd) else: raise + if customconfig: + customconfig(ic) currusers = ic.get_users() lanchan = ic.get_network_channel() userdata = ic.xraw_command(netfn=6, command=0x44, data=(lanchan, diff --git a/confluent_server/confluent/discovery/handlers/xcc.py b/confluent_server/confluent/discovery/handlers/xcc.py index ad222489..7cbafd5e 100644 --- a/confluent_server/confluent/discovery/handlers/xcc.py +++ b/confluent_server/confluent/discovery/handlers/xcc.py @@ -13,11 +13,13 @@ # limitations under the License. import confluent.discovery.handlers.imm as immhandler +import confluent.util as util import pyghmi.exceptions as pygexc import pyghmi.ipmi.oem.lenovo.imm as imm + class NodeHandler(immhandler.NodeHandler): devname = 'XCC' @@ -41,16 +43,45 @@ class NodeHandler(immhandler.NodeHandler): #if ipmicmd: # ipmicmd.ipmi_session.logout() + def validate_cert(self, certificate): + # broadly speaking, merely checks consistency moment to moment, + # but if https_cert gets stricter, this check means something + fprint = util.get_fingerprint(self.https_cert) + return util.cert_matches(fprint, certificate) + + def set_password_policy(self, ic): + ruleset = {'USER_GlobalMinPassChgInt': '0'} + for rule in self.ruleset.split(','): + if '=' not in rule: + continue + name, value = rule.split('=') + if value.lower() in ('no', 'none', 'disable', 'disabled'): + value = '0' + if name.lower() in ('expiry', 'expiration'): + ruleset['USER_GlobalPassExpPeriod'] = value + if int(value) < 5: + ruleset['USER_GlobalPassExpWarningPeriod'] = value + if name.lower() in ('lockout', 'loginfailures'): + if value.lower() in ('no', 'none', 'disable', 'disabled'): + value = '0' + ruleset['USER_GlobalMaxLoginFailures'] = value + ic.register_key_handler(self.validate_cert) + ic.oem_init() + ic._oem.immhandler.wc.grab_json_response('/api/dataset', ruleset) + def config(self, nodename, reset=False): # 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. - ic = self._bmcconfig(nodename) + dpp = self.configmanager.get_node_attributes( + nodename, 'discovery.passwordrules') + self.ruleset = dpp.get(nodename, {}).get( + 'discovery.passwordrules', {}).get('value', '') + ic = self._bmcconfig(nodename, customconfig=self.set_password_policy) ff = self.info.get('attributes', {}).get('enclosure-form-factor', '') if ff not in ('dense-computing', [u'dense-computing']): return # Ok, we can get the enclosure uuid now.. - ic.oem_init() enclosureuuid = ic._oem.immhandler.get_property( '/v2/ibmc/smm/chassis/uuid') enclosureuuid = ic._oem.immhandler.get_property( diff --git a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py index 05c77050..1cd70e23 100644 --- a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py +++ b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py @@ -25,6 +25,7 @@ import eventlet.green.threading as threading import eventlet.greenpool as greenpool import eventlet.queue as queue import eventlet.support.greendns +from fnmatch import fnmatch import pyghmi.constants as pygconstants import pyghmi.exceptions as pygexc console = eventlet.import_patched('pyghmi.ipmi.console') @@ -32,6 +33,37 @@ ipmicommand = eventlet.import_patched('pyghmi.ipmi.command') import socket import ssl +pci_cache = {} + +def get_dns_txt(qstring): + return eventlet.support.greendns.resolver.query( + qstring, 'TXT')[0].strings[0].replace('i=', '') + +def get_pci_text_from_ids(subdevice, subvendor, device, vendor): + fqpi = '{0}.{1}.{2}.{3}'.format(subdevice, subvendor, device, vendor) + if fqpi in pci_cache: + return pci_cache[fqpi] + vendorstr = None + try: + vendorstr = get_dns_txt('{0}.pci.id.ucw.cz'.format(subvendor)) + except Exception: + try: + vendorstr = get_dns_txt('{0}.pci.id.ucw.cz'.format(vendor)) + except Exception: + pass + devstr = None + try: + devstr = get_dns_txt(fqpi + '.pci.id.ucw.cz') + except Exception: + try: + devstr = get_dns_txt('{0}.{1}.pci.id.ucw.cz'.format( + device, vendor)) + except Exception: + pass + if vendorstr and devstr: + pci_cache[fqpi] = vendorstr, devstr + return vendorstr, devstr + # There is something not right with the RLocks used in pyghmi when # eventlet comes into play. It seems like sometimes on acquire, @@ -841,7 +873,7 @@ class IpmiHandler(object): sanitize_invdata(invdata[1]) newinf = {'present': True, 'information': invdata[1]} newinf['name'] = invdata[0] - invitems.append(newinf) + self.add_invitem(invitems, newinf) else: self.make_inventory_map() compname = self.invmap.get(component, None) @@ -855,7 +887,7 @@ class IpmiHandler(object): sanitize_invdata(invdata) newinf = {'present': True, 'information': invdata} newinf['name'] = compname - invitems.append(newinf) + self.add_invitem(invitems, newinf) except ssl.SSLEOFError: errorneeded = msg.ConfluentNodeError( self.node, 'Unable to communicate with the https server on ' @@ -872,6 +904,21 @@ class IpmiHandler(object): if errorneeded: self.output.put(errorneeded) + def add_invitem(self, invitems, newinf): + if fnmatch(newinf['name'], 'Adapter ??:??:??') or fnmatch( + newinf['name'], 'PCIeGen? x*'): + myinf = newinf.get('information', {}) + sdid = myinf.get('PCI Subsystem Device ID', None) + svid = myinf.get('PCI Subsystem Vendor ID', None) + did = myinf.get('PCI Device ID', None) + vid = myinf.get('PCI Vendor ID', None) + vstr, dstr = get_pci_text_from_ids(sdid, svid, did, vid) + if vstr: + newinf['information']['PCI Vendor'] = vstr + if dstr: + newinf['name'] = dstr + invitems.append(newinf) + def handle_sensors(self): if self.element[-1] == '': self.element = self.element[:-1]