diff --git a/confluent_server/confluent/config/attributes.py b/confluent_server/confluent/config/attributes.py index 9a12e218..468358b1 100644 --- a/confluent_server/confluent/config/attributes.py +++ b/confluent_server/confluent/config/attributes.py @@ -188,12 +188,24 @@ node = { # 'appliesto': ['vm'], # }, 'hardwaremanagement.manager': { - 'description': 'The management address dedicated to this node', + 'description': 'The management address dedicated to this node. This ' + 'is the address of, for example, the Lenovo IMM.', }, 'hardwaremanagement.method': { 'description': 'The method used to perform operations such as power ' 'control, get sensor data, get inventory, and so on. ' }, + 'hardwaremanagement.switch': { + 'description': 'The switch to which the hardware manager is connected.' + ' Only relevant if using switch based discovery via the' + ' hardware manager (Lenovo IMMs and CMMs). Not ' + 'applicable to Lenovo Flex nodes.' + }, + 'hardwaremanagement.switchport': { + 'description': 'The port of the switch that the hardware manager is ' + 'connected. See documentation of ' + 'hardwaremanagement.switch for more detail.' + }, 'enclosure.manager': { 'description': "The management device for this node's chassis", # 'appliesto': ['system'], @@ -202,6 +214,7 @@ node = { 'description': 'The bay in the enclosure, if any', # 'appliesto': ['system'], }, + # 'enclosure.type': { # 'description': '''The type of enclosure in use (e.g. IBM BladeCenter, #IBM Flex)''', @@ -225,19 +238,19 @@ node = { # 'secret.snmpuser': { # 'description': 'The user to use for SNMPv3 access to this node', # }, -# 'secret.snmppassphrase': { -# 'description': 'The passphrase to use for SNMPv3 access to this node', +# 'secret.snmppassword': { +# 'description': 'The password to use for SNMPv3 access to this node', # }, + 'secret.snmpcommunity': { + 'description': ('SNMPv1 community string, it is highly recommended to' + 'step up to SNMPv3'), + }, # 'secret.snmplocalizedkey': { # 'description': ("SNMPv3 key localized to this node's SNMP Engine id" # 'This can be used in lieu of snmppassphrase to avoid' # 'retaining the passphrase TODO: document procedure' # 'to commit passphrase to localized key'), # }, -# 'secret.snmpcommunity': { -# 'description': ('SNMPv1 community string, it is highly recommended to' -# 'step up to SNMPv3'), -# }, # 'secret.adminpassword': { # 'description': ('The passphrase to apply to local root/administrator ' # 'account. ' @@ -249,35 +262,18 @@ node = { # 'AD') # }, 'secret.ipmikg': { - 'description': 'Optional Integrity key for IPMI communication' + 'description': 'Optional Integrity key for IPMI communication. This ' + 'should generally be ignored, as mutual authentication ' + 'is normally done with the password alone (which is a ' + 'shared secret in IPMI)' }, -# 'secret.ipmiuser': { -# 'description': ('The username to use to log into IPMI device related ' -# 'to the node. For setting username, default ' -# 'behavior is to randomize username, for using ' -# 'username if not set, USERID is assumed'), -# }, -# 'secret.ipmipassphrase': { -# 'description': ('The key to use to authenticate to IPMI device ' -# 'related to the node. For setting passphrase, ' -# 'default behavior is to randomize passphrase and ' -# 'store it here. If going to connect over the ' -# 'network and value is not set, PASSW0RD is attempted') -# }, 'secret.hardwaremanagementuser': { - 'description': ('Username to be set and used by protocols like SSH ' - 'and HTTP where client provides passphrase over the ' - 'network. Given the distinct security models betwen ' - 'this class of protocols and SNMP and IPMI, snmp and ' - 'ipmi utilize dedicated values.'), + 'description': ('The username to use when connecting to the hardware ' + 'manager'), }, 'secret.hardwaremanagementpassword': { - 'description': ('Passphrase to be set and used by protocols like SSH ' - 'and HTTP, where client sends passphrase over the ' - 'network. Given distinct security models between ' - 'this class of protocols, SNMP, and IPMI, SNMP and ' - 'IPMI are given their own settings with distinct ' - 'behaviors'), + 'description': ('Password to use when connecting to the hardware ' + 'manager'), }, 'pubkeys.addpolicy': { 'description': ('Policy to use when encountering unknown public ' diff --git a/confluent_server/confluent/config/configmanager.py b/confluent_server/confluent/config/configmanager.py index 6986043b..c5043ab9 100644 --- a/confluent_server/confluent/config/configmanager.py +++ b/confluent_server/confluent/config/configmanager.py @@ -761,7 +761,9 @@ class ConfigManager(object): decrypt=self.decrypt) return nodeobj - def get_node_attributes(self, nodelist, attributes=()): + def get_node_attributes(self, nodelist, attributes=(), decrypt=None): + if decrypt is None: + decrypt = self.decrypt retdict = {} relattribs = attributes if isinstance(nodelist, str) or isinstance(nodelist, unicode): @@ -783,7 +785,7 @@ class ConfigManager(object): # skipped. The decryption, however, we want to do only on # demand nodeobj[attribute] = _decode_attribute(attribute, cfgnodeobj, - decrypt=self.decrypt) + decrypt=decrypt) retdict[node] = nodeobj return retdict diff --git a/confluent_server/confluent/networking/macmap.py b/confluent_server/confluent/networking/macmap.py index fd601f7b..172d6d6c 100644 --- a/confluent_server/confluent/networking/macmap.py +++ b/confluent_server/confluent/networking/macmap.py @@ -33,11 +33,14 @@ import confluent.exceptions as exc import confluent.snmputil as snmp +import confluent.util as util +import eventlet +from eventlet.greenpool import GreenPool _macmap = {} -def _map_switch(switch, password, user=None): +def _map_switch(args): """Manipulate portions of mac address map relevant to a given switch """ @@ -53,6 +56,8 @@ def _map_switch(switch, password, user=None): # .1.3.6.1.2.1.2.2.1.2 - ifDescr, usually useless, but a # fallback if ifName is empty # + global _macmap + switch, password, user = args haveqbridge = False mactobridge = {} conn = snmp.Session(switch, password, user) @@ -85,10 +90,47 @@ def _map_switch(switch, password, user=None): ifidx, ifname = vb ifidx = int(str(ifidx).rsplit('.', 1)[1]) ifnamemap[ifidx] = str(ifname) - localmap = {} for mac in mactobridge: - localmap[mac] = ifnamemap[bridgetoifmap[mactobridge[mac]]] - print(repr(localmap)) + _macmap[mac] = (switch, ifnamemap[bridgetoifmap[mactobridge[mac]]], + util.monotonic_time()) + + +def update_macmap(configmanager): + """Interrogate switches to build/update mac table + + Begin a rebuild process. This process is a generator that will yield + as each switch interrogation completes, allowing a caller to + recheck the cache as results become possible, rather + than having to wait for the process to complete to interrogate. + """ + if configmanager.tenant is not None: + raise exc.ForbiddenRequest('Network topology not available to tenants') + nodelocations = configmanager.get_node_attributes( + configmanager.list_nodes(), ('hardwaremanagement.switch',)) + switches = set([]) + for node in nodelocations: + cfg = nodelocations[node] + if 'hardwaremanagement.switch' in cfg: + switches.add(cfg['hardwaremanagement.switch']['value']) + switchcfg = configmanager.get_node_attributes( + switches, ('secret.hardwaremanagementuser', + 'secret.hardwaremanagementpassword'), decrypt=True) + switchauth = [] + for switch in switches: + password = 'public' + user = None + if (switch in switchcfg and + 'secret.hardwaremanagementpassword' in switchcfg[switch]): + password = switchcfg[switch]['secret.hardwaremanagementpassword'][ + 'value'] + if 'secret.hardwaremanagementuser' in switchcfg[switch]: + user = switchcfg[switch]['secret.hardwaremanagementuser'][ + 'value'] + switchauth.append((switch, password, user)) + pool = GreenPool() + for res in pool.imap(_map_switch, switchauth): + yield res + print(repr(_macmap)) if __name__ == '__main__':