2
0
mirror of https://github.com/xcat2/confluent.git synced 2026-09-02 07:26:04 +00:00

Fix undefined names (F821)

Every one of these raises NameError if its code path is reached:

- nodeapply: run_automation accumulated into an exitcode that only existed
  in run(), so any automation error crashed instead of being reported.  It
  now keeps and returns its own, tracked separately from the exit code of
  the ssh commands: the early exit after the spawn loop tests that one,
  and folding automation failures into it would exit with children already
  running and their pipes abandoned.  Both are reported at the real exits.
- nodeconsole: redraw() reads firstnodename, which was local to
  do_screenshot(); promote it to a module global like the other drawing
  state.
- nodedeploy: the redeploy path appended to a lockednodes list that did not
  exist yet.  The block that follows re-reads the same lock state and acts
  on it, so drop the dead duplicate.
- samples/nodeattrib_from_switch.py, misc/filterpasswd: missing import sys.
- xcc3: fixuuid was never imported.  xcc imports xcc3, so take a local copy
  the way the smm handler does instead of creating an import cycle.
- httpapi: the async session call still passed the WSGI-era env and an
  extra argument to handle_async(), which has taken only querydict since
  the aiohttp port.  Calling it correctly exposed that handle_async()
  registers an AsyncSession before raising on the discontinued long poll
  path, so every request to it would leak a session that is never reaped.
  It now only creates one when there is a websocket handler to yield it to.
- messages: the InputFirmwareUpdate.filename property checked
  self.filebynode[node] with no node in scope.  __init__ already validates
  every expanded path and nodefile() rechecks per node, so drop the checks.
- pam: drop the python2 branches referencing unicode and raw_input.  The
  server has been python3 only since the asyncio port.
- cooltera: the sensor-name listing referenced a nonexistent sensors dict.
  The available sensors depend on the model, which is only known after
  reading the device, so list them from the same status data the readings
  use.
- deltapdu, eatonpdu, geist: the not-implemented response in update() used
  node outside the loop, unlike retrieve() in the same files and unlike
  raritan/enlogic.
- confluentdbgcli: stray self. on a module-level socket connect.
This commit is contained in:
Markus Hilger
2026-08-09 07:05:49 +02:00
parent 76aef703ff
commit 8a3fce85c0
15 changed files with 57 additions and 48 deletions
+10 -5
View File
@@ -37,6 +37,7 @@ import confluent.sortutil as sortutil
devnull = None
def run_automation(noderange, category, c):
exitcode = 0
automationbynode = {}
for res in c.update('/noderange/{0}/deployment/remote_config/run'.format(noderange), {
'category': category,
@@ -64,7 +65,8 @@ def run_automation(noderange, category, c):
if res.get('complete', False):
del automationbynode[node]
sys.stdout.write('{0}: Automation complete\n'.format(node))
return exitcode
def run():
global devnull
@@ -104,10 +106,13 @@ def run():
pipedesc = {}
pendingexecs = deque()
exitcode = 0
# Kept apart from exitcode: a failed automation run must not trip the
# early exit below, which would abandon ssh children already spawned.
autoexitcode = 0
c.stop_if_noderange_over(args[0], options.maxnodes)
if options.automation:
run_automation(args[0], options.automation, c)
autoexitcode = run_automation(args[0], options.automation, c)
nodemap = {}
cmdparms = []
@@ -124,7 +129,7 @@ def run():
cmdstorun.append(['run_remote', script])
if not cmdstorun:
if options.automation:
sys.exit(0)
sys.exit(autoexitcode)
argparser.print_help()
sys.exit(1)
for res in c.read('/noderange/{0}/nodes/'.format(args[0])):
@@ -145,7 +150,7 @@ def run():
else:
pendingexecs.append((sshnode, cmdv))
if not all or exitcode:
sys.exit(exitcode)
sys.exit(exitcode | autoexitcode)
rdy = poller.poll(10)
while all:
pernodeout = {}
@@ -193,7 +198,7 @@ def run():
sys.stdout.flush()
if all:
rdy = poller.poll(10)
sys.exit(exitcode)
sys.exit(exitcode | autoexitcode)
def run_cmdv(node, cmdv, all, poller, pipedesc):
+2
View File
@@ -790,6 +790,7 @@ numrows = 0
cwidth = 0
cheight = 0
imagedatabynode = {}
firstnodename = None
def redraw():
for node in imagedatabynode:
@@ -818,6 +819,7 @@ async def do_screenshot():
global streaming
global resized
global numrows
global firstnodename
sess = client.Command()
if streaming:
asyncio.create_task(watch_input())
-5
View File
@@ -133,11 +133,6 @@ def main(args):
curr = nodeinfo[attr].get('value', '')
if curr and node not in profilebynode:
profilebynode[node] = curr
for lockinfo in c.read('/noderange/{0}/deployment/lock'.format(args.noderange)):
for node in lockinfo.get('databynode', {}):
lockstate = lockinfo['databynode'][node]['lock']['value']
if lockstate == 'locked':
lockednodes.append(node)
if args.profile and profilebynode:
sys.stderr.write('The -r/--redeploy option cannot be used with a profile, it redeploys the current or pending profile\n')
return 1
@@ -13,6 +13,7 @@
import confluent.client as cl
import socket
import struct
import sys
c = cl.Command()
macs = []
interface = sys.argv[1]
+5 -5
View File
@@ -119,12 +119,12 @@ def handle_async(querydict, wshandler=None):
# This may be one of two things, a request for a new async stream
# or a request for next data from async stream
# httpapi otherwise handles requests an injecting them to queue
if 'asyncid' not in querydict or not querydict['asyncid']:
if wshandler and ('asyncid' not in querydict or not querydict['asyncid']):
# This is a new request, create a new multiplexer
currsess = AsyncSession(wshandler)
if wshandler:
yield currsess
return
yield AsyncSession(wshandler)
return
# Without a websocket handler there is nobody to hand a session to, so do
# not register one that would never be reaped.
raise Exception("Long polling asynchttp is discontinued")
@@ -12,11 +12,27 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import codecs
import confluent.discovery.handlers.redfishbmc as redfishbmc
import confluent.util as util
import socket
import struct
import aiohmi.util.webclient as webclient
# Duplicated from the xcc handler rather than imported: xcc imports this
# module, so importing it back would be circular. smm carries its own copy of
# this for the same reason.
def fixuuid(baduuid):
# SMM dumps it out in hex
uuidprefix = (baduuid[:8], baduuid[9:13], baduuid[14:18])
a = codecs.encode(struct.pack('<IHH', *[int(x, 16) for x in uuidprefix]),
'hex')
a = util.stringify(a)
uuid = (a[:8], a[8:12], a[12:16], baduuid[19:23], baduuid[24:])
return '-'.join(uuid).lower()
class NodeHandler(redfishbmc.NodeHandler):
devname = 'XCC'
+1 -3
View File
@@ -799,9 +799,7 @@ async def resourcehandler_backend(req, make_response):
pagecontent = ""
try:
async for rsp in _assemble_json(
confluent.asynchttp.handle_async(
env, querydict,
httpsessions[authorized['sessionid']]['inflight'])):
confluent.asynchttp.handle_async(querydict)):
pagecontent += rsp
rsp = await make_response(mimetype, 200, cookies=cookies)
if not isinstance(pagecontent, bytes):
+2 -6
View File
@@ -687,12 +687,8 @@ class InputFirmwareUpdate(ConfluentMessage):
raise Exception('User requested substitutions, but code is '
'written against old api, code must be fixed or '
'skip {} expansion')
if self.filebynode[node].startswith('/etc/confluent'):
raise Exception(
'File transfer with /etc/confluent is not supported')
if self.filebynode[node].startswith('/var/log/confluent'):
raise Exception(
'File transfer with /var/log/confluent is not supported')
# The per-node paths were already checked in __init__, and nodefile()
# rechecks them when handing a path out for a specific node.
return self._filename
def nodefile(self, node):
+3 -15
View File
@@ -178,16 +178,8 @@ class pam():
return 0
# python3 ctypes prefers bytes
if sys.version_info >= (3,):
if isinstance(username, str): username = username.encode(encoding)
if isinstance(service, str): service = service.encode(encoding)
else:
if isinstance(username, unicode):
username = username.encode(encoding)
if isinstance(password, unicode):
password = password.encode(encoding)
if isinstance(service, unicode):
service = service.encode(encoding)
if isinstance(username, str): username = username.encode(encoding)
if isinstance(service, str): service = service.encode(encoding)
if b'\x00' in username or b'\x00' in service:
self.code = 4 # PAM_SYSTEM_ERR in Linux-PAM
@@ -242,11 +234,7 @@ if __name__ == "__main__":
readline.redisplay()
readline.set_pre_input_hook(hook)
if sys.version_info >= (3,):
getinput = input
else:
getinput = raw_input
result = getinput(prompt)
result = input(prompt)
readline.set_pre_input_hook()
return result
@@ -151,12 +151,9 @@ _sensors_by_node = {}
async def read_sensors(element, node, configmanager):
category, name = element[-2:]
if len(element) == 3:
# just get names
# the request is for the names under a category, so that is the last
# element rather than the one before it
category = name
name = 'all'
for sensor in sensors:
yield msg.ChildCollection(simplify_name(sensors[sensor][0]))
return
if category in ('leds, fans'):
return
sn = _sensors_by_node.get(node, None)
@@ -166,6 +163,12 @@ async def read_sensors(element, node, configmanager):
statinfo = xml2stateinfo(statdata)
_sensors_by_node[node] = (statinfo, time.time() + 1)
sn = _sensors_by_node.get(node, None)
if len(element) == 3:
# the names are only known after reading the device, as the sensor
# set depends on the model
for sensor in sn[0] if sn else ():
yield msg.ChildCollection(simplify_name(sensor['name']))
return
if sn:
yield msg.SensorReadings(sn[0], name=node)
@@ -195,7 +195,8 @@ async def retrieve(nodes, element, configmanager, inputdata):
async def update(nodes, element, configmanager, inputdata):
if 'outlets' not in element:
yield msg.ConfluentResourceUnavailable(node, 'Not implemented')
for node in nodes:
yield msg.ConfluentResourceUnavailable(node, 'Not implemented')
return
timeout = 4
for node in nodes:
@@ -327,7 +327,8 @@ async def retrieve(nodes, element, configmanager, inputdata):
async def update(nodes, element, configmanager, inputdata):
if 'outlets' not in element:
yield msg.ConfluentResourceUnavailable(node, 'Not implemented')
for node in nodes:
yield msg.ConfluentResourceUnavailable(node, 'Not implemented')
return
for node in nodes:
gc = PDUClient(node, configmanager)
@@ -336,7 +336,8 @@ async def retrieve(nodes, element, configmanager, inputdata):
async def update(nodes, element, configmanager, inputdata):
if 'outlets' not in element:
yield msg.ConfluentResourceUnavailable(node, 'Not implemented')
for node in nodes:
yield msg.ConfluentResourceUnavailable(node, 'Not implemented')
return
for node in nodes:
gc = GeistClient(node, configmanager)
+1 -1
View File
@@ -20,7 +20,7 @@ import readline
import socket
connection = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.connection.connect('/var/run/confluent/dbg.sock')
connection.connect('/var/run/confluent/dbg.sock')
readline.parse_and_bind("tab: complete")
readline.parse_and_bind("set bell-style none")
+2
View File
@@ -1,3 +1,5 @@
import sys
uidmin = 1000
uidmax = 60000
gidmin = 1000