2
0
mirror of https://github.com/xcat2/confluent.git synced 2026-09-21 16:39:32 +00:00

Merge pull request #266 from Obihoernchen/ruff

Add ruff to CI and fix issues
This commit is contained in:
Jarrod Johnson
2026-08-10 08:17:09 -04:00
committed by GitHub
50 changed files with 251 additions and 145 deletions
+62 -7
View File
@@ -18,14 +18,39 @@ jobs:
# files are sourced fragments or dracut hooks; ShellCheck then
# falls back to checking them as bash.
run: |
shebang_re='^#!.*[/ ](sh|bash|dash|ash|ksh)([[:blank:]]|$)'
{
git ls-files '*.sh'
git ls-files | while IFS= read -r f; do
[ -f "$f" ] || continue
head -c 200 "$f" | head -n 1 | \
grep -qE '^#!.*[/ ](sh|bash|dash|ash|ksh)([ \t]|$)' && echo "$f"
firstline=
# An empty file makes read fail, which under -e would end the run.
IFS= read -r -n 200 firstline < "$f" 2>/dev/null || true
if [[ $firstline =~ $shebang_re ]]; then
printf '%s\n' "$f"
fi
done
} | sort -u | xargs -d '\n' shellcheck --severity=error --exclude=SC2148
} | sort -u > /tmp/shfiles
# A selection that quietly comes up empty would check nothing and
# still pass, so say how many files there are and insist on some.
echo "$(wc -l < /tmp/shfiles) shell files"
[ -s /tmp/shfiles ] || { echo '::error::No shell files found'; exit 1; }
xargs -d '\n' shellcheck --severity=error --exclude=SC2148 < /tmp/shfiles
ruff:
name: Ruff
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
# Pinned to an exact release: unlike actions/checkout, ruff-action
# publishes no moving major tag past v3, so @v4 does not resolve.
- uses: astral-sh/ruff-action@v4.1.0
with:
version: latest
# Rule selection, file discovery (the many extensionless Python
# executables) and exclusions all live in ruff.toml, so the whole
# workspace can be handed over as-is.
args: check --output-format=github
python-compileall:
name: Python compileall
@@ -48,15 +73,45 @@ jobs:
- uses: actions/setup-python@v6
with:
python-version: ${{ env.PYTHON_VERSIONS }}
- name: List the Python files without a .py name
# compileall only ever compiles *.py: handed anything else, even by
# name, it skips it and still exits 0. That leaves every extensionless
# CLI tool, deploy script and setup.py.tmpl unchecked, so collect them
# here and feed them to py_compile, which does compile what it is
# given. The first line is read with the shell builtin rather than
# forking head and grep per file.
run: |
shebang_re='^#!.*python'
{
git ls-files | while IFS= read -r f; do
[ -f "$f" ] || continue
case "$f" in *.py) continue ;; esac
firstline=
# An empty file makes read fail, which under -e would end the run.
IFS= read -r -n 200 firstline < "$f" 2>/dev/null || true
if [[ $firstline =~ $shebang_re ]]; then
printf '%s\n' "$f"
fi
done
# Python that carries no shebang at all, so nothing can detect it.
# Kept in step with extend-include in ruff.toml.
git ls-files '*/setup.py.tmpl' '*/scripts/configbmc' \
'*/scripts/add_local_repositories' 'misc/filterpasswd'
} | sort -u > /tmp/pyfiles
# An empty list would leave py_compile with nothing to do and the
# job green, which is the very hole this step exists to close.
echo "$(wc -l < /tmp/pyfiles) files without a .py name"
[ -s /tmp/pyfiles ] || { echo '::error::No such files found'; exit 1; }
- name: Compile all Python files
run: |
rc=0
for v in $PYTHON_VERSIONS; do
echo "::group::Python $v"
if "python$v" -W error -m compileall -q -x '/\.git/' .; then
echo "::endgroup::"
else
echo "::endgroup::"
ok=0
"python$v" -W error -m compileall -q -x '/\.git/' . || ok=1
xargs -d '\n' "python$v" -W error -m py_compile < /tmp/pyfiles || ok=1
echo "::endgroup::"
if [ "$ok" -ne 0 ]; then
echo "::error::Python $v compileall failed"
rc=1
fi
+1 -2
View File
@@ -41,7 +41,6 @@
# esc-( would interfere with normal esc use too much
# ~ I will not use for now...
import math
import getpass
import optparse
import os
@@ -1043,7 +1042,7 @@ def main():
except IOError:
pass
if powerstate is None or powertime < time.time() - 10: # Check powerstate every 10 seconds
if powerstate == None:
if powerstate is None:
powerstate = True
powertime = time.time()
check_power_state()
-1
View File
@@ -14,7 +14,6 @@ path = os.path.realpath(os.path.join(path, '..', 'lib', 'python'))
if path.startswith('/opt'):
sys.path.append(path)
import confluent.client as client
import confluent.sortutil as sortutil
def partitionhostsline(line):
comment = ''
+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):
+1 -1
View File
@@ -92,7 +92,7 @@ for rsp in session.read('/noderange/{0}/configuration/management_controller/user
continue
for user in rsp['databynode'][node]['users']:
if user['username'] == username:
if not user['uid'] in uid_dict:
if user['uid'] not in uid_dict:
uid_dict[user['uid']] = node
continue
uid_dict[user['uid']] = uid_dict[user['uid']] + ',{}'.format(node)
+2 -2
View File
@@ -170,7 +170,7 @@ def parse_config_line(arguments, single=False):
if '=' in param or param[-1] == ':' or forceset:
if setmode is None:
setmode = True
if setmode != True:
if not setmode:
bailout('Cannot do set and query in same command: Query detected but "{0}" appears to be set'.format(param))
if '=' in param:
key, _, value = param.partition('=')
@@ -182,7 +182,7 @@ def parse_config_line(arguments, single=False):
else:
if setmode is None:
setmode = False
if setmode != False:
if setmode:
bailout('Cannot do set and query in same command: Set mode detected but "{0}" appears to be a query'.format(param))
if '.' not in param:
if param == 'bmc':
+3 -1
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())
@@ -892,7 +894,7 @@ async def do_screenshot():
imgdata = res['databynode'][node].get('image', {}).get('imgdata', None)
if imgdata:
if len(imgdata) < 32: # We were subjected to error
errorstr = f'Unable to get screenshot'
errorstr = 'Unable to get screenshot'
if errorstr or imgdata:
imgdata = base64.b64decode(imgdata)
draw_node(node, imgdata, errorstr, firstnodename, cwidth, cheight)
-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
-1
View File
@@ -27,7 +27,6 @@ if path.startswith('/opt'):
sys.path.append(path)
import confluent.asynclient as client
import confluent.sortutil as sortutil
defcolumns = ['Node', 'Model', 'Serial', 'UUID', 'Mac Address', 'Type',
'Current IP Addresses']
+2 -2
View File
@@ -121,8 +121,8 @@ if len(args) > 1:
os.execlp('nodefirmware', 'nodefirmware', noderange)
else:
url = '/noderange/{0}/inventory/hardware/all/system'
for arg in args:
for arg in arg.split(','):
for rawarg in args:
for arg in rawarg.split(','):
if arg == 'serial':
filters.append(re.compile('serial number'))
elif arg == 'model':
-1
View File
@@ -19,7 +19,6 @@ import optparse
import os
import signal
import sys
import time
try:
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
-1
View File
@@ -35,7 +35,6 @@ if path.startswith('/opt'):
import confluent.client as client
import confluent.screensqueeze as sq
import confluent.sortutil as sortutil
def run():
@@ -13,6 +13,7 @@
import confluent.client as cl
import socket
import struct
import sys
c = cl.Command()
macs = []
interface = sys.argv[1]
@@ -6,14 +6,12 @@ except ImportError:
import base64
import ctypes
import ctypes.util
import glob
import os
import select
import socket
import subprocess
import ssl
import sys
import struct
import time
import re
import hashlib
@@ -625,18 +625,18 @@ if __name__ == '__main__':
time.sleep(1)
continue
dc = json.loads(dc)
iname = get_interface_name(idxmap[curridx], nc.get('default', {}))
if iname:
for iname in iname.split(','):
inames = get_interface_name(idxmap[curridx], nc.get('default', {}))
if inames:
for iname in inames.split(','):
if 'default' in netname_to_interfaces:
netname_to_interfaces['default']['interfaces'].add(iname)
else:
netname_to_interfaces['default'] = {'interfaces': set([iname]), 'settings': nc['default']}
for netname in nc.get('extranets', {}):
uname = '_' + netname
iname = get_interface_name(idxmap[curridx], nc['extranets'][netname])
if iname:
for iname in iname.split(','):
inames = get_interface_name(idxmap[curridx], nc['extranets'][netname])
if inames:
for iname in inames.split(','):
if uname in netname_to_interfaces:
netname_to_interfaces[uname]['interfaces'].add(iname)
else:
@@ -2,7 +2,6 @@
import random
import time
import subprocess
import importlib
import tempfile
import json
import os
@@ -545,18 +545,18 @@ if __name__ == '__main__':
time.sleep(1)
continue
dc = json.loads(dc)
iname = get_interface_name(idxmap[curridx], nc.get('default', {}))
if iname:
for iname in iname.split(','):
inames = get_interface_name(idxmap[curridx], nc.get('default', {}))
if inames:
for iname in inames.split(','):
if 'default' in netname_to_interfaces:
netname_to_interfaces['default']['interfaces'].add(iname)
else:
netname_to_interfaces['default'] = {'interfaces': set([iname]), 'settings': nc['default']}
for netname in nc.get('extranets', {}):
uname = '_' + netname
iname = get_interface_name(idxmap[curridx], nc['extranets'][netname])
if iname:
for iname in iname.split(','):
inames = get_interface_name(idxmap[curridx], nc['extranets'][netname])
if inames:
for iname in inames.split(','):
if uname in netname_to_interfaces:
netname_to_interfaces[uname]['interfaces'].add(iname)
else:
@@ -1,6 +1,5 @@
#!/usr/bin/python
import time
import importlib
import tempfile
import json
import os
@@ -4,7 +4,6 @@ except ImportError:
import ConfigParser as configparser
import cStringIO
import imp
import sys
apiclient = imp.load_source('apiclient', '/etc/confluent/apiclient')
repo = None
server = None
@@ -1,5 +1,4 @@
#!/usr/bin/python
import importlib
import tempfile
import json
import os
@@ -5,7 +5,6 @@ except ImportError:
import cStringIO
import importlib.util
import importlib.machinery
import sys
import glob
modloader = importlib.machinery.SourceFileLoader('apiclient', '/opt/confluent/bin/apiclient')
modspec = importlib.util.spec_from_file_location('apiclient', '/opt/confluent/bin/apiclient', loader=modloader)
@@ -229,8 +229,8 @@ def main():
sc.write(f'install --drive={nd[0]} --overwritevmfs\n')
else:
with open('/tmp/storagecfg', 'w') as sc:
sc.write(f'clearpart --firstdisk --overwritevmfs\n')
sc.write(f'install --firstdisk --overwritevmfs\n')
sc.write('clearpart --firstdisk --overwritevmfs\n')
sc.write('install --firstdisk --overwritevmfs\n')
if __name__ == '__main__':
@@ -2,7 +2,7 @@
import re
import subprocess
import json
uplinkmatch = re.compile('^\s*Uplinks:\s*(.*)')
uplinkmatch = re.compile(r'^\s*Uplinks:\s*(.*)')
nodename = None
for inf in open('/etc/confluent/confluent.info', 'r').read().split('\n'):
if inf.startswith('NODENAME: '):
@@ -1,6 +1,5 @@
#!/usr/bin/python3
import yaml
import os
ainst = {}
with open('/autoinstall.yaml', 'r') as allin:
@@ -409,6 +409,8 @@ class OEMHandler(generic.OEMHandler):
privilege_level):
if await self.is_fpc() and self._fpc_variant != 6:
await self.smmhandler.set_user_priv(uid, privilege_level)
if await self.has_xcc():
await self.immhandler.set_user_access(uid, privilege_level)
async def is_fpc(self):
"""True if the target is a Lenovo nextscale fan power controller"""
@@ -1361,10 +1363,6 @@ class OEMHandler(generic.OEMHandler):
return await self.immhandler.get_user_privilege_level(uid)
return None
async def set_user_access(self, uid, channel, callback, link_auth, ipmi_msg, privilege_level):
if await self.has_xcc():
await self.immhandler.set_user_access(uid, privilege_level)
async def process_zero_fru(self, zerofru):
if (self.oemid['manufacturer_id'] == 19046
and self.oemid['product_id'] == 13616):
@@ -623,12 +623,12 @@ class OEMHandler(generic.OEMHandler):
async def _get_agentless_firmware(self, components):
skipkeys = set([])
wc = await self.wc()
adata = await wc.grab_json_response(
adapterdata = await wc.grab_json_response(
'/api/dataset/imm_adapters?params=pci_GetAdapters')
fdata = await wc.grab_json_response(
'/api/function/adapter_update?params=pci_GetAdapterListAndFW')
anames = set()
for adata in adata.get('items', []):
for adata in adapterdata.get('items', []):
baseaname = adata['adapterName']
aname = baseaname
idx = 1
@@ -684,8 +684,8 @@ class OEMHandler(generic.OEMHandler):
"Drives":[
{'@odata.id': f'/redfish/v1/Systems/1/Storage/{cid}/Drives/{did}'} for did in spec_disks]}}
if spec_hotspares:
request_data["Links"]["DedicatedSpareDrives"] = {[
{'@odata.id': f'/redfish/v1/Systems/1/Storage/{cid}/Drives/{hid}' for hid in spec_hotspares}]}
request_data["Links"]["DedicatedSpareDrives"] = [
{'@odata.id': f'/redfish/v1/Systems/1/Storage/{cid}/Drives/{hid}'} for hid in spec_hotspares]
if volsize:
request_data["CapacityBytes"] = volsize
if stripsize:
-1
View File
@@ -32,7 +32,6 @@ import confluent.main
#p = cProfile.Profile(time.clock)
#p.enable()
#try:
import multiprocessing
def main():
confluent.main.run(sys.argv)
-2
View File
@@ -17,8 +17,6 @@ import confluent.certutil as certutil
import confluent.client as client
import confluent.config.configmanager as configmanager
import confluent.netutil as netutil
import tempfile
import shutil
import pwd
import signal
import confluent.collective.manager as collective
+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")
+11 -11
View File
@@ -131,24 +131,24 @@ def load_plugins():
continue
sys.path.insert(1, plugindir)
# two passes, to avoid adding both py and pyc files
for plugin in os.listdir(plugindir):
if plugin.startswith('.'):
for pluginname in os.listdir(plugindir):
if pluginname.startswith('.'):
continue
if '__pycache__' in plugin:
if '__pycache__' in pluginname:
continue
(plugin, plugtype) = os.path.splitext(plugin)
(pluginname, plugtype) = os.path.splitext(pluginname)
if plugtype == '.sh':
pluginmap[plugin] = shellmodule.Plugin(
os.path.join(plugindir, plugin + '.sh'))
elif "__init__" not in plugin:
plugins.add(plugin)
for plugin in plugins:
tmpmod = __import__(plugin)
pluginmap[pluginname] = shellmodule.Plugin(
os.path.join(plugindir, pluginname + '.sh'))
elif "__init__" not in pluginname:
plugins.add(pluginname)
for pluginname in plugins:
tmpmod = __import__(pluginname)
if 'plugin_names' in tmpmod.__dict__:
for name in tmpmod.plugin_names:
pluginmap[name] = tmpmod
else:
pluginmap[plugin] = tmpmod
pluginmap[pluginname] = tmpmod
_register_resource(tmpmod)
plugins.clear()
# restore path to not include the plugindir
+1 -1
View File
@@ -714,7 +714,7 @@ async def _recheck_nodes_backend(nodeattribs, configmanager):
info = pending_nodes[nodename]
try:
if info['handler'] is None:
next
continue
handler = info['handler'].NodeHandler(info, configmanager)
tasks.spawn(eval_node(configmanager, handler, info, nodename))
except Exception:
@@ -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):
+5 -15
View File
@@ -178,16 +178,10 @@ 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 +236,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
@@ -26,7 +26,6 @@ import confluent.tasks as tasks
import confluent.util as util
import aiohmi.exceptions as pygexc
import aiohmi.redfish.command as rcmd
import aiohmi.util.webclient as webclient
import aiohttp
class CustomVerifier(aiohttp.Fingerprint):
@@ -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)
@@ -637,28 +637,6 @@ class IpmiHandler:
os.unlink(certname)
await self.ipmicmd.install_bmc_certificate(cert)
async def handle_cert_authorities(self):
if len(self.element) == 3:
if self.op == 'read':
async for cert in self.ipmicmd.get_trusted_cas():
await self.output.put(msg.ChildCollection(cert['id']))
elif self.op == 'update':
cert = self.inputdata.get_pem(self.node)
await self.ipmicmd.add_trusted_ca(cert)
elif len(self.element) == 4:
certid = self.element[-1]
if self.op == 'read':
async for certdata in self.ipmicmd.get_trusted_cas():
if certdata['id'] == certid:
await self.output.put(msg.CertificateAuthority(
pem=certdata['pem'],
node=self.node,
subject=certdata['subject'],
san=certdata.get('san', None)))
elif self.op == 'delete':
await self.ipmicmd.del_trusted_ca(certid)
return
async def handle_alerts(self):
if self.element[3] == 'destinations':
if len(self.element) == 4:
+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")
-1
View File
@@ -1,5 +1,4 @@
from setuptools import setup
import os
setup(
name='confluent_server',
+1 -1
View File
@@ -5,7 +5,7 @@ import ctypes
import ctypes.util
import datetime
import inspect
from shutil import copytree as copytree
from shutil import copytree
if hasattr(inspect, 'getfullargspec') and 'dirs_exist_ok' in inspect.getfullargspec(copytree).args:
def copy_tree(src, dst):
copytree(src, dst, dirs_exist_ok=True)
+2
View File
@@ -1,3 +1,5 @@
import sys
uidmin = 1000
uidmax = 60000
gidmin = 1000
+2 -2
View File
@@ -171,11 +171,11 @@ async def main(switch, port):
portname = portcandidate
if not portname:
await ping_everywhere()
async for rsp in client.update(f'/networking/macs/rescan', {'rescan': 'start'}):
async for rsp in client.update('/networking/macs/rescan', {'rescan': 'start'}):
pass
scanning = True
while scanning:
async for rsp in client.read(f'/networking/macs/rescan'):
async for rsp in client.read('/networking/macs/rescan'):
if 'scanning' in rsp:
scanning = rsp['scanning']
if scanning:
-1
View File
@@ -1,7 +1,6 @@
#!/usr/bin/python3
import glob
import os
import select
import socket
-4
View File
@@ -232,10 +232,6 @@ def dotwait():
sys.stderr.flush()
time.sleep(0.5)
def disable_host_interface():
s = Session('/dev/ipmi0')
s.raw_command(netfn=0xc, command=1, data=(1, 0xc1, 0))
def get_redfish_creds():
os.makedirs('/run/redfish', exist_ok=True, mode=0o700)
try:
+87
View File
@@ -0,0 +1,87 @@
# Ruff configuration for confluent.
#
# py37 is the oldest version ruff can target. The oldest interpreter parts of
# this tree still run on is 3.6 (el8, sles15)
target-version = "py37"
line-length = 120
# Ruff discovers *.py only: it does not read shebangs when walking a tree, so
# without the patterns below it silently skips every CLI tool in
# confluent_client/bin and confluent_server/bin, the osdeploy deploy scripts,
# and the loose misc/ utilities.
# Some of the osdeploy scripts carry no shebang at all.
extend-include = [
"confluent_client/bin/*",
"confluent_server/bin/*",
# Generated into setup.py at build time by makesetup; #VERSION# only ever
# appears inside a string literal, so the template itself is valid Python.
"**/setup.py.tmpl",
"imgutil/imgutil",
"misc/filterpasswd",
"misc/getipsfromswitchport",
"misc/getusbnicaddr",
# confluent_osdeploy: per-profile deploy scripts, matched by name because
# each profile directory mixes Python and shell.
"**/bfb-autoinstall",
"**/nodedeploy-bfb",
"**/opt/confluent/bin/apiclient",
"**/scripts/add_local_repositories",
"**/scripts/autoconsole",
"**/scripts/configbmc",
"**/scripts/confignet",
"**/scripts/getinstalldisk",
"**/scripts/makeksnet",
"**/scripts/mergetime",
"**/scripts/syncfileclient",
]
extend-exclude = [
# Shell scripts that live in the directories included wholesale above.
"*.sh",
]
# Honour exclusions even when CI passes an explicit file list.
force-exclude = true
# `ruff format` is deliberately not adopted: the tree uses single quotes almost
# everywhere and reformatting it would bury real changes. Preserve quotes so an
# accidental run does less damage.
[format]
quote-style = "preserve"
[lint]
select = [
"E9", # unparseable file
"F63", # `is` against a literal, assert on a tuple, bad print/if-tuple
"F7", # statements in impossible positions: return/yield outside a
# function, break/continue outside a loop, except clause not last
"F81", # redefinition of an unused name (shadowed def/class)
"F82", # undefined name, undefined name in __all__, use before assignment
"F401", # unused import
"F402", # import shadowed by a loop variable
"F541", # f-string with no placeholders
"E401", # several imports on one line
"E701", # several statements on one line
"E711", # comparison to None with == rather than is
"E712", # comparison to True/False with == rather than truthiness
"E713", # `not x in y` rather than `x not in y`
"PLC0414", # import alias that renames nothing
"PLE", # pylint errors: bad string format, invalid returns, ...
"T100", # forgotten pdb/breakpoint call
"W6", # invalid escape sequence in a non-raw string, and any future
# deprecated-construct warning pycodestyle adds
"B015", # comparison whose result is discarded
"B020", # loop control variable overrides the iterable it iterates
"B023", # closure captures a loop variable, so every closure sees the
# last value rather than the one from its iteration
"B035", # dict comprehension with a static key
]
# Deliberately not selected, though currently at zero: B905 (zip without an
# explicit strict=). It only reports on py310+, so it reads as clean here, and
# "fixing" it would mean adding a keyword the oldest supported interpreters
# cannot parse.
# No ignores and no per-file exemptions: every rule selected above is expected
# to stay at zero on its own.