From f88e0bca4cba293ba17c1163e3ee338cf56f6e7e Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Mon, 19 Mar 2018 08:45:13 -0400 Subject: [PATCH 01/13] Fix nodeshell hang on incomplete lines readline would hang because the filehandle was really not ready. --- confluent_client/bin/nodeshell | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/confluent_client/bin/nodeshell b/confluent_client/bin/nodeshell index 6db57f6a..d090e0ba 100755 --- a/confluent_client/bin/nodeshell +++ b/confluent_client/bin/nodeshell @@ -86,7 +86,7 @@ def run(): desc = pipedesc[r] node = desc['node'] data = True - while data and select.select([r], [], [], 0): + while data and select.select([r], [], [], 0)[0]: data = r.readline() if data: if desc['type'] == 'stdout': From 6671b9aad3c7fe4c0fde8891e0b63d11275696f2 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Fri, 23 Mar 2018 08:27:27 -0400 Subject: [PATCH 02/13] Provide cleaner behavior on timeouts If a timeout occurred outside of a keeplaive, provide a more consistent message about the situation. --- confluent_server/confluent/discovery/protocols/ssdp.py | 2 -- confluent_server/confluent/plugins/hardwaremanagement/ipmi.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/confluent_server/confluent/discovery/protocols/ssdp.py b/confluent_server/confluent/discovery/protocols/ssdp.py index de543697..2b03f28a 100644 --- a/confluent_server/confluent/discovery/protocols/ssdp.py +++ b/confluent_server/confluent/discovery/protocols/ssdp.py @@ -191,7 +191,6 @@ def _parse_ssdp(peer, rsp, peerdata): _, code, _ = headlines[0].split(' ', 2) except ValueError: return - myurl = None if code == '200': if nid in peerdata: peerdatum = peerdata[nid] @@ -208,7 +207,6 @@ def _parse_ssdp(peer, rsp, peerdata): header = header.strip() value = value.strip() if header == 'AL' or header == 'LOCATION': - myurl = value if 'urls' not in peerdatum: peerdatum['urls'] = [value] elif value not in peerdatum['urls']: diff --git a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py index eff94678..e3daa375 100644 --- a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py +++ b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py @@ -344,7 +344,7 @@ def perform_request(operator, node, element, cfg, results).handle_request() except pygexc.IpmiException as ipmiexc: excmsg = str(ipmiexc) - if excmsg == 'Session no longer connected': + if excmsg in ('Session no longer connected', 'timeout'): results.put(msg.ConfluentTargetTimeout(node)) else: results.put(msg.ConfluentNodeError(node, excmsg)) From f97c39cea4845a59c3ff7ccc007eeb7f1ef80f1f Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Tue, 27 Mar 2018 15:51:14 -0400 Subject: [PATCH 03/13] Add hostname to api The hostname of the BMC is added to the api. --- confluent_server/confluent/core.py | 4 +++ confluent_server/confluent/messages.py | 35 ++++++++++++++++++- .../plugins/hardwaremanagement/ipmi.py | 12 +++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/confluent_server/confluent/core.py b/confluent_server/confluent/core.py index 88db962d..2cd5e11f 100644 --- a/confluent_server/confluent/core.py +++ b/confluent_server/confluent/core.py @@ -154,6 +154,10 @@ def _init_core(): 'pluginattrs': ['hardwaremanagement.method'], 'default': 'ipmi', }), + 'hostname': PluginRoute({ + 'pluginattrs': ['hardwaremanagement.method'], + 'default': 'ipmi', + }), 'identifier': PluginRoute({ 'pluginattrs': ['hardwaremanagement.method'], 'default': 'ipmi', diff --git a/confluent_server/confluent/messages.py b/confluent_server/confluent/messages.py index 13331f11..e9694b17 100644 --- a/confluent_server/confluent/messages.py +++ b/confluent_server/confluent/messages.py @@ -394,9 +394,12 @@ def get_input_message(path, operation, inputdata, nodes=None, multinode=False, elif (path[:3] == ['configuration', 'management_controller', 'reset'] and operation != 'retrieve'): return InputBMCReset(path, nodes, inputdata) - elif (path[:3] == ['configuration', 'management_controller', 'identifier'] + elif (path[:2] == ['configuration', 'management_controller', 'identifier'] and operation != 'retrieve'): return InputMCI(path, nodes, inputdata) + elif (path[:2] == ['configuration', 'management_controller', 'hostname'] + and operation != 'retrieve'): + return InputHostname(path, nodes, inputdata, configmanager) elif (path[:4] == ['configuration', 'management_controller', 'net_interfaces', 'management'] and operation != 'retrieve'): return InputNetworkConfiguration(path, nodes, inputdata, @@ -719,6 +722,25 @@ class InputBMCReset(ConfluentInputMessage): return self.inputbynode[node] + +class InputHostname(ConfluentInputMessage): + def __init__(self, path, nodes, inputdata, configmanager): + self.inputbynode = {} + self.stripped = False + if not inputdata or 'hostname' not in inputdata: + raise exc.InvalidArgumentException('missing hostname attribute') + if nodes is None: + raise exc.InvalidArgumentException( + 'This only supports per-node input') + for expanded in configmanager.expand_attrib_expression( + nodes, inputdata['hostname']): + node, value = expanded + self.inputbynode[node] = value + + def hostname(self, node): + return self.inputbynode[node] + + class InputMCI(ConfluentInputMessage): def __init__(self, path, nodes, inputdata): self.inputbynode = {} @@ -1298,6 +1320,17 @@ class MCI(ConfluentMessage): self.kvpairs = {name: kv} +class Hostname(ConfluentMessage): + def __init__(self, name=None, hostname=None): + self.notnode = name is None + self.desc = 'BMC hostname' + + kv = {'hostname': {'value': hostname}} + if self.notnode: + self.kvpairs = kv + else: + self.kvpairs = {name: kv} + class DomainName(ConfluentMessage): def __init__(self, name=None, dn=None): self.notnode = name is None diff --git a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py index eff94678..b42c01d9 100644 --- a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py +++ b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py @@ -518,6 +518,8 @@ class IpmiHandler(object): return self.handle_reset() elif self.element[1:3] == ['management_controller', 'identifier']: return self.handle_identifier() + elif self.element[1:3] == ['management_controller', 'hostname']: + return self.handle_hostname() elif self.element[1:3] == ['management_controller', 'domain_name']: return self.handle_domain_name() elif self.element[1:3] == ['management_controller', 'ntp']: @@ -986,6 +988,16 @@ class IpmiHandler(object): self.ipmicmd.set_mci(mci) return + def handle_hostname(self): + if 'read' == self.op: + hostname = self.ipmicmd.get_hostname() + self.output.put(msg.Hostname(self.node, hostname)) + return + elif 'update' == self.op: + hostname = self.inputdata.hostname(self.node) + self.ipmicmd.set_hostname(hostname) + return + def handle_domain_name(self): if 'read' == self.op: dn = self.ipmicmd.get_domain_name() From 3dd86c71fd0a283eae3767c219be6c2b4c943c13 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Tue, 27 Mar 2018 16:32:37 -0400 Subject: [PATCH 04/13] Add bmc.hostname to nodeconfig --- confluent_client/bin/nodeconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/confluent_client/bin/nodeconfig b/confluent_client/bin/nodeconfig index 6055e315..78f46680 100755 --- a/confluent_client/bin/nodeconfig +++ b/confluent_client/bin/nodeconfig @@ -67,6 +67,8 @@ cfgpaths = { 'bmc.ipv4_gateway': ( 'configuration/management_controller/net_interfaces/management', 'ipv4_gateway'), + 'bmc.hostname': ( + 'configuration/management_controller/hostname', 'hostname'), } autodeps = { From bd40f2f4a6f452a0bee7f5f916cf7d414384c411 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Tue, 27 Mar 2018 17:11:35 -0400 Subject: [PATCH 05/13] Fix mistake in indexing of url --- confluent_server/confluent/messages.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/confluent_server/confluent/messages.py b/confluent_server/confluent/messages.py index e9694b17..fb40ad79 100644 --- a/confluent_server/confluent/messages.py +++ b/confluent_server/confluent/messages.py @@ -394,10 +394,10 @@ def get_input_message(path, operation, inputdata, nodes=None, multinode=False, elif (path[:3] == ['configuration', 'management_controller', 'reset'] and operation != 'retrieve'): return InputBMCReset(path, nodes, inputdata) - elif (path[:2] == ['configuration', 'management_controller', 'identifier'] + elif (path[:3] == ['configuration', 'management_controller', 'identifier'] and operation != 'retrieve'): return InputMCI(path, nodes, inputdata) - elif (path[:2] == ['configuration', 'management_controller', 'hostname'] + elif (path[:3] == ['configuration', 'management_controller', 'hostname'] and operation != 'retrieve'): return InputHostname(path, nodes, inputdata, configmanager) elif (path[:4] == ['configuration', 'management_controller', From 5ca52ff03ba38784e010b98a2ff47407c4cec3b1 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Mon, 9 Apr 2018 10:48:06 -0400 Subject: [PATCH 06/13] Handle interruptions to select such as resize Resize can cause an interrupted operation on stdin, handle that. --- confluent_client/bin/confetty | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/confluent_client/bin/confetty b/confluent_client/bin/confetty index 02690e68..86ef1aea 100755 --- a/confluent_client/bin/confetty +++ b/confluent_client/bin/confetty @@ -763,7 +763,10 @@ def conserver_command(filehandle, localcommand): def get_command_bytes(filehandle, localcommand, cmdlen): while len(localcommand) < cmdlen: - ready, _, _ = select.select((filehandle,), (), (), 1) + try: + ready, _, _ = select.select((filehandle,), (), (), 1) + except select.error: + ready = () if ready: localcommand += filehandle.read() return localcommand @@ -776,7 +779,10 @@ def check_escape_seq(currinput, filehandle): sys.stdout.flush() return conserver_command( filehandle, currinput[len(conserversequence):]) - ready, _, _ = select.select((filehandle,), (), (), 3) + try: + ready, _, _ = select.select((filehandle,), (), (), 3) + except select.error: + ready = () if not ready: # 3 seconds of no typing break currinput += filehandle.read() @@ -866,8 +872,11 @@ def check_power_state(): while inconsole or not doexit: if inconsole: - rdylist, _, _ = select.select( - (sys.stdin, session.connection), (), (), 10) + try: + rdylist, _, _ = select.select( + (sys.stdin, session.connection), (), (), 10) + except select.error: + rdylist = () for fh in rdylist: if fh == session.connection: # this only should get called in the From 76a4a913519476d775d7796fde6f7cc746922fc0 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Tue, 10 Apr 2018 15:11:20 -0400 Subject: [PATCH 07/13] Fix pyparsing rpm name Accept another likely formulation of an rpm name for the package. --- confluent_server/confluent_server.spec.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/confluent_server/confluent_server.spec.tmpl b/confluent_server/confluent_server.spec.tmpl index 55e307cc..e93922d7 100644 --- a/confluent_server/confluent_server.spec.tmpl +++ b/confluent_server/confluent_server.spec.tmpl @@ -12,7 +12,7 @@ Group: Development/Libraries BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot Prefix: %{_prefix} BuildArch: noarch -Requires: python-pyghmi >= 1.0.34, python-eventlet, python-greenlet, python-crypto >= 2.6.1, confluent_client, pyparsing, python-paramiko, python-dns, python-netifaces, python2-pyasn1 >= 0.2.3, python-pysnmp >= 4.3.4, python-pyte, python-lxml, python-eficompressor +Requires: python-pyghmi >= 1.0.34, python-eventlet, python-greenlet, python-crypto >= 2.6.1, confluent_client, python-pyparsing, python-paramiko, python-dns, python-netifaces, python2-pyasn1 >= 0.2.3, python-pysnmp >= 4.3.4, python-pyte, python-lxml, python-eficompressor Vendor: Jarrod Johnson Url: http://xcat.sf.net/ From 91238f1dcbdfdde10bdd899777028f6a1a06847a Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Tue, 10 Apr 2018 16:06:37 -0400 Subject: [PATCH 08/13] Clean up pure python packaging Fix __init__.py redundancy, update requirements to current state of affairs. --- confluent_client/confluent/__init__.py | 1 + confluent_server/confluent/__init__.py | 1 - confluent_server/makesetup | 1 - confluent_server/setup.py.tmpl | 3 ++- 4 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 confluent_server/confluent/__init__.py diff --git a/confluent_client/confluent/__init__.py b/confluent_client/confluent/__init__.py index e69de29b..b9851570 100644 --- a/confluent_client/confluent/__init__.py +++ b/confluent_client/confluent/__init__.py @@ -0,0 +1 @@ +__version__ = "1.8.1.dev16.gg7f29d3a" diff --git a/confluent_server/confluent/__init__.py b/confluent_server/confluent/__init__.py deleted file mode 100644 index 07e5b37e..00000000 --- a/confluent_server/confluent/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = "1.5.0.dev395.ggacb3a20" diff --git a/confluent_server/makesetup b/confluent_server/makesetup index 26bb712a..2ee30143 100755 --- a/confluent_server/makesetup +++ b/confluent_server/makesetup @@ -6,4 +6,3 @@ if [ "$NUMCOMMITS" != "$VERSION" ]; then fi echo $VERSION > VERSION sed -e "s/#VERSION#/$VERSION/" setup.py.tmpl > setup.py -echo '__version__ = "'$VERSION'"' > confluent/__init__.py diff --git a/confluent_server/setup.py.tmpl b/confluent_server/setup.py.tmpl index 85ecb57e..94b010f0 100644 --- a/confluent_server/setup.py.tmpl +++ b/confluent_server/setup.py.tmpl @@ -17,7 +17,8 @@ setup( 'confluent/plugins/shell/', 'confluent/plugins/configuration/'], install_requires=['paramiko', 'pycrypto>=2.6', 'confluent_client>=0.1.0', 'eventlet', - 'pyghmi>=0.6.5'], + 'dnspython', 'netifaces', 'pyte', 'pysnmp', 'pyparsing', + 'pyghmi>=1.0.44'], scripts=['bin/confluent', 'bin/confluentdbutil'], data_files=[('/etc/init.d', ['sysvinit/confluent']), ('/usr/lib/sysctl.d', ['sysctl/confluent.conf']), From fc7b26eaf7fd550ca86648c2e5d85edc0e3f93d4 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Tue, 10 Apr 2018 16:09:26 -0400 Subject: [PATCH 09/13] Remove __init__.py from tracking in client --- confluent_client/confluent/__init__.py | 1 - 1 file changed, 1 deletion(-) delete mode 100644 confluent_client/confluent/__init__.py diff --git a/confluent_client/confluent/__init__.py b/confluent_client/confluent/__init__.py deleted file mode 100644 index b9851570..00000000 --- a/confluent_client/confluent/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = "1.8.1.dev16.gg7f29d3a" From 39ee0da879918025673ecbbff0af414789c7051c Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Tue, 10 Apr 2018 16:11:14 -0400 Subject: [PATCH 10/13] Fix makesetup for confluent_client Fixing the redundant __init__.py led to no __init__.py, fix that mistake. --- confluent_server/makesetup | 3 +++ 1 file changed, 3 insertions(+) diff --git a/confluent_server/makesetup b/confluent_server/makesetup index 2ee30143..44e7293f 100755 --- a/confluent_server/makesetup +++ b/confluent_server/makesetup @@ -6,3 +6,6 @@ if [ "$NUMCOMMITS" != "$VERSION" ]; then fi echo $VERSION > VERSION sed -e "s/#VERSION#/$VERSION/" setup.py.tmpl > setup.py +if [ -f confluent/client.py ]; then + echo '__version__ = "'$VERSION'"' > confluent/__init__.py +fi From 008f8e22ae33b9b128e79243c000b733ee88cd44 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Thu, 12 Apr 2018 15:45:07 -0400 Subject: [PATCH 11/13] Abort traversing gap in SMM chain Once there is a gap, the next hop in the chain will be ambiguous. Discovery must always precede from the front-most chassis. --- confluent_server/confluent/discovery/core.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/confluent_server/confluent/discovery/core.py b/confluent_server/confluent/discovery/core.py index 0b7523a2..eb5aea26 100644 --- a/confluent_server/confluent/discovery/core.py +++ b/confluent_server/confluent/discovery/core.py @@ -695,6 +695,9 @@ def get_chained_smm_name(nodename, cfg, handler, nl=None, checkswitch=True): smmaddr = cd[nodename]['hardwaremanagement.manager']['value'] pkey = cd[nodename].get('pubkeys.tls_hardwaremanager', {}).get( 'value', None) + if not pkey: + # We cannot continue through a break in the chain + return None, False if pkey: cv = util.TLSCertVerifier( cfg, nodename, 'pubkeys.tls_hardwaremanager').verify_cert From af72d0e71abc2384f6368dacb18aad993e6f3fee Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Thu, 12 Apr 2018 17:05:06 -0400 Subject: [PATCH 12/13] Update the discovery lookup tables on node add/remove This will mitigate stale mappings in the discovery process. --- confluent_server/confluent/discovery/core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/confluent_server/confluent/discovery/core.py b/confluent_server/confluent/discovery/core.py index eb5aea26..9dfc76db 100644 --- a/confluent_server/confluent/discovery/core.py +++ b/confluent_server/confluent/discovery/core.py @@ -1078,6 +1078,7 @@ def newnodes(added, deleting, configmanager): global attribwatcher global needaddhandled global nodeaddhandler + _map_unique_ids() configmanager.remove_watcher(attribwatcher) allnodes = configmanager.list_nodes() attribwatcher = configmanager.watch_attributes( From a1156097d207061b6fa9d7fc6b9114d2e17302d6 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Fri, 13 Apr 2018 16:54:27 -0400 Subject: [PATCH 13/13] Add facility to disable autosense discovery autosense at scale may produce undesirable performance. Provide an interface to turn off the autosense. If autosense is off, manual scan can still be performed. --- .../confluent/config/configmanager.py | 18 +++++++- confluent_server/confluent/discovery/core.py | 41 +++++++++++++++++-- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/confluent_server/confluent/config/configmanager.py b/confluent_server/confluent/config/configmanager.py index 7bad6802..9d2087af 100644 --- a/confluent_server/confluent/config/configmanager.py +++ b/confluent_server/confluent/config/configmanager.py @@ -1684,19 +1684,33 @@ def restore_db_from_directory(location, password): if e.errno == 2: raise Exception("Cannot restore without keys, this may be a " "redacted dump") + try: + moreglobals = json.load(open(os.path.join(location, 'globals.json'))) + for globvar in moreglobals: + set_global(globvar, moreglobals[globvar]) + except IOError as e: + if e.errno != 2: + raise with open(os.path.join(location, 'main.json'), 'r') as cfgfile: cfgdata = cfgfile.read() ConfigManager(tenant=None)._load_from_json(cfgdata) -def dump_db_to_directory(location, password, redact=None): - if not redact: +def dump_db_to_directory(location, password, redact=None, skipkeys=False): + if not redact and not skipkeys: with open(os.path.join(location, 'keys.json'), 'w') as cfgfile: cfgfile.write(_dump_keys(password)) cfgfile.write('\n') with open(os.path.join(location, 'main.json'), 'w') as cfgfile: cfgfile.write(ConfigManager(tenant=None)._dump_to_json(redact=redact)) cfgfile.write('\n') + bkupglobals = {} + for globvar in _cfgstore['globals']: + if globvar.endswith('_key'): + continue + bkupglobals[globvar] = _cfgstore['globals'][globvar] + if bkupglobals: + json.dump(bkupglobals, open(os.path.join(location, 'globals.json'))) try: for tenant in os.listdir( os.path.join(ConfigManager._cfgdir, '/tenants/')): diff --git a/confluent_server/confluent/discovery/core.py b/confluent_server/confluent/discovery/core.py index 9dfc76db..1cc31a9c 100644 --- a/confluent_server/confluent/discovery/core.py +++ b/confluent_server/confluent/discovery/core.py @@ -85,6 +85,7 @@ import eventlet import eventlet.greenpool import eventlet.semaphore +autosensors = set() class nesteddict(dict): def __missing__(self, key): @@ -350,7 +351,28 @@ def _parameterize_path(pathcomponents): return validselectors, keyparams, listrequested, childcoll +def handle_autosense_config(operation, inputdata): + autosense = cfm.get_global('discovery.autosense') + autosense = autosense or autosense is None + if operation == 'retrieve': + yield msg.KeyValueData({'enabled': autosense}) + elif operation == 'update': + enabled = inputdata['enabled'] + if type(enabled) in (unicode, str): + enabled = enabled.lower() in ('true', '1', 'y', 'yes', 'enable', + 'enabled') + if autosense == enabled: + return + cfm.set_global('discovery.autosense', enabled) + if enabled: + start_autosense() + else: + stop_autosense() + + def handle_api_request(configmanager, inputdata, operation, pathcomponents): + if pathcomponents == ['discovery', 'autosense']: + return handle_autosense_config(operation, inputdata) if operation == 'retrieve': return handle_read_api_request(pathcomponents) elif (operation in ('update', 'create') and @@ -398,6 +420,7 @@ def handle_read_api_request(pathcomponents): if len(pathcomponents) == 1: dirlist = [msg.ChildCollection(x + '/') for x in sorted(list(subcats))] dirlist.append(msg.ChildCollection('rescan')) + dirlist.append(msg.ChildCollection('autosense')) return dirlist if not coll: return show_info(queryparms['by-mac']) @@ -1083,8 +1106,9 @@ def newnodes(added, deleting, configmanager): allnodes = configmanager.list_nodes() attribwatcher = configmanager.watch_attributes( allnodes, ('discovery.policy', 'net*.switch', - 'hardwaremanagement.manager', 'net*.switchport', 'id.uuid', - 'pubkeys.tls_hardwaremanager', 'net*.bootable'), _recheck_nodes) + 'hardwaremanagement.manager', 'net*.switchport', + 'id.uuid', 'pubkeys.tls_hardwaremanager', + 'net*.bootable'), _recheck_nodes) if nodeaddhandler: needaddhandled = True else: @@ -1130,14 +1154,23 @@ def start_detection(): 'hardwaremanagement.manager', 'net*.switchport', 'id.uuid', 'pubkeys.tls_hardwaremanager'), _recheck_nodes) cfg.watch_nodecollection(newnodes) - eventlet.spawn_n(slp.snoop, safe_detected) - eventlet.spawn_n(pxe.snoop, safe_detected) + autosense = cfm.get_global('discovery.autosense') + if autosense or autosense is None: + start_autosense() if rechecker is None: rechecktime = util.monotonic_time() + 900 rechecker = eventlet.spawn_after(900, _periodic_recheck, cfg) # eventlet.spawn_n(ssdp.snoop, safe_detected) +def stop_autosense(): + for watcher in list(autosensors): + watcher.kill() + autosensors.discard(watcher) + +def start_autosense(): + autosensors.add(eventlet.spawn(slp.snoop, safe_detected)) + autosensors.add(eventlet.spawn(pxe.snoop, safe_detected)) nodes_by_fprint = {}