From 03135543a6dda88160e47136c44153b404b06ce1 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Wed, 5 Sep 2018 13:51:19 -0400 Subject: [PATCH 1/4] Add 'switchuser' and 'switchpass' aliases --- confluent_server/confluent/config/configmanager.py | 2 ++ 1 file changed, 2 insertions(+) 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): From cf3e9037ab9c87f59ab1d224931061b042a4f96e Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Wed, 5 Sep 2018 15:50:36 -0400 Subject: [PATCH 2/4] Provide 'discovery.passwordrules' This provides an ability to designate the desired rules that are applied in the wake of automatic discovery. The most popular would be 'expiry=no,loginfailures=0' --- .../confluent/config/attributes.py | 6 ++++ .../confluent/discovery/handlers/bmc.py | 4 ++- .../confluent/discovery/handlers/xcc.py | 35 +++++++++++++++++-- 3 files changed, 42 insertions(+), 3 deletions(-) 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/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( From 2a8d61ecf631332b283338d9fd7c811e67a07f45 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Thu, 6 Sep 2018 16:10:48 -0400 Subject: [PATCH 3/4] Enrich the less than useful 'Adapter' inventory items We can provide DNS provided info about such generic items to make them look more fleshed out. --- .../plugins/hardwaremanagement/ipmi.py | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py index 05c77050..aad59c5a 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,20 @@ class IpmiHandler(object): if errorneeded: self.output.put(errorneeded) + def add_invitem(self, invitems, newinf): + if fnmatch(newinf['name'], 'Adapter ??:??:??'): + 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] From b3bf6929dfc1f4827dd224762b43508b25bed0fc Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Thu, 6 Sep 2018 16:16:26 -0400 Subject: [PATCH 4/4] Add replacement logic for another generic variant In IMM, PCeGen3 x8 and similar is also possible. --- confluent_server/confluent/plugins/hardwaremanagement/ipmi.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py index aad59c5a..1cd70e23 100644 --- a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py +++ b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py @@ -905,7 +905,8 @@ class IpmiHandler(object): self.output.put(errorneeded) def add_invitem(self, invitems, newinf): - if fnmatch(newinf['name'], 'Adapter ??:??:??'): + 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)