gather propagates the first exception and leaves its siblings running,
so a transport level failure against one node ends the import with a
traceback while the rest of the batch is cancelled at loop shutdown.
The forked children used to contain such a failure to their own node.
assign_macs already reports an error response itself, so this is the
connection dropping rather than the server refusing the assignment.
Collect the exceptions instead, report each one and count it towards
the exit code. Schedule the assignments as tasks while doing so, since
the plain coroutines are left unawaited if defining a later node raises
before the gather is reached.
Replacing the forked children with a gather kept their fan-out: every
row of the import file gets a session of its own and they all start at
once, so a large file opens a local socket and a server side session
task per node simultaneously.
Hold a semaphore for the duration of each node's assignment instead, so
a finished node's session is dropped before the next one starts. Also
build that session once per node rather than once per MAC, and say why
the caller's session is not reused, which was self evident while this
ran in a forked child.
register_endpoint primes current but not total, so a first response
without a count field goes straight to
UnboundLocalError: local variable 'total' referenced before assignment
on the elif. Start at zero, which skips the progress line until the
server does report a count.
nodediscover's rescan poll and nodeconsole's screenshot refresh both slept
with time.sleep inside a coroutine. In nodeconsole --video that stops the
input handler and the VNC streaming tasks for the whole interval.
import_csv was left with several synchronous idioms:
- search_record is a coroutine function, but was called without await.
The returned coroutine is always truthy, so the rescan on incomplete
discovery data never happened, and iterating the result raised
TypeError: 'coroutine' object is not iterable
- the node creation loop iterated an async generator with plain for
- the per-node discovery assignment was forked off with os.fork() while
the event loop was running, and the child then built a fresh session
on the inherited selector
Assign discovery entries with asyncio.gather instead of a forked child,
which keeps the assignments concurrent and lets their exit codes
propagate. The forked child always ended in sys.exit(0), so its
accumulated errorcode was discarded.
register_endpoint and subscribe_discovery were left as plain functions
iterating the async client generators, so nodediscover register,
subscribe and unsubscribe all failed immediately with
TypeError: 'async_generator' object is not iterable
For one, understand 'ip' to potentially mean 'bmc' for list
to assign convenience.
Parallelize handling of csv importing to improve performance.
Only call rescan once per bulk assign
This provides a self-evident entry point from
CLI to extending the discovery to
affluent switches that support it.
This function will work with newer affluent
and Lenovo XCC2 systems.
Sometimes the model name is
useful criteria for evaluating systems,
and the model number isn't
quite that handy.
For XCC, we can provide this data too. Provide it in xcc scan
method and then offer it up to clients.
There are circumstances where the advertised IP is
useful information, even if that IP cannot be validated.
It is also the case that most of the time the advertised IP
is useless if not validated, so it is an optional field.
This causes the manual assignment to sleep less and for the rescan
command to meaningfully slow down shell requests to provide better sense
of when scan completes.
The order argument only had any meaning when used in custom fields.
Add a loop in the custom field agnostic section of the code to correct
the behavior.
This opens the path to more customized output and have the appropriate
formatting. Also revise the Tabulator code to more closely match
the original nodediscover list output.