mirror of
https://github.com/xcat2/confluent.git
synced 2026-09-29 16:50:57 +00:00
Compare commits
127 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c8745292bf | |||
| 75e7b9040b | |||
| d2d77ab1d5 | |||
| ce8531375a | |||
| 762adb882a | |||
| 36687069aa | |||
| 11ff2dabfc | |||
| f9351484a4 | |||
| b22c17208a | |||
| 4982ac1a17 | |||
| a43d7e11e2 | |||
| c5896c056e | |||
| a73dced80b | |||
| b6188683b8 | |||
| 50243b67d5 | |||
| 7cdfcd4913 | |||
| 179ad4e196 | |||
| be2ae57a38 | |||
| f34395648e | |||
| 3f5d96788e | |||
| 17866d7657 | |||
| a1144fd49a | |||
| c472d96406 | |||
| 02791418bc | |||
| 2d29813320 | |||
| a9d15de156 | |||
| a4ba92a2e7 | |||
| 6938bba2d3 | |||
| 871685ea20 | |||
| a480cc73df | |||
| 39eb32df38 | |||
| 3505fe36e6 | |||
| 29accaa494 | |||
| f66093680b | |||
| 97d4015b09 | |||
| 184132c398 | |||
| ac7fdb3ef7 | |||
| d7879bad5b | |||
| 8911193aca | |||
| e7e8daafea | |||
| 3f9a13ed6f | |||
| 500cdf7535 | |||
| 22c8921455 | |||
| ebcf7d7bf8 | |||
| 7a2cb80f6a | |||
| dd2b7be2ca | |||
| 678bd6052a | |||
| cb5fcf077a | |||
| 5f26fb73e6 | |||
| c9ca199b16 | |||
| 8109adaabf | |||
| 29c6ce230f | |||
| 87a6891eff | |||
| a112297e60 | |||
| c567bfbd17 | |||
| 6d2146f252 | |||
| 5045b46014 | |||
| 5905510a32 | |||
| f321f56109 | |||
| 9defc47474 | |||
| 595b628e08 | |||
| 710b24e9f5 | |||
| c26fba74e7 | |||
| a01eb64adc | |||
| ac8179b867 | |||
| 87990c72c3 | |||
| a6a57e8590 | |||
| 6be98c7e60 | |||
| 1a64768fca | |||
| 157641e37a | |||
| 63bbe53448 | |||
| ec3fcee7d7 | |||
| b2b2b5710b | |||
| b32ded9c6a | |||
| 75c228dae4 | |||
| afd2b6c219 | |||
| 9a85b9ee94 | |||
| c9c5165245 | |||
| d4e91b1c7e | |||
| 98e78dd43c | |||
| e7606e69bd | |||
| 580c451945 | |||
| a71804a13b | |||
| dbda4f45a1 | |||
| 5ac0cccc4d | |||
| 465e985cc7 | |||
| 836b629986 | |||
| 58b6a2d317 | |||
| fc6c1495d3 | |||
| c9b9275bb1 | |||
| c0a99f63a5 | |||
| 51afcc68a7 | |||
| 902ff43a9b | |||
| e01701bcf1 | |||
| a1cf8023c6 | |||
| 960a890530 | |||
| d43de05b09 | |||
| 36ce0922fc | |||
| aafa65274c | |||
| 63bb5f4d1b | |||
| d99689f84b | |||
| 816f3be2ed | |||
| bab169269c | |||
| 85ddf528a2 | |||
| 8cfbf40a2e | |||
| 48a0c21300 | |||
| 2c43055aec | |||
| 16a1c4d598 | |||
| 97e4d7c3d0 | |||
| cfa16237e1 | |||
| 7066f85520 | |||
| 8c6f36adf3 | |||
| 33cee4174e | |||
| 47710756a5 | |||
| 21429c6d7d | |||
| ff0c11e919 | |||
| bf209a8009 | |||
| 6ec072be9d | |||
| 79e44e420a | |||
| 5a0b2468f6 | |||
| 13d9fe2712 | |||
| 5028ed9f07 | |||
| 05dbbd6ce0 | |||
| 61749c3649 | |||
| 1f3b84cc9d | |||
| ac42c1b4c7 | |||
| e489d2d532 |
@@ -13,7 +13,7 @@ If you're coming from xCAT, check out [this comparison](https://hpc.lenovo.com/u
|
||||
|
||||
# Documentation
|
||||
|
||||
Confluent documentation is hosted on hpc.lenovo.com: https://hpc.lenovo.com/users/documentation/
|
||||
Confluent documentation is hosted on: https://xcat2.github.io/confluent-docs/
|
||||
|
||||
# Download
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ import math
|
||||
import getpass
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
import select
|
||||
import shlex
|
||||
import signal
|
||||
@@ -969,8 +970,15 @@ def main():
|
||||
sys.stdout.write('Lost connection to server')
|
||||
quitconfetty(fullexit=True)
|
||||
|
||||
sgr_re = re.compile(r'(\x1b\[[0-9;]*m)')
|
||||
sgr_parameters_re = re.compile(r'\x1b\[([0-9;]*)m')
|
||||
fgcolor = None
|
||||
bgcolor = None
|
||||
fgshifted = False
|
||||
pendseq = ''
|
||||
def consume_termdata(fh, bufferonly=False):
|
||||
global clearpowermessage
|
||||
global fgcolor, bgcolor, fgshifted, pendseq
|
||||
try:
|
||||
data = tlvdata.recv(fh)
|
||||
except Exception:
|
||||
@@ -979,7 +987,59 @@ def consume_termdata(fh, bufferonly=False):
|
||||
updatestatus(data)
|
||||
return ''
|
||||
if data is not None:
|
||||
data = client.stringify(data)
|
||||
indata = pendseq + client.stringify(data)
|
||||
pendseq = ''
|
||||
data = ''
|
||||
for segment in sgr_re.split(indata):
|
||||
if sgr_re.match(segment): # it is an sgr, analyze, maybe replace
|
||||
params = []
|
||||
for parameters in sgr_parameters_re.findall(segment):
|
||||
for param in parameters.split(';'):
|
||||
params.append(param)
|
||||
if param == '0':
|
||||
fgcolor = None
|
||||
bgcolor = None
|
||||
try:
|
||||
ival = int(param)
|
||||
except ValueError:
|
||||
continue
|
||||
if 40 <= ival <= 47 or 100 <= ival <= 107:
|
||||
bgcolor = ival
|
||||
if 30 <= ival <= 37 or 90 <= ival <= 97:
|
||||
fgcolor = ival
|
||||
if bgcolor is not None:
|
||||
fgindicated = False
|
||||
for idx, param in enumerate(params):
|
||||
try:
|
||||
ival = int(param)
|
||||
except ValueError:
|
||||
continue
|
||||
if 30 <= ival <= 37 and (bgcolor % 10 == ival % 10):
|
||||
fgindicated = True
|
||||
fgshifted = True
|
||||
ival += 60
|
||||
params[idx] = str(ival)
|
||||
if not fgindicated and fgcolor is not None:
|
||||
if bgcolor and (bgcolor % 10) == (fgcolor % 10):
|
||||
fgshifted = True
|
||||
params.append(str((fgcolor % 10) + 90))
|
||||
elif fgshifted:
|
||||
params.append(str(fgcolor))
|
||||
segment = '\x1b[' + ';'.join(str(p) for p in params) + 'm'
|
||||
data += segment
|
||||
# defer any partial ansi escape sequence for a later pass
|
||||
escidx = segment.rfind('\x1b[')
|
||||
if escidx >= 0:
|
||||
for chr in segment[escidx + 2:]:
|
||||
if 0x40 <= ord(chr) <= 0x7e:
|
||||
break
|
||||
else:
|
||||
# incomplete escape sequence, don't print it yet
|
||||
data = data[:-len(segment) + escidx]
|
||||
pendseq = segment[escidx:]
|
||||
if not pendseq and segment and segment[-1] == '\x1b':
|
||||
data = data[:-1]
|
||||
pendseq = '\x1b'
|
||||
if clearpowermessage:
|
||||
sys.stdout.write("\x1b[2J\x1b[;H")
|
||||
clearpowermessage = False
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
import optparse
|
||||
import signal
|
||||
import sys
|
||||
import os
|
||||
|
||||
try:
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
except AttributeError:
|
||||
@@ -17,18 +18,16 @@ import confluent.client as client
|
||||
import confluent.sortutil as sortutil
|
||||
|
||||
|
||||
def lookupdata(data, key):
|
||||
ret = data.get(key, {}).get('value', '')
|
||||
if ret is None:
|
||||
ret = ''
|
||||
return ret
|
||||
|
||||
|
||||
def main():
|
||||
argparser = optparse.OptionParser(
|
||||
usage='''\n %prog noderange -o ansible.hosts
|
||||
\n ''')
|
||||
usage='''\n %prog noderange -o ansible.hosts -a
|
||||
''')
|
||||
argparser.add_option('-o', '--output',
|
||||
help='Ansible hosts file')
|
||||
help='Writes an Ansible hosts file')
|
||||
argparser.add_option('-a', '--append', action='store_true',
|
||||
help='Appends to existing hosts file')
|
||||
(options, args) = argparser.parse_args()
|
||||
try:
|
||||
noderange = args[0]
|
||||
@@ -45,24 +44,42 @@ def main():
|
||||
if node not in databynode:
|
||||
databynode[node] = {}
|
||||
databynode[node].update(res['databynode'][node])
|
||||
|
||||
nodesbygroup = {}
|
||||
with open(options.output, 'w') as importfile:
|
||||
needempty = False
|
||||
for node in sortutil.natural_sort(databynode):
|
||||
data = databynode[node]
|
||||
if not data.get('groups', []):
|
||||
importfile.write(node + '\n')
|
||||
needempty = True
|
||||
for g in data.get('groups', []):
|
||||
if g not in nodesbygroup:
|
||||
nodesbygroup[g] = set([node])
|
||||
for node in sortutil.natural_sort(databynode):
|
||||
data = databynode[node]
|
||||
groups = data.get('groups', [])
|
||||
if not groups:
|
||||
nodesbygroup.setdefault('', set()).add(node.strip().lower())
|
||||
else:
|
||||
for g in groups:
|
||||
nodesbygroup.setdefault(g, set()).add(node.strip().lower())
|
||||
existing_data = {}
|
||||
if options.append and os.path.exists(options.output):
|
||||
current_group = ''
|
||||
with open(options.output, 'r') as f:
|
||||
for line in f:
|
||||
line = line.strip().lower()
|
||||
if not line:
|
||||
continue
|
||||
if line.startswith('[') and line.endswith(']'):
|
||||
current_group = line[1:-1]
|
||||
existing_data.setdefault(current_group, set())
|
||||
else:
|
||||
nodesbygroup[g].add(node)
|
||||
if needempty:
|
||||
importfile.write('\n')
|
||||
for group in sortutil.natural_sort(nodesbygroup):
|
||||
importfile.write('[{0}]\n'.format(group))
|
||||
for node in sortutil.natural_sort(nodesbygroup[group]):
|
||||
existing_data.setdefault(current_group, set()).add(line)
|
||||
|
||||
for group, nodes in nodesbygroup.items():
|
||||
nodes = {n.strip().lower() for n in nodes}
|
||||
current_nodes = existing_data.get(group, set())
|
||||
new_nodes = nodes - current_nodes
|
||||
if new_nodes:
|
||||
existing_data.setdefault(group, set()).update(nodes)
|
||||
|
||||
with open(options.output, 'w') as importfile:
|
||||
for group in sortutil.natural_sort(existing_data.keys()):
|
||||
if group:
|
||||
importfile.write('[{0}]\n'.format(group))
|
||||
for node in sortutil.natural_sort(existing_data[group]):
|
||||
importfile.write('{0}\n'.format(node))
|
||||
importfile.write('\n')
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
from cryptography import x509
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
|
||||
path = os.path.dirname(os.path.realpath(__file__))
|
||||
path = os.path.realpath(os.path.join(path, '..', 'lib', 'python'))
|
||||
if path.startswith('/opt'):
|
||||
sys.path.append(path)
|
||||
import confluent.client as client
|
||||
|
||||
def removebmccacert(noderange, certid, cmd):
|
||||
for res in cmd.delete(f'/noderange/{noderange}/configuration/management_controller/certificate_authorities/{certid}'):
|
||||
print(repr(res))
|
||||
|
||||
def listbmccacerts(noderange, cmd):
|
||||
certids = []
|
||||
for res in cmd.read(f'/noderange/{noderange}/configuration/management_controller/certificate_authorities'):
|
||||
certids.append(res.get('item', {}).get('href', ''))
|
||||
for certid in certids:
|
||||
for res in cmd.read(f'/noderange/{noderange}/configuration/management_controller/certificate_authorities/{certid}'):
|
||||
for node in res.get('databynode', {}):
|
||||
certdata = res['databynode'][node].get('pem', {}).get('value', '')
|
||||
summary = ''
|
||||
if not certdata:
|
||||
continue
|
||||
san = res['databynode'][node].get('san', {}).get('value', '')
|
||||
if san:
|
||||
summary += f" SANs: {san}"
|
||||
subject = res['databynode'][node].get('subject', {}).get('value', '')
|
||||
if subject:
|
||||
summary = subject
|
||||
try:
|
||||
cert = x509.load_pem_x509_certificate(certdata.encode())
|
||||
sha256 = cert.fingerprint(hashes.SHA256()).hex().upper()
|
||||
except Exception as e:
|
||||
print(f"Error processing certificate for {node}: {e}", file=sys.stderr)
|
||||
continue
|
||||
summary += f" (SHA256={sha256})"
|
||||
print(f"{node}: {certid}: {summary}")
|
||||
|
||||
def installbmccacert(noderange, certfile, cmd):
|
||||
if certfile:
|
||||
try:
|
||||
with open(certfile, 'r') as f:
|
||||
certdata = f.read()
|
||||
except Exception as e:
|
||||
print(f"Error reading certificate file: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Simple validation: check if it starts and ends with the correct PEM markers
|
||||
if not (certdata.startswith("-----BEGIN CERTIFICATE-----") and certdata.strip().endswith("-----END CERTIFICATE-----")):
|
||||
print("Invalid certificate format. Must be a PEM encoded certificate.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
payload = {'pem': certdata}
|
||||
for res in cmd.update(f'/noderange/{noderange}/configuration/management_controller/certificate_authorities', payload):
|
||||
print(repr(res))
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description='Node certificate utility')
|
||||
parser.add_argument('noderange', help='Node range to operate on')
|
||||
subparsers = parser.add_subparsers(dest='command', help='Available commands')
|
||||
|
||||
# installbmccacert subcommand
|
||||
install_parser = subparsers.add_parser('installbmccacert', help='Install BMC CA certificate')
|
||||
install_parser.add_argument('filename', help='Certificate file to install')
|
||||
|
||||
remove_parser = subparsers.add_parser('removebmccacert', help='Remove BMC CA certificate')
|
||||
remove_parser.add_argument('id', help='Certificate id to remove')
|
||||
|
||||
list_parser = subparsers.add_parser('listbmccacerts', help='List BMC CA certificates')
|
||||
|
||||
args = parser.parse_args()
|
||||
c = client.Command()
|
||||
if args.command == 'installbmccacert':
|
||||
installbmccacert(args.noderange, args.filename, c)
|
||||
elif args.command == 'removebmccacert':
|
||||
removebmccacert(args.noderange, args.id, c)
|
||||
elif args.command == 'listbmccacerts':
|
||||
listbmccacerts(args.noderange, c)
|
||||
else:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2017 Lenovo
|
||||
# Copyright 2025 Lenovo
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -96,6 +96,12 @@ cfgpaths = {
|
||||
'bmc.static_ipv6_gateway': (
|
||||
'configuration/management_controller/net_interfaces/management',
|
||||
'static_v6_gateway'),
|
||||
'bmc.vlan_id': (
|
||||
'configuration/management_controller/net_interfaces/management',
|
||||
'vlan_id'),
|
||||
'bmc.mac_address': (
|
||||
'configuration/management_controller/net_interfaces/management',
|
||||
'hw_addr'),
|
||||
'bmc.hostname': (
|
||||
'configuration/management_controller/hostname', 'hostname'),
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ import termios
|
||||
import fcntl
|
||||
import confluent.screensqueeze as sq
|
||||
try:
|
||||
from PIL import Image, ImageDraw
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
except ImportError:
|
||||
Image = None
|
||||
|
||||
@@ -227,22 +227,24 @@ def draw_text(text, width, height):
|
||||
nd = ImageDraw.Draw(nerr)
|
||||
for txtpiece in text.split('\n'):
|
||||
fntsize = 8
|
||||
while nd.textlength(txtpiece, font_size=fntsize) < int(imgwidth * 0.90):
|
||||
txtfont = ImageFont.truetype('DejaVuSans.ttf', size=fntsize)
|
||||
while nd.textlength(txtpiece, font=txtfont) < int(imgwidth * 0.90):
|
||||
fntsize += 1
|
||||
txtfont = ImageFont.truetype('DejaVuSans.ttf', size=fntsize)
|
||||
fntsize -= 1
|
||||
if fntsize < maxfntsize:
|
||||
maxfntsize = fntsize
|
||||
hmargin = int(imgwidth * 0.05)
|
||||
vmargin = int(imgheight * 0.10)
|
||||
nd.text((hmargin, vmargin), text, font_size=maxfntsize)
|
||||
nd.rectangle((0, 0, nerr.width - 1, nerr.height -1), outline='white', width=1)
|
||||
nd.text((hmargin, vmargin), text, font=txtfont)
|
||||
nd.rectangle((0, 0, nerr.width - 1, nerr.height -1), outline='white')
|
||||
outfile = io.BytesIO()
|
||||
nerr.save(outfile, format='PNG')
|
||||
data = base64.b64encode(outfile.getbuffer())
|
||||
draw_image(data, width, height, doscale=False)
|
||||
else:
|
||||
sys.stdout.write(text)
|
||||
cursor_left(len(txt))
|
||||
cursor_left(len(text))
|
||||
|
||||
def draw_image(data, width, height, doscale=True):
|
||||
imageformat = os.environ.get('CONFLUENT_IMAGE_PROTOCOL', 'kitty')
|
||||
@@ -270,8 +272,8 @@ def draw_image(data, width, height, doscale=True):
|
||||
rzheight = imgheight
|
||||
img = img.resize((rzwidth, rzheight))
|
||||
nd = ImageDraw.Draw(nimg)
|
||||
nd.rectangle((1, 1, rzwidth + 2, rzheight + 2), outline='black', width=1)
|
||||
nd.rectangle((0, 0, rzwidth + 3, rzheight + 3), outline='white', width=1)
|
||||
nd.rectangle((1, 1, rzwidth + 2, rzheight + 2), outline='black')
|
||||
nd.rectangle((0, 0, rzwidth + 3, rzheight + 3), outline='white')
|
||||
nimg.paste(img, box=(2, 2))
|
||||
outfile = io.BytesIO()
|
||||
nimg.save(outfile, format='PNG')
|
||||
|
||||
@@ -48,7 +48,18 @@ def armonce(nr, cli):
|
||||
pass
|
||||
|
||||
|
||||
def setpending(nr, profile, cli):
|
||||
def setpending(nr, profile, profilebynodes, cli):
|
||||
if profilebynodes:
|
||||
for node in sortutil.natural_sort(profilebynodes):
|
||||
prof = profilebynodes[node]
|
||||
args = {'deployment.pendingprofile': prof, 'deployment.state': '', 'deployment.state_detail': ''}
|
||||
if not prof.startswith('genesis-'):
|
||||
args['deployment.stagedprofile'] = ''
|
||||
args['deployment.profile'] = ''
|
||||
for rsp in cli.update('/nodes/{0}/attributes/current'.format(node),
|
||||
args):
|
||||
pass
|
||||
return
|
||||
args = {'deployment.pendingprofile': profile, 'deployment.state': '', 'deployment.state_detail': ''}
|
||||
if not profile.startswith('genesis-'):
|
||||
args['deployment.stagedprofile'] = ''
|
||||
@@ -69,6 +80,7 @@ def main(args):
|
||||
ap.add_argument('-n', '--network', help='Initiate deployment over PXE/HTTP', action='store_true')
|
||||
ap.add_argument('-p', '--prepareonly', help='Prepare only, skip any interaction with a BMC associated with this deployment action', action='store_true')
|
||||
ap.add_argument('-m', '--maxnodes', help='Specifiy a maximum nodes to be deployed')
|
||||
ap.add_argument('-r', '--redeploy', help='Redeploy nodes with the current or pending profile', action='store_true')
|
||||
ap.add_argument('noderange', help='Set of nodes to deploy')
|
||||
ap.add_argument('profile', nargs='?', help='Profile name to deploy')
|
||||
args, extra = ap.parse_known_args(args)
|
||||
@@ -78,7 +90,7 @@ def main(args):
|
||||
if args.profile and not args.network:
|
||||
sys.stderr.write('-n is a required argument currently to perform an install, optionally with -p\n')
|
||||
return 1
|
||||
if not args.profile and args.network:
|
||||
if not args.profile and args.network and not args.redeploy:
|
||||
sys.stderr.write('Both noderange and a profile name are required arguments to request a network deployment\n')
|
||||
return 1
|
||||
if args.clear and args.profile:
|
||||
@@ -96,27 +108,38 @@ def main(args):
|
||||
if 'error' in rsp:
|
||||
sys.stderr.write(rsp['error'] + '\n')
|
||||
sys.exit(1)
|
||||
profilebynode = {}
|
||||
if args.clear:
|
||||
cleararm(args.noderange, c)
|
||||
clearpending(args.noderange, c)
|
||||
elif args.profile:
|
||||
profnames = []
|
||||
for prof in c.read('/deployment/profiles/'):
|
||||
profname = prof.get('item', {}).get('href', None)
|
||||
if profname:
|
||||
profname = profname.replace('/', '')
|
||||
profnames.append(profname)
|
||||
if profname == args.profile:
|
||||
break
|
||||
else:
|
||||
sys.stderr.write('The specified profile "{}" is not an available profile\n'.format(args.profile))
|
||||
if profnames:
|
||||
sys.stderr.write('The following profiles are available:\n')
|
||||
for profname in profnames:
|
||||
sys.stderr.write(' ' + profname + '\n')
|
||||
else:
|
||||
sys.stderr.write('No deployment profiles available, try osdeploy import or imgutil capture\n')
|
||||
sys.exit(1)
|
||||
elif args.redeploy:
|
||||
hadpending = {}
|
||||
for rsp in c.read('/noderange/{0}/attributes/current'.format(args.noderange)):
|
||||
for node in rsp.get('databynode', {}):
|
||||
nodeinfo = rsp['databynode'][node]
|
||||
for attr in nodeinfo:
|
||||
if attr == 'deployment.pendingprofile':
|
||||
curr = nodeinfo[attr].get('value', '')
|
||||
if curr:
|
||||
hadpending[node] = True
|
||||
profilebynode[node] = curr
|
||||
if attr == 'deployment.stagedprofile':
|
||||
curr = nodeinfo[attr].get('value', '')
|
||||
if curr and node not in hadpending:
|
||||
profilebynode[node] = curr
|
||||
if attr == 'deployment.profile':
|
||||
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
|
||||
if args.profile or profilebynode:
|
||||
lockednodes = []
|
||||
for lockinfo in c.read('/noderange/{0}/deployment/lock'.format(args.noderange)):
|
||||
for node in lockinfo.get('databynode', {}):
|
||||
@@ -127,8 +150,26 @@ def main(args):
|
||||
sys.stderr.write('Requested noderange has nodes with locked deployment: ' + ','.join(lockednodes))
|
||||
sys.stderr.write('\n')
|
||||
sys.exit(1)
|
||||
if args.profile:
|
||||
profnames = []
|
||||
for prof in c.read('/deployment/profiles/'):
|
||||
profname = prof.get('item', {}).get('href', None)
|
||||
if profname:
|
||||
profname = profname.replace('/', '')
|
||||
profnames.append(profname)
|
||||
if profname == args.profile:
|
||||
break
|
||||
else:
|
||||
sys.stderr.write('The specified profile "{}" is not an available profile\n'.format(args.profile))
|
||||
if profnames:
|
||||
sys.stderr.write('The following profiles are available:\n')
|
||||
for profname in profnames:
|
||||
sys.stderr.write(' ' + profname + '\n')
|
||||
else:
|
||||
sys.stderr.write('No deployment profiles available, try osdeploy import or imgutil capture\n')
|
||||
sys.exit(1)
|
||||
armonce(args.noderange, c)
|
||||
setpending(args.noderange, args.profile, c)
|
||||
setpending(args.noderange, args.profile, profilebynode, c)
|
||||
else:
|
||||
databynode = {}
|
||||
for r in c.read('/noderange/{0}/attributes/current'.format(args.noderange)):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2016-2017 Lenovo
|
||||
@@ -114,11 +114,24 @@ def update_firmware(session, filename):
|
||||
upargs['bank'] = 'backup'
|
||||
noderrs = {}
|
||||
if session.unixdomain:
|
||||
of = open(filename, 'rb')
|
||||
try:
|
||||
session.add_file(filename, of.fileno(), 'rb')
|
||||
except Exception:
|
||||
pass
|
||||
filesbynode = {}
|
||||
for exp in session.create('/noderange/{0}/attributes/expression'.format(noderange),
|
||||
{'expression': filename}):
|
||||
if 'error' in exp:
|
||||
sys.stderr.write(exp['error'] + '\n')
|
||||
exitcode |= exp.get('errorcode', 1)
|
||||
ex = exp.get('databynode', ())
|
||||
for node in ex:
|
||||
filesbynode[node] = ex[node]['value']
|
||||
if not isinstance(filesbynode[node], bytes) and not isinstance(filesbynode[node], str):
|
||||
filesbynode[node] = filesbynode[node].encode('utf-8')
|
||||
for node in filesbynode:
|
||||
endfilename = filesbynode[node]
|
||||
of = open(endfilename, 'rb')
|
||||
try:
|
||||
session.add_file(endfilename, of.fileno(), 'rb')
|
||||
except Exception:
|
||||
pass
|
||||
for res in session.create(resource, upargs):
|
||||
if 'created' not in res:
|
||||
for nodename in res.get('databynode', ()):
|
||||
@@ -153,9 +166,13 @@ def show_firmware(session):
|
||||
firmware_shown = False
|
||||
nodes_matched = False
|
||||
for component in components:
|
||||
category = 'all'
|
||||
if component in ('adapters', 'disks', 'misc', 'core'):
|
||||
category = component
|
||||
component = 'all'
|
||||
for res in session.read(
|
||||
'/noderange/{0}/inventory/firmware/all/{1}'.format(
|
||||
noderange, component)):
|
||||
'/noderange/{0}/inventory/firmware/{2}/{1}'.format(
|
||||
noderange, component, category)):
|
||||
nodes_matched = True
|
||||
exitcode |= client.printerror(res)
|
||||
if 'databynode' not in res:
|
||||
|
||||
@@ -49,7 +49,9 @@ def pretty(text):
|
||||
|
||||
def print_mem_info(node, prefix, meminfo):
|
||||
memdescfmt = '{0}GB PC'
|
||||
if meminfo['memory_type'] == 'DDR3 SDRAM':
|
||||
if meminfo['memory_type'] is None:
|
||||
memdescfmt = '{0}GB '
|
||||
elif meminfo['memory_type'] == 'DDR3 SDRAM':
|
||||
memdescfmt += '3-{1} '
|
||||
elif 'DDR4' in meminfo['memory_type']:
|
||||
memdescfmt += '4-{1} '
|
||||
@@ -58,16 +60,21 @@ def print_mem_info(node, prefix, meminfo):
|
||||
elif 'DCPMM' in meminfo['memory_type']:
|
||||
memdescfmt = '{0}GB {1} '
|
||||
meminfo['module_type'] = 'DCPMM'
|
||||
elif meminfo['memory_type'] == 'HBM':
|
||||
memdescfmt = '{0}GB HBM '
|
||||
else:
|
||||
print('{0}: {1}: Unrecognized Memory'.format(node, prefix))
|
||||
return
|
||||
if meminfo.get('ecc', False):
|
||||
memdescfmt += 'ECC '
|
||||
capacity = meminfo['capacity_mb'] / 1024
|
||||
modtype = meminfo.get('module_type', None)
|
||||
if modtype:
|
||||
memdescfmt += modtype
|
||||
memdesc = memdescfmt.format(capacity, meminfo['speed'])
|
||||
if meminfo.get('capacity_mb', None):
|
||||
capacity = meminfo['capacity_mb'] // 1024
|
||||
memdesc = memdescfmt.format(capacity, meminfo['speed'])
|
||||
else:
|
||||
memdesc = 'Unspecified Module'
|
||||
print('{0}: {1} description: {2}'.format(node, prefix, memdesc))
|
||||
print('{0}: {1} manufacturer: {2}'.format(
|
||||
node, prefix, meminfo['manufacturer']))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2019 Lenovo
|
||||
@@ -65,16 +65,30 @@ client.check_globbing(noderange)
|
||||
|
||||
def install_license(session, filename):
|
||||
global exitcode
|
||||
|
||||
resource = '/noderange/{0}/configuration/' \
|
||||
'management_controller/licenses/'.format(noderange)
|
||||
filename = os.path.abspath(filename)
|
||||
instargs = {'filename': filename}
|
||||
if session.unixdomain:
|
||||
of = open(filename, 'rb')
|
||||
try:
|
||||
session.add_file(filename, of.fileno(), 'rb')
|
||||
except Exception:
|
||||
pass
|
||||
filesbynode = {}
|
||||
for exp in session.create('/noderange/{0}/attributes/expression'.format(noderange),
|
||||
{'expression': filename}):
|
||||
if 'error' in exp:
|
||||
sys.stderr.write(exp['error'] + '\n')
|
||||
exitcode |= exp.get('errorcode', 1)
|
||||
ex = exp.get('databynode', ())
|
||||
for node in ex:
|
||||
filesbynode[node] = ex[node]['value']
|
||||
if not isinstance(filesbynode[node], bytes) and not isinstance(filesbynode[node], str):
|
||||
filesbynode[node] = filesbynode[node].encode('utf-8')
|
||||
for node in filesbynode:
|
||||
endfilename = filesbynode[node]
|
||||
of = open(endfilename, 'rb')
|
||||
try:
|
||||
session.add_file(endfilename, of.fileno(), 'rb')
|
||||
except Exception:
|
||||
pass
|
||||
for res in session.create(resource, instargs):
|
||||
for node in res.get('databynode', []):
|
||||
if 'error' in res['databynode'][node]:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2018 Lenovo
|
||||
@@ -101,22 +101,37 @@ def detach_media(noderange, media):
|
||||
|
||||
def upload_media(noderange, media):
|
||||
global exitcode
|
||||
if not os.path.exists(media):
|
||||
sys.stderr.write('Unable to locate requested file {0}\n'.format(
|
||||
media))
|
||||
sys.exit(404)
|
||||
|
||||
session = client.Command()
|
||||
output = sq.ScreenPrinter(noderange, session)
|
||||
filename = os.path.abspath(media)
|
||||
resource = '/noderange/{0}/media/uploads/'.format(noderange)
|
||||
filename = os.path.abspath(filename)
|
||||
upargs = {'filename': filename}
|
||||
noderrs = {}
|
||||
if session.unixdomain:
|
||||
of = open(filename, 'rb')
|
||||
try:
|
||||
session.add_file(filename, of.fileno(), 'rb')
|
||||
except Exception:
|
||||
pass
|
||||
filesbynode = {}
|
||||
for exp in session.create('/noderange/{0}/attributes/expression'.format(noderange),
|
||||
{'expression': filename}):
|
||||
if 'error' in exp:
|
||||
sys.stderr.write(exp['error'] + '\n')
|
||||
exitcode |= exp.get('errorcode', 1)
|
||||
ex = exp.get('databynode', ())
|
||||
for node in ex:
|
||||
filesbynode[node] = ex[node]['value']
|
||||
if not isinstance(filesbynode[node], bytes) and not isinstance(filesbynode[node], str):
|
||||
filesbynode[node] = filesbynode[node].encode('utf-8')
|
||||
for node in filesbynode:
|
||||
endfilename = filesbynode[node]
|
||||
if not os.path.exists(endfilename):
|
||||
sys.stderr.write('Unable to locate requested file {0}\n'.format(
|
||||
endfilename))
|
||||
sys.exit(404)
|
||||
of = open(endfilename, 'rb')
|
||||
try:
|
||||
session.add_file(endfilename, of.fileno(), 'rb')
|
||||
except Exception:
|
||||
pass
|
||||
nodeurls = {}
|
||||
for res in session.create(resource, upargs):
|
||||
if 'created' not in res:
|
||||
|
||||
@@ -57,7 +57,7 @@ def stringify(instr):
|
||||
# Normalize unicode and bytes to 'str', correcting for
|
||||
# current python version
|
||||
if isinstance(instr, bytes) and not isinstance(instr, str):
|
||||
return instr.decode('utf-8')
|
||||
return instr.decode('utf-8', errors='replace')
|
||||
elif not isinstance(instr, bytes) and not isinstance(instr, str):
|
||||
return instr.encode('utf-8')
|
||||
return instr
|
||||
@@ -464,8 +464,8 @@ def printattributes(session, requestargs, showtype, nodetype, noderange, options
|
||||
|
||||
def _sort_attrib(k):
|
||||
if isinstance(k[1], dict) and k[1].get('sortid', None) is not None:
|
||||
return k[1]['sortid']
|
||||
return k[0]
|
||||
return sortutil.naturalize_string('{}'.format(k[1]['sortid']))
|
||||
return sortutil.naturalize_string(k[0])
|
||||
|
||||
def print_attrib_path(path, session, requestargs, options, rename=None, attrprefix=None):
|
||||
exitcode = 0
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
# imgutil(1) -- Work with confluent OS cloning and diskless images
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`imgutil` `build` [<options>] <scratchdir>
|
||||
`imgutil` `exec` [<options>] <scratchdir> [<cmd>...]
|
||||
`imgutil` `unpack` <profilename> <scratchdir>
|
||||
`imgutil` `pack` [<options>] <scratchdir> <profilename>
|
||||
`imgutil` `capture` <node> <profilename>
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
**imgutil** is a utility for creating, managing, and deploying OS images for diskless boot and system cloning in a Confluent environment. It supports building images from scratch, capturing images from running systems, and packing/unpacking diskless profiles.
|
||||
|
||||
## COMMANDS
|
||||
|
||||
* `build`:
|
||||
Build a new diskless image from scratch in the specified scratch directory.
|
||||
|
||||
* `exec`:
|
||||
Start the specified scratch directory as a container and optionally run a command inside it.
|
||||
|
||||
* `unpack`:
|
||||
Unpack a diskless image profile to a scratch directory for modification.
|
||||
|
||||
* `pack`:
|
||||
Pack a scratch directory into a diskless profile that can be deployed.
|
||||
|
||||
* `capture`:
|
||||
Capture an image for cloning from a running system.
|
||||
|
||||
## BUILD OPTIONS
|
||||
|
||||
* `-r`, `--addrepos` <repository>:
|
||||
Repositories to add in addition to the main source. May be specified multiple times.
|
||||
|
||||
* `-p`, `--packagelist` <file>:
|
||||
Filename of package list to replace default pkglist.
|
||||
|
||||
* `-a`, `--addpackagelist` <file>:
|
||||
A list of additional packages to include. May be specified multiple times.
|
||||
|
||||
* `-s`, `--source` <directory>:
|
||||
Directory to pull installation from, typically a subdirectory of `/var/lib/confluent/distributions`. By default, the repositories for the build system are used. For Ubuntu, this is not supported; the build system repositories are always used.
|
||||
|
||||
* `-y`, `--non-interactive`:
|
||||
Avoid prompting for confirmation.
|
||||
|
||||
* `-v`, `--volume` <mount>:
|
||||
Directory to make available in the build environment. `-v /` will cause it to be mounted in image as `/run/external/`. `-v /:/run/root` will override the target to be `/run/root`. Something like `/var/lib/repository:-` will cause it to mount to the identical path inside the image. May be specified multiple times.
|
||||
|
||||
* <scratchdir>:
|
||||
Directory to build new diskless root in.
|
||||
|
||||
## EXEC OPTIONS
|
||||
|
||||
* `-v`, `--volume` <mount>:
|
||||
Directory to make available in the build environment. `-v /` will cause it to be mounted in image as `/run/external/`. `-v /:/run/root` will override the target to be `/run/root`. May be specified multiple times.
|
||||
|
||||
* <scratchdir>:
|
||||
Directory of an unpacked diskless root.
|
||||
|
||||
* <cmd>:
|
||||
Optional command to run (defaults to a shell).
|
||||
|
||||
## UNPACK OPTIONS
|
||||
|
||||
* <profilename>:
|
||||
The diskless OS profile to unpack.
|
||||
|
||||
* <scratchdir>:
|
||||
Directory to extract diskless root to.
|
||||
|
||||
## PACK OPTIONS
|
||||
|
||||
* `-b`, `--baseprofile` <profile>:
|
||||
Profile to copy extra info from. For example, to make a new version of an existing profile, reference the previous one as baseprofile.
|
||||
|
||||
* `-u`, `--unencrypted`:
|
||||
Pack an unencrypted image rather than encrypting.
|
||||
|
||||
* <scratchdir>:
|
||||
Directory containing diskless root.
|
||||
|
||||
* <profilename>:
|
||||
The desired diskless OS profile name to pack the root into.
|
||||
|
||||
## CAPTURE OPTIONS
|
||||
|
||||
* <node>:
|
||||
Node to capture image from.
|
||||
|
||||
* <profilename>:
|
||||
Profile name for captured image.
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
Build a diskless image from a distribution:
|
||||
|
||||
imgutil build -s alma-9.6-x86_64 /tmp/myimage
|
||||
|
||||
Execute a shell in an unpacked image:
|
||||
|
||||
imgutil exec /tmp/myimage
|
||||
|
||||
Execute a specific command in an image:
|
||||
|
||||
imgutil exec /tmp/myimage /bin/rpm -qa
|
||||
|
||||
Unpack an existing profile for modification:
|
||||
|
||||
imgutil unpack myprofile /tmp/myimage
|
||||
|
||||
Pack a modified image into a new profile:
|
||||
|
||||
imgutil pack /tmp/myimage myprofile-v2
|
||||
|
||||
Capture an image from a running node:
|
||||
|
||||
imgutil capture node01 production-image
|
||||
|
||||
## FILES
|
||||
|
||||
* `/var/lib/confluent/public/os/`:
|
||||
Default location for OS profiles.
|
||||
|
||||
* `/var/lib/confluent/private/os/`:
|
||||
Location for encrypted image keys and private data.
|
||||
|
||||
* `/var/lib/confluent/distributions/`:
|
||||
Default location for installation sources.
|
||||
|
||||
## SEE ALSO
|
||||
|
||||
osdeploy(8)
|
||||
|
||||
## AUTHOR
|
||||
|
||||
Written for the Confluent project.
|
||||
@@ -19,7 +19,9 @@ interval of 1 second is used.
|
||||
## OPTIONS
|
||||
|
||||
* `-c`, `--csv`:
|
||||
Organize output into CSV format, one sensor per column.
|
||||
Organize output into CSV format, one sensor per column. Note that while normally nodesensors reports
|
||||
sensors in order as returned by server, CSV output enforces consistency by sorting after receiving
|
||||
the results, which may have a different ordering than non-CSV usage of nodesensors.
|
||||
|
||||
* `-i`, `--interval`=**SECONDS**:
|
||||
Repeat data gathering waiting, waiting the specified time between samples. Unless `-n` is
|
||||
|
||||
@@ -3,6 +3,7 @@ try:
|
||||
import http.client as client
|
||||
except ImportError:
|
||||
import httplib as client
|
||||
import base64
|
||||
import ctypes
|
||||
import ctypes.util
|
||||
import glob
|
||||
@@ -15,7 +16,13 @@ import sys
|
||||
import struct
|
||||
import time
|
||||
import re
|
||||
import json
|
||||
import hashlib
|
||||
try:
|
||||
import json
|
||||
import hmac
|
||||
except ImportError:
|
||||
json = None
|
||||
hmac = None
|
||||
|
||||
class InvalidApiKey(Exception):
|
||||
pass
|
||||
@@ -73,7 +80,7 @@ def get_my_addresses():
|
||||
return addrs
|
||||
|
||||
|
||||
def scan_confluents():
|
||||
def scan_confluents(confuuid=None):
|
||||
srvs = {}
|
||||
s6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
||||
s6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
|
||||
@@ -85,12 +92,13 @@ def scan_confluents():
|
||||
s4.bind(('0.0.0.0', 1900))
|
||||
doneidxs = set([])
|
||||
msg = 'M-SEARCH * HTTP/1.1\r\nST: urn:xcat.org:service:confluent:'
|
||||
with open('/etc/confluent/confluent.deploycfg') as dcfg:
|
||||
for line in dcfg.read().split('\n'):
|
||||
if line.startswith('confluent_uuid:'):
|
||||
confluentuuid = line.split(': ')[1]
|
||||
msg += '/confluentuuid=' + confluentuuid
|
||||
break
|
||||
if not confuuid:
|
||||
with open('/etc/confluent/confluent.deploycfg') as dcfg:
|
||||
for line in dcfg.read().split('\n'):
|
||||
if line.startswith('confluent_uuid:'):
|
||||
confluentuuid = line.split(': ')[1]
|
||||
msg += '/confluentuuid=' + confluentuuid
|
||||
break
|
||||
try:
|
||||
with open('/sys/devices/virtual/dmi/id/product_uuid') as uuidin:
|
||||
msg += '/uuid=' + uuidin.read().strip()
|
||||
@@ -127,6 +135,7 @@ def scan_confluents():
|
||||
srvlist = []
|
||||
if r:
|
||||
r = r[0]
|
||||
nodename = None
|
||||
while r:
|
||||
for s in r:
|
||||
(rsp, peer) = s.recvfrom(9000)
|
||||
@@ -134,6 +143,7 @@ def scan_confluents():
|
||||
current = None
|
||||
for line in rsp:
|
||||
if line.startswith(b'NODENAME: '):
|
||||
nodename = line.replace(b'NODENAME: ', b'').strip().decode('utf8')
|
||||
current = {}
|
||||
elif line.startswith(b'DEFAULTNET: 1'):
|
||||
current['isdefault'] = True
|
||||
@@ -149,16 +159,32 @@ def scan_confluents():
|
||||
r = select.select((s4, s6), (), (), 2)
|
||||
if r:
|
||||
r = r[0]
|
||||
if not os.path.exists('/etc/confluent/confluent.info'):
|
||||
with open('/etc/confluent/confluent.info', 'w+') as cinfo:
|
||||
if nodename:
|
||||
cinfo.write('NODENAME: {0}\n'.format(nodename))
|
||||
for srv in srvlist:
|
||||
cinfo.write('MANAGER: {0}\n'.format(srv))
|
||||
return srvlist, srvs
|
||||
|
||||
|
||||
def get_net_apikey(nodename, mgr):
|
||||
def get_net_apikey(nodename, mgr, hmackey=None, confuuid=None):
|
||||
alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./'
|
||||
newpass = ''.join([alpha[x >> 2] for x in bytearray(os.urandom(32))])
|
||||
salt = '$5$' + ''.join([alpha[x >> 2] for x in bytearray(os.urandom(8))])
|
||||
newpass = newpass.encode('utf8')
|
||||
salt = salt.encode('utf8')
|
||||
crypted = c_crypt(newpass, salt)
|
||||
if hmackey:
|
||||
hmacvalue = hmac.new(hmackey.encode('utf8'), crypted, hashlib.sha256).digest()
|
||||
hmacvalue = base64.b64encode(hmacvalue).decode('utf8')
|
||||
client = HTTPSClient(host=mgr, phmac=hmacvalue, nodename=nodename, confuuid=confuuid)
|
||||
try:
|
||||
status, rsp = client.grab_url_with_status('/confluent-api/self/registerapikey', data=crypted, returnrsp=True)
|
||||
if status == 200:
|
||||
return newpass.decode('utf8')
|
||||
except Exception:
|
||||
pass
|
||||
for addrinfo in socket.getaddrinfo(mgr, 13001, 0, socket.SOCK_STREAM):
|
||||
try:
|
||||
clisock = socket.socket(addrinfo[0], addrinfo[1])
|
||||
@@ -196,7 +222,7 @@ def get_net_apikey(nodename, mgr):
|
||||
return ''
|
||||
|
||||
|
||||
def get_apikey(nodename, hosts, errout=None):
|
||||
def get_apikey(nodename, hosts, errout=None, hmackey=None, confuuid=None):
|
||||
apikey = ""
|
||||
if os.path.exists('/etc/confluent/confluent.apikey'):
|
||||
apikey = open('/etc/confluent/confluent.apikey').read().strip()
|
||||
@@ -205,16 +231,16 @@ def get_apikey(nodename, hosts, errout=None):
|
||||
while not apikey:
|
||||
for host in hosts:
|
||||
try:
|
||||
apikey = get_net_apikey(nodename, host)
|
||||
apikey = get_net_apikey(nodename, host, hmackey=hmackey, confuuid=confuuid)
|
||||
except OSError:
|
||||
apikey = None
|
||||
if apikey:
|
||||
break
|
||||
else:
|
||||
srvlist, _ = scan_confluents()
|
||||
srvlist, _ = scan_confluents(confuuid=confuuid)
|
||||
for host in srvlist:
|
||||
try:
|
||||
apikey = get_net_apikey(nodename, host)
|
||||
apikey = get_net_apikey(nodename, host, hmackey=hmackey, confuuid=confuuid)
|
||||
except OSError:
|
||||
apikey = None
|
||||
if apikey:
|
||||
@@ -232,35 +258,43 @@ def get_apikey(nodename, hosts, errout=None):
|
||||
return apikey
|
||||
|
||||
class HTTPSClient(client.HTTPConnection, object):
|
||||
def __init__(self, usejson=False, port=443, host=None, errout=None, phmac=None, checkonly=False):
|
||||
def __init__(self, usejson=False, port=443, host=None, errout=None, phmac=None, checkonly=False, hmackey=None, nodename=None, confuuid=None):
|
||||
self.ignorehosts = set([])
|
||||
self.phmac = phmac
|
||||
self.hmackey = hmackey
|
||||
self.confuuid = confuuid
|
||||
self.errout = None
|
||||
self.stdheaders = {}
|
||||
if nodename:
|
||||
self.stdheaders['CONFLUENT_NODENAME'] = nodename
|
||||
if errout:
|
||||
self.errout = open(errout, 'w')
|
||||
self.errout.flush()
|
||||
self.stdheaders = {}
|
||||
mgtiface = None
|
||||
if usejson:
|
||||
self.stdheaders['ACCEPT'] = 'application/json'
|
||||
if host:
|
||||
self.hosts = [host]
|
||||
with open('/etc/confluent/confluent.info') as cinfo:
|
||||
info = cinfo.read().split('\n')
|
||||
for line in info:
|
||||
if line.startswith('NODENAME:'):
|
||||
node = line.split(' ')[1]
|
||||
self.stdheaders['CONFLUENT_NODENAME'] = node
|
||||
if not nodename:
|
||||
with open('/etc/confluent/confluent.info') as cinfo:
|
||||
info = cinfo.read().split('\n')
|
||||
for line in info:
|
||||
if line.startswith('NODENAME:'):
|
||||
nodename = line.split(' ')[1]
|
||||
self.stdheaders['CONFLUENT_NODENAME'] = nodename
|
||||
else:
|
||||
self.hosts = []
|
||||
info = open('/etc/confluent/confluent.info').read().split('\n')
|
||||
try:
|
||||
info = open('/etc/confluent/confluent.info').read().split('\n')
|
||||
except Exception:
|
||||
info = []
|
||||
havedefault = '0'
|
||||
plainhost = ''
|
||||
for line in info:
|
||||
host = ''
|
||||
if line.startswith('NODENAME:'):
|
||||
node = line.split(' ')[1]
|
||||
self.stdheaders['CONFLUENT_NODENAME'] = node
|
||||
nodename = line.split(' ')[1]
|
||||
self.stdheaders['CONFLUENT_NODENAME'] = nodename
|
||||
if line.startswith('MANAGER:') and not host:
|
||||
host = line.split(' ')[1]
|
||||
self.hosts.append(host)
|
||||
@@ -295,15 +329,14 @@ class HTTPSClient(client.HTTPConnection, object):
|
||||
if plainhost and not self.hosts:
|
||||
self.hosts.append(plainhost)
|
||||
if self.phmac:
|
||||
with open(phmac, 'r') as hmacin:
|
||||
self.stdheaders['CONFLUENT_CRYPTHMAC'] = hmacin.read()
|
||||
self.stdheaders['CONFLUENT_CRYPTHMAC'] = self.phmac
|
||||
elif not checkonly:
|
||||
self.stdheaders['CONFLUENT_APIKEY'] = get_apikey(node, self.hosts, errout=self.errout)
|
||||
self.stdheaders['CONFLUENT_APIKEY'] = get_apikey(nodename, self.hosts, errout=self.errout, hmackey=hmackey, confuuid=self.confuuid)
|
||||
if mgtiface:
|
||||
self.stdheaders['CONFLUENT_MGTIFACE'] = mgtiface
|
||||
self.port = port
|
||||
self.host = None
|
||||
self.node = node
|
||||
self.node = nodename
|
||||
host = self.check_connections()
|
||||
client.HTTPConnection.__init__(self, host, port)
|
||||
self.connect()
|
||||
@@ -343,7 +376,7 @@ class HTTPSClient(client.HTTPConnection, object):
|
||||
continue
|
||||
break
|
||||
if not foundsrv:
|
||||
srvlist, srvs = scan_confluents()
|
||||
srvlist, srvs = scan_confluents(self.confuuid)
|
||||
hosts = []
|
||||
for srv in srvlist:
|
||||
if srvs[srv].get('isdefault', False):
|
||||
@@ -417,7 +450,7 @@ class HTTPSClient(client.HTTPConnection, object):
|
||||
with open('/etc/confluent/confluent.apikey', 'w+') as akfile:
|
||||
akfile.write('')
|
||||
self.stdheaders['CONFLUENT_APIKEY'] = get_apikey(
|
||||
self.node, [self.host], errout=self.errout)
|
||||
self.node, [self.host], errout=self.errout, hmackey=self.hmackey, confuuid=self.confuuid)
|
||||
if rsp.status == 503: # confluent is down, but the server running confluent is otherwise up
|
||||
authed = False
|
||||
self.ignorehosts.add(self.host)
|
||||
@@ -464,7 +497,7 @@ def get_available_nics():
|
||||
parts = re.split(r'\s{2,}', line.strip())
|
||||
if len(parts) >= 5:
|
||||
nic_name = parts[0]
|
||||
nic_status = parts[4] # "Link Status" este al 5-lea câmp
|
||||
nic_status = parts[4] # "Link Status" is the 5th field
|
||||
available_nics[nic_name] = nic_status
|
||||
|
||||
return available_nics
|
||||
@@ -509,7 +542,7 @@ if __name__ == '__main__':
|
||||
try:
|
||||
fix_vswitch()
|
||||
except Exception as e:
|
||||
print(f"fix_vswitch() error: {e}")
|
||||
print("fix_vswitch() error: {}".format(e))
|
||||
sys.argv.remove('-f')
|
||||
sys.exit(0)
|
||||
usejson = False
|
||||
@@ -546,8 +579,24 @@ if __name__ == '__main__':
|
||||
phmac = sys.argv.index('-p')
|
||||
sys.argv.pop(phmac)
|
||||
phmac = sys.argv.pop(phmac)
|
||||
with open(phmac, 'r') as hmacin:
|
||||
phmac = hmacin.read()
|
||||
except ValueError:
|
||||
phmac = None
|
||||
try:
|
||||
identfile = sys.argv.index('-i')
|
||||
sys.argv.pop(identfile)
|
||||
identfile = sys.argv.pop(identfile)
|
||||
with open(identfile) as idin:
|
||||
data = idin.read()
|
||||
identinfo = json.loads(data)
|
||||
nodename = identinfo.get('nodename', None)
|
||||
hmackey = identinfo.get('apitoken', None)
|
||||
confuuid = identinfo.get('confluent_uuid', None)
|
||||
except ValueError:
|
||||
hmackey = None
|
||||
nodename = None
|
||||
confuuid = None
|
||||
try:
|
||||
checkonly = False
|
||||
idxit = sys.argv.index('-c')
|
||||
@@ -559,7 +608,7 @@ if __name__ == '__main__':
|
||||
data = open(sys.argv[-1]).read()
|
||||
if outbin:
|
||||
with open(outbin, 'ab+') as outf:
|
||||
reader = HTTPSClient(usejson=usejson, errout=errout).grab_url(
|
||||
reader = HTTPSClient(usejson=usejson, errout=errout, hmackey=hmackey, nodename=nodename, confuuid=confuuid).grab_url(
|
||||
sys.argv[1], data, returnrsp=True)
|
||||
chunk = reader.read(16384)
|
||||
while chunk:
|
||||
@@ -567,7 +616,7 @@ if __name__ == '__main__':
|
||||
chunk = reader.read(16384)
|
||||
sys.exit(0)
|
||||
|
||||
mclient = HTTPSClient(usejson, errout=errout, phmac=phmac, checkonly=checkonly)
|
||||
mclient = HTTPSClient(usejson, errout=errout, phmac=phmac, checkonly=checkonly, hmackey=hmackey, nodename=nodename, confuuid=confuuid)
|
||||
if waitfor:
|
||||
status = 201
|
||||
while status != waitfor:
|
||||
|
||||
@@ -164,6 +164,9 @@ class NetplanManager(object):
|
||||
if curraddr not in currips:
|
||||
needcfgwrite = True
|
||||
currips.append(curraddr)
|
||||
if stgs.get('mtu', None):
|
||||
devdict = self.getcfgarrpath([devname])
|
||||
devdict['mtu'] = int(stgs['mtu'])
|
||||
gws = []
|
||||
gws.append(stgs.get('ipv4_gateway', None))
|
||||
gws.append(stgs.get('ipv6_gateway', None))
|
||||
@@ -381,6 +384,8 @@ class NetworkManager(object):
|
||||
cmdargs['ipv4.gateway'] = stgs['ipv4_gateway']
|
||||
if stgs.get('ipv6_gateway', None):
|
||||
cmdargs['ipv6.gateway'] = stgs['ipv6_gateway']
|
||||
if stgs.get('mtu', None):
|
||||
cmdargs['802-3-ethernet.mtu'] = stgs['mtu']
|
||||
dnsips = self.deploycfg.get('nameservers', [])
|
||||
if not dnsips:
|
||||
dnsips = []
|
||||
@@ -406,7 +411,7 @@ class NetworkManager(object):
|
||||
cargs = []
|
||||
for arg in cmdargs:
|
||||
cargs.append(arg)
|
||||
cargs.append(cmdargs[arg])
|
||||
cargs.append('{}'.format(cmdargs[arg]))
|
||||
if stgs['team_mode'] in self.bondtypes:
|
||||
stgs['team_mode'] = self.bondtypes[stgs['team_mode']]
|
||||
subprocess.check_call(['nmcli', 'c', 'add', 'type', 'bond', 'con-name', cname, 'connection.interface-name', cname, 'bond.options', 'miimon=100,mode={}'.format(stgs['team_mode'])] + cargs)
|
||||
@@ -441,7 +446,7 @@ class NetworkManager(object):
|
||||
cargs = []
|
||||
for arg in cmdargs:
|
||||
cargs.append(arg)
|
||||
cargs.append(cmdargs[arg])
|
||||
cargs.append('{}'.format(cmdargs[arg]))
|
||||
if u:
|
||||
subprocess.check_call(['nmcli', 'c', 'm', u, 'connection.interface-name', iname] + cargs)
|
||||
subprocess.check_call(['nmcli', 'c', 'u', u])
|
||||
@@ -455,6 +460,9 @@ class NetworkManager(object):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
checktarg = None
|
||||
if '-c' in sys.argv:
|
||||
checktarg = sys.argv[sys.argv.index('-c') + 1]
|
||||
havefirewall = subprocess.call(['systemctl', 'status', 'firewalld'])
|
||||
havefirewall = havefirewall == 0
|
||||
if havefirewall:
|
||||
@@ -540,7 +548,7 @@ if __name__ == '__main__':
|
||||
rm_tmp_llas(tmpllas)
|
||||
if os.path.exists('/usr/sbin/netplan'):
|
||||
nm = NetplanManager(dc)
|
||||
if os.path.exists('/usr/bin/nmcli'):
|
||||
elif os.path.exists('/usr/bin/nmcli'):
|
||||
nm = NetworkManager(devtypes, dc)
|
||||
elif os.path.exists('/usr/sbin/wicked'):
|
||||
nm = WickedManager()
|
||||
@@ -562,4 +570,27 @@ if __name__ == '__main__':
|
||||
if havefirewall:
|
||||
subprocess.check_call(['systemctl', 'start', 'firewalld'])
|
||||
await_tentative()
|
||||
maxwait = 10
|
||||
while maxwait:
|
||||
try:
|
||||
tclient = apiclient.HTTPSClient(checkonly=True)
|
||||
tclient.check_connections()
|
||||
break
|
||||
except Exception:
|
||||
maxwait -= 1
|
||||
time.sleep(1)
|
||||
maxwait = 10
|
||||
if checktarg:
|
||||
while maxwait:
|
||||
try:
|
||||
addrinf = socket.getaddrinfo(checktarg, 443)[0]
|
||||
psock = socket.socket(addrinf[0], socket.SOCK_STREAM)
|
||||
psock.settimeout(10)
|
||||
psock.connect(addrinf[4])
|
||||
psock.close()
|
||||
break
|
||||
except Exception:
|
||||
maxwait -= 1
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ for pubkey in /etc/ssh/ssh_host*key.pub; do
|
||||
continue
|
||||
fi
|
||||
certfile=${pubkey/.pub/-cert.pub}
|
||||
rm $certfile
|
||||
echo -n > $certfile
|
||||
confluentpython $confapiclient /confluent-api/self/sshcert $pubkey -o $certfile
|
||||
done
|
||||
if [ -d /etc/ssh/sshd_config.d/ -a ! -e /etc/ssh/sshd_config.d/90-confluent.conf ]; then
|
||||
@@ -17,6 +17,13 @@ if [ -d /etc/ssh/sshd_config.d/ -a ! -e /etc/ssh/sshd_config.d/90-confluent.conf
|
||||
echo HostbasedAuthentication yes >> /etc/ssh/sshd_config.d/90-confluent.conf
|
||||
echo HostbasedUsesNameFromPacketOnly yes >> /etc/ssh/sshd_config.d/90-confluent.conf
|
||||
echo IgnoreRhosts no >> /etc/ssh/sshd_config.d/90-confluent.conf
|
||||
elif [ ! -d /etc/ssh/sshd_config.d/ ] && ! grep HostCertificate /etc/ssh/sshd_config > /dev/null; then
|
||||
for cert in /etc/ssh/ssh*-cert.pub; do
|
||||
echo HostCertificate $cert >> /etc/ssh/sshd_config
|
||||
done
|
||||
echo HostbasedAuthentication yes >> /etc/ssh/sshd_config
|
||||
echo HostbasedUsesNameFromPacketOnly yes >> /etc/ssh/sshd_config
|
||||
echo IgnoreRhosts no >> /etc/ssh/sshd_config
|
||||
fi
|
||||
|
||||
TMPDIR=$(mktemp -d)
|
||||
@@ -25,13 +32,20 @@ confluentpython $confapiclient /confluent-public/site/initramfs.tgz -o initramfs
|
||||
tar xf initramfs.tgz
|
||||
for ca in ssh/*.ca; do
|
||||
LINE=$(cat $ca)
|
||||
cp -af /etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts.new
|
||||
grep -v "$LINE" /etc/ssh/ssh_known_hosts > /etc/ssh/ssh_known_hosts.new
|
||||
if [ -z "$LINE" ]; then continue; fi
|
||||
if [ -f /etc/ssh/ssh_known_hosts ]; then
|
||||
cp -af /etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts.new
|
||||
grep -v "$LINE" /etc/ssh/ssh_known_hosts > /etc/ssh/ssh_known_hosts.new
|
||||
fi
|
||||
echo '@cert-authority *' $LINE >> /etc/ssh/ssh_known_hosts.new
|
||||
mv /etc/ssh/ssh_known_hosts.new /etc/ssh/ssh_known_hosts
|
||||
done
|
||||
mkdir -p /root/.ssh/
|
||||
chmod 700 /root/.ssh/
|
||||
touch /root/.ssh/authorized_keys
|
||||
for pubkey in ssh/*.*pubkey; do
|
||||
LINE=$(cat $pubkey)
|
||||
if [ -z "$LINE" ]; then continue; fi
|
||||
cp -af /root/.ssh/authorized_keys /root/.ssh/authorized_keys.new
|
||||
grep -v "$LINE" /root/.ssh/authorized_keys > /root/.ssh/authorized_keys.new
|
||||
echo "$LINE" >> /root/.ssh/authorized_keys.new
|
||||
@@ -41,3 +55,4 @@ confluentpython $confapiclient /confluent-api/self/nodelist | sed -e 's/^- //' >
|
||||
cat /etc/ssh/shosts.equiv > /root/.shosts
|
||||
cd -
|
||||
rm -rf $TMPDIR
|
||||
systemctl try-restart sshd
|
||||
|
||||
@@ -27,7 +27,12 @@ mkdir -p stateless-bin
|
||||
cp -a el8bin/* .
|
||||
ln -s el8 el9
|
||||
ln -s el8 el10
|
||||
for os in rhvh4 el7 genesis el8 suse15 ubuntu20.04 ubuntu22.04 ubuntu24.04 coreos el9 el10; do
|
||||
mv el10/initramfs/usr el10/initramfs/var
|
||||
cp -a debian debian13
|
||||
mkdir -p debian13/initramfs/usr
|
||||
mv debian13/initramfs/lib debian13/initramfs/usr/
|
||||
mv el10/initramfs/usr el10/initramfs/var
|
||||
for os in rhvh4 el7 genesis el8 suse15 debian debian13 ubuntu20.04 ubuntu22.04 ubuntu24.04 coreos el9 el10; do
|
||||
mkdir ${os}out
|
||||
cd ${os}out
|
||||
if [ -d ../${os}bin ]; then
|
||||
@@ -81,7 +86,7 @@ cp -a esxi7 esxi8
|
||||
%install
|
||||
mkdir -p %{buildroot}/opt/confluent/share/licenses/confluent_osdeploy/
|
||||
#cp LICENSE %{buildroot}/opt/confluent/share/licenses/confluent_osdeploy/
|
||||
for os in rhvh4 el7 el8 el9 el10 genesis suse15 ubuntu20.04 ubuntu22.04 ubuntu24.04 esxi6 esxi7 esxi8 coreos; do
|
||||
for os in rhvh4 el7 el8 el9 el10 debian debian13 genesis suse15 ubuntu20.04 ubuntu22.04 ubuntu24.04 esxi6 esxi7 esxi8 coreos; do
|
||||
mkdir -p %{buildroot}/opt/confluent/lib/osdeploy/$os/initramfs/aarch64/
|
||||
cp ${os}out/addons.* %{buildroot}/opt/confluent/lib/osdeploy/$os/initramfs/aarch64/
|
||||
if [ -d ${os}disklessout ]; then
|
||||
|
||||
@@ -29,8 +29,11 @@ This contains support utilities for enabling deployment of x86_64 architecture s
|
||||
#cd ..
|
||||
ln -s el8 el9
|
||||
cp -a el8 el10
|
||||
cp -a debian debian13
|
||||
mkdir -p debian13/initramfs/usr
|
||||
mv debian13/initramfs/lib debian13/initramfs/usr/
|
||||
mv el10/initramfs/usr el10/initramfs/var
|
||||
for os in rhvh4 el7 genesis el8 suse15 debian ubuntu18.04 ubuntu20.04 ubuntu22.04 ubuntu24.04 coreos el9 el10; do
|
||||
for os in rhvh4 el7 genesis el8 suse15 debian debian13 ubuntu18.04 ubuntu20.04 ubuntu22.04 ubuntu24.04 coreos el9 el10; do
|
||||
mkdir ${os}out
|
||||
cd ${os}out
|
||||
if [ -d ../${os}bin ]; then
|
||||
@@ -86,7 +89,7 @@ cp -a esxi7 esxi9
|
||||
%install
|
||||
mkdir -p %{buildroot}/opt/confluent/share/licenses/confluent_osdeploy/
|
||||
cp LICENSE %{buildroot}/opt/confluent/share/licenses/confluent_osdeploy/
|
||||
for os in rhvh4 el7 el8 el9 el10 genesis suse15 ubuntu20.04 debian ubuntu18.04 ubuntu22.04 ubuntu24.04 esxi6 esxi7 esxi8 esxi9 coreos; do
|
||||
for os in rhvh4 el7 el8 el9 el10 genesis suse15 ubuntu20.04 debian debian13 ubuntu18.04 ubuntu22.04 ubuntu24.04 esxi6 esxi7 esxi8 esxi9 coreos; do
|
||||
mkdir -p %{buildroot}/opt/confluent/lib/osdeploy/$os/initramfs
|
||||
mkdir -p %{buildroot}/opt/confluent/lib/osdeploy/$os/profiles
|
||||
cp ${os}out/addons.* %{buildroot}/opt/confluent/lib/osdeploy/$os/initramfs
|
||||
|
||||
@@ -164,6 +164,9 @@ class NetplanManager(object):
|
||||
if curraddr not in currips:
|
||||
needcfgwrite = True
|
||||
currips.append(curraddr)
|
||||
if stgs.get('mtu', None):
|
||||
devdict = self.getcfgarrpath([devname])
|
||||
devdict['mtu'] = int(stgs['mtu'])
|
||||
gws = []
|
||||
gws.append(stgs.get('ipv4_gateway', None))
|
||||
gws.append(stgs.get('ipv6_gateway', None))
|
||||
@@ -381,6 +384,8 @@ class NetworkManager(object):
|
||||
cmdargs['ipv4.gateway'] = stgs['ipv4_gateway']
|
||||
if stgs.get('ipv6_gateway', None):
|
||||
cmdargs['ipv6.gateway'] = stgs['ipv6_gateway']
|
||||
if stgs.get('mtu', None):
|
||||
cmdargs['802-3-ethernet.mtu'] = stgs['mtu']
|
||||
dnsips = self.deploycfg.get('nameservers', [])
|
||||
if not dnsips:
|
||||
dnsips = []
|
||||
@@ -406,10 +411,10 @@ class NetworkManager(object):
|
||||
cargs = []
|
||||
for arg in cmdargs:
|
||||
cargs.append(arg)
|
||||
cargs.append(cmdargs[arg])
|
||||
cargs.append('{}'.format(cmdargs[arg]))
|
||||
if stgs['team_mode'] in self.bondtypes:
|
||||
stgs['team_mode'] = self.bondtypes[stgs['team_mode']]
|
||||
subprocess.check_call(['nmcli', 'c', 'add', 'type', 'bond', 'con-name', cname, 'connection.interface-name', cname, 'bond.options', 'mode={}'.format(stgs['team_mode'])] + cargs)
|
||||
subprocess.check_call(['nmcli', 'c', 'add', 'type', 'bond', 'con-name', cname, 'connection.interface-name', cname, 'bond.options', 'miimon=100,mode={}'.format(stgs['team_mode'])] + cargs)
|
||||
for iface in cfg['interfaces']:
|
||||
self.add_team_member(cname, iface)
|
||||
subprocess.check_call(['nmcli', 'c', 'u', cname])
|
||||
@@ -441,7 +446,7 @@ class NetworkManager(object):
|
||||
cargs = []
|
||||
for arg in cmdargs:
|
||||
cargs.append(arg)
|
||||
cargs.append(cmdargs[arg])
|
||||
cargs.append('{}'.format(cmdargs[arg]))
|
||||
if u:
|
||||
subprocess.check_call(['nmcli', 'c', 'm', u, 'connection.interface-name', iname] + cargs)
|
||||
subprocess.check_call(['nmcli', 'c', 'u', u])
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -2,12 +2,18 @@
|
||||
# This script would run in post.d
|
||||
#
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
echo "deb [arch=amd64] http://download.proxmox.com/debian/pve bookworm pve-no-subscription" > /etc/apt/sources.list.d/pve-install-repo.list
|
||||
wget https://enterprise.proxmox.com/debian/proxmox-release-bookworm.gpg -O /etc/apt/trusted.gpg.d/proxmox-release-bookworm.gpg
|
||||
sum=$(sha512sum /etc/apt/trusted.gpg.d/proxmox-release-bookworm.gpg)
|
||||
if [ "$sum" -ne "7da6fe34168adc6e479327ba517796d4702fa2f8b4f0a9833f5ea6e6b48f6507a6da403a274fe201595edc86a84463d50383d07f64bdde2e3658108db7d6dc87" ]; then
|
||||
codename=$(grep ^VERSION_CODENAME /etc/os-release | cut -d= -f2)
|
||||
echo "deb [arch=amd64] http://download.proxmox.com/debian/pve $codename pve-no-subscription" > /etc/apt/sources.list.d/pve-install-repo.list
|
||||
wget https://enterprise.proxmox.com/debian/proxmox-release-$codename.gpg -O /etc/apt/trusted.gpg.d/proxmox-release-$codename.gpg
|
||||
sum=$(sha512sum /etc/apt/trusted.gpg.d/proxmox-release-$codename.gpg)
|
||||
if [ $codename == "bookworm" ]; then
|
||||
expectedsum=7da6fe34168adc6e479327ba517796d4702fa2f8b4f0a9833f5ea6e6b48f6507a6da403a274fe201595edc86a84463d50383d07f64bdde2e3658108db7d6dc87
|
||||
elif [ $codename == "trixie" ]; then
|
||||
expectedsum=8678f2327c49276615288d7ca11e7d296bc8a2b96946fe565a9c81e533f9b15a5dbbad210a0ad5cd46d361ff1d3c4bac55844bc296beefa4f88b86e44e69fa51
|
||||
fi
|
||||
if [ "$sum" -ne "$expectedsum" ]; then
|
||||
echo "Mismatch in fingerprint!"
|
||||
rm /etc/apt/trusted.gpg.d/proxmox-release-bookworm.gpg
|
||||
rm /etc/apt/trusted.gpg.d/proxmox-release-$codename.gpg
|
||||
exit 1
|
||||
fi
|
||||
apt-get update && apt-get -y full-upgrade < /dev/null
|
||||
|
||||
@@ -7,7 +7,6 @@ for pubkey in /etc/ssh/ssh_host*key.pub; do
|
||||
continue
|
||||
fi
|
||||
certfile=${pubkey/.pub/-cert.pub}
|
||||
rm $certfile
|
||||
confluentpython $confapiclient /confluent-api/self/sshcert $pubkey -o $certfile
|
||||
done
|
||||
if [ -d /etc/ssh/sshd_config.d/ -a ! -e /etc/ssh/sshd_config.d/90-confluent.conf ]; then
|
||||
@@ -25,6 +24,7 @@ confluentpython $confapiclient /confluent-public/site/initramfs.tgz -o initramfs
|
||||
tar xf initramfs.tgz
|
||||
for ca in ssh/*.ca; do
|
||||
LINE=$(cat $ca)
|
||||
if [ -z "$LINE" ]; then continue; fi
|
||||
cp -af /etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts.new
|
||||
grep -v "$LINE" /etc/ssh/ssh_known_hosts > /etc/ssh/ssh_known_hosts.new
|
||||
echo '@cert-authority *' $LINE >> /etc/ssh/ssh_known_hosts.new
|
||||
@@ -32,6 +32,7 @@ for ca in ssh/*.ca; do
|
||||
done
|
||||
for pubkey in ssh/*.*pubkey; do
|
||||
LINE=$(cat $pubkey)
|
||||
if [ -z "$LINE" ]; then continue; fi
|
||||
cp -af /root/.ssh/authorized_keys /root/.ssh/authorized_keys.new
|
||||
grep -v "$LINE" /root/.ssh/authorized_keys > /root/.ssh/authorized_keys.new
|
||||
echo "$LINE" >> /root/.ssh/authorized_keys.new
|
||||
@@ -41,3 +42,4 @@ confluentpython $confapiclient /confluent-api/self/nodelist | sed -e 's/^- //' >
|
||||
cat /etc/ssh/shosts.equiv > /root/.shosts
|
||||
cd -
|
||||
rm -rf $TMPDIR
|
||||
systemctl try-restart sshd
|
||||
|
||||
@@ -129,4 +129,10 @@ ln -s /sysroot/lib/modules/$(uname -r) /lib/modules/
|
||||
mv /lib/firmware /lib/firmware-ramfs
|
||||
ln -s /sysroot/lib/firmware /lib/firmware
|
||||
kill $(grep -l ^/usr/lib/systemd/systemd-udevd /proc/*/cmdline|cut -d/ -f 3)
|
||||
exec /opt/confluent/bin/start_root
|
||||
if grep debugssh /proc/cmdline >& /dev/null; then
|
||||
exec /opt/confluent/bin/start_root
|
||||
else
|
||||
rm -rf /lib/modules/$(uname -r) /lib/modules/$(uname -r)-ramfs /lib/firmware-ramfs /usr/lib64/libcrypto.so* /usr/lib64/systemd/ /kernel/ /usr/bin/ /usr/sbin/ /usr/libexec/
|
||||
exec /opt/confluent/bin/start_root -s # share mount namespace, keep kernel callbacks intact
|
||||
fi
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
+5
-1
@@ -107,7 +107,11 @@ if [ ! -z "$confluentsrv" ]; then
|
||||
/usr/libexec/nm-initrd-generator ip=:dhcp6
|
||||
else
|
||||
confluenthttpsrv=$confluentsrv
|
||||
ifname=$(ip -br link|grep LOWER_UP|grep -v UNKNOWN|head -n 1|awk '{print $1}')
|
||||
ifname=""
|
||||
while [ -z "$ifname" ]; do
|
||||
ifname=$(ip -br link|grep LOWER_UP|grep -v ib|grep -v UNKNOWN|head -n 1|awk '{print $1}')
|
||||
sleep 0.5
|
||||
done
|
||||
echo -n "Attempting to use dhcp to bring up $ifname..."
|
||||
dhclient $ifname
|
||||
while ! ip -br addr show dev $ifname | grep \\. > /dev/null; do
|
||||
|
||||
@@ -25,7 +25,8 @@ if [ ! -f /etc/confluent/firstboot.ran ]; then
|
||||
touch /etc/confluent/firstboot.ran
|
||||
|
||||
cat /etc/confluent/tls/*.pem >> /etc/pki/tls/certs/ca-bundle.crt
|
||||
|
||||
confluentpython /root/confignet
|
||||
rm /root/confignet
|
||||
run_remote firstboot.custom
|
||||
# Firstboot scripts may be placed into firstboot.d, e.g. firstboot.d/01-firstaction.sh, firstboot.d/02-secondaction.sh
|
||||
run_remote_parts firstboot.d
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
@@ -56,7 +56,11 @@ def get_image_metadata(imgpath):
|
||||
for md in get_multipart_image_meta(img):
|
||||
yield md
|
||||
else:
|
||||
raise Exception('Installation from single part image not supported')
|
||||
yield {'mount': '/', 'filesystem': 'xfs', 'minsize': 4294967296, 'initsize': 954128662528, 'flags': 'rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota', 'device': '/dev/mapper/root', 'compressed_size': 27022069760}
|
||||
yield {'mount': '/boot', 'filesystem': 'xfs', 'minsize': 536870912, 'initsize': 1006632960, 'flags': 'rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota', 'device': '/dev/nvme1n1p2', 'compressed_size': 171462656}
|
||||
yield {'mount': '/boot/efi', 'filesystem': 'vfat', 'minsize': 33554432, 'initsize': 627900416, 'flags': 'rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=ascii,shortname=winnt,errors=remount-ro', 'device': '/dev/nvme1n1p1', 'compressed_size': 1576960}
|
||||
#raise Exception('Installation from single part image not supported')
|
||||
|
||||
|
||||
class PartedRunner():
|
||||
def __init__(self, disk):
|
||||
@@ -75,8 +79,17 @@ def fixup(rootdir, vols):
|
||||
for vol in vols:
|
||||
devbymount[vol['mount']] = vol['targetdisk']
|
||||
fstabfile = os.path.join(rootdir, 'etc/fstab')
|
||||
with open(fstabfile) as tfile:
|
||||
fstab = tfile.read().split('\n')
|
||||
if os.path.exists(fstabfile):
|
||||
with open(fstabfile) as tfile:
|
||||
fstab = tfile.read().split('\n')
|
||||
else:
|
||||
# fabricate a reference fstab
|
||||
fstab = [
|
||||
"#ORIGFSTAB#/dev/mapper/root# / xfs defaults 0 0",
|
||||
"#ORIGFSTAB#UUID=aaf9e0f9-aa4d-4d74-9e75-3537620cfe23# /boot xfs defaults 0 0",
|
||||
"#ORIGFSTAB#UUID=C21D-B881# /boot/efi vfat umask=0077,shortname=winnt 0 2",
|
||||
"#ORIGFSTAB#/dev/mapper/swap# none swap defaults 0 0",
|
||||
]
|
||||
while not fstab[0]:
|
||||
fstab = fstab[1:]
|
||||
if os.path.exists(os.path.join(rootdir, '.autorelabel')):
|
||||
@@ -126,8 +139,10 @@ def fixup(rootdir, vols):
|
||||
newcfg = ifcfg.split('/')[-1]
|
||||
newcfg = os.path.join(rootdir, 'etc/NetworkManager/system-connections/{0}'.format(newcfg))
|
||||
shutil.copy2(ifcfg, newcfg)
|
||||
shutil.rmtree(os.path.join(rootdir, 'etc/confluent/'))
|
||||
shutil.copytree('/etc/confluent', os.path.join(rootdir, 'etc/confluent'))
|
||||
rootconfluentdir = os.path.join(rootdir, 'etc/confluent/')
|
||||
if os.path.exists(rootconfluentdir):
|
||||
shutil.rmtree(rootconfluentdir)
|
||||
shutil.copytree('/etc/confluent', rootconfluentdir)
|
||||
if policy:
|
||||
sys.stdout.write('Applying SELinux labeling...')
|
||||
sys.stdout.flush()
|
||||
@@ -142,14 +157,41 @@ def fixup(rootdir, vols):
|
||||
grubsyscfg = os.path.join(rootdir, 'etc/sysconfig/grub')
|
||||
if not os.path.exists(grubsyscfg):
|
||||
grubsyscfg = os.path.join(rootdir, 'etc/default/grub')
|
||||
with open(grubsyscfg) as defgrubin:
|
||||
defgrub = defgrubin.read().split('\n')
|
||||
currcmdline = []
|
||||
with open('/proc/cmdline') as cmdlinein:
|
||||
cmdline = cmdlinein.read().strip()
|
||||
for arg in cmdline.split():
|
||||
if arg.startswith('console='):
|
||||
currcmdline.append(arg)
|
||||
elif arg == 'quiet':
|
||||
currcmdline.append(arg)
|
||||
currcmdlinestr = ' '.join(currcmdline)
|
||||
if os.path.exists(grubsyscfg):
|
||||
with open(grubsyscfg) as defgrubin:
|
||||
defgrub = defgrubin.read().split('\n')
|
||||
else:
|
||||
defgrub = [
|
||||
'GRUB_TIMEOUT=5',
|
||||
'GRUB_DISTRIBUTOR="$(sed ' + "'s, release .*$,,g'" + ' /etc/system-release)"',
|
||||
'GRUB_DEFAULT=saved',
|
||||
'GRUB_DISABLE_SUBMENU=true',
|
||||
'GRUB_TERMINAL=""',
|
||||
'GRUB_SERIAL_COMMAND=""',
|
||||
'GRUB_CMDLINE_LINUX="{} crashkernel=1G-4G:192M,4G-64G:256M,64G-:512M rd.lvm.lv=vg/root rd.lvm.lv=vg/swap"'.format(currcmdlinestr),
|
||||
'GRUB_DISABLE_RECOVERY="true"',
|
||||
'GRUB_ENABLE_BLSCFG=true',
|
||||
]
|
||||
if not os.path.exists(os.path.join(rootdir, "etc/kernel/cmdline")):
|
||||
with open(os.path.join(rootdir, "etc/kernel/cmdline"), "w") as cmdlineout:
|
||||
cmdlineout.write("{} root=/dev/mapper/localstorage-root rd.lvm.lv=localstorage/root".format(currcmdlinestr))
|
||||
with open(grubsyscfg, 'w') as defgrubout:
|
||||
for gline in defgrub:
|
||||
gline = gline.split()
|
||||
newline = []
|
||||
for ent in gline:
|
||||
if ent.startswith('resume=') or ent.startswith('rd.lvm.lv'):
|
||||
if ent.endswith('"'):
|
||||
newline.append('"')
|
||||
continue
|
||||
newline.append(ent)
|
||||
defgrubout.write(' '.join(newline) + '\n')
|
||||
@@ -159,6 +201,12 @@ def fixup(rootdir, vols):
|
||||
grubcfg = grubcfg[:-1]
|
||||
if len(grubcfg) == 1:
|
||||
grubcfg = grubcfg[0]
|
||||
elif not grubcfg:
|
||||
grubcfg = '/boot/grub2/grub.cfg'
|
||||
paths = glob.glob(os.path.join(rootdir, 'boot/efi/EFI/*'))
|
||||
for path in paths:
|
||||
with open(os.path.join(path, 'grub.cfg'), 'w') as stubgrubout:
|
||||
stubgrubout.write("search --no-floppy --root-dev-only --fs-uuid --set=dev " + bootuuid + "\nset prefix=($dev)/grub2\nexport $prefix\nconfigfile $prefix/grub.cfg\n")
|
||||
else:
|
||||
for gcfg in grubcfg:
|
||||
rgcfg = os.path.join(rootdir, gcfg[1:]) # gcfg has a leading / to get rid of
|
||||
@@ -214,10 +262,18 @@ def fixup(rootdir, vols):
|
||||
shimpath = subprocess.check_output(['find', os.path.join(rootdir, 'boot/efi'), '-name', 'shimx64.efi']).decode('utf8').strip()
|
||||
shimpath = shimpath.replace(rootdir, '/').replace('/boot/efi', '').replace('//', '/').replace('/', '\\')
|
||||
subprocess.check_call(['efibootmgr', '-c', '-d', targblock, '-l', shimpath, '--part', partnum])
|
||||
try:
|
||||
os.makedirs(os.path.join(rootdir, 'opt/confluent/bin'))
|
||||
except Exception:
|
||||
pass
|
||||
shutil.copy2('/opt/confluent/bin/apiclient', os.path.join(rootdir, 'opt/confluent/bin/apiclient'))
|
||||
#other network interfaces
|
||||
|
||||
|
||||
def had_swap():
|
||||
if not os.path.exists('/etc/fstab'):
|
||||
# diskless source, assume swap
|
||||
return True
|
||||
with open('/etc/fstab') as tabfile:
|
||||
tabs = tabfile.read().split('\n')
|
||||
for tab in tabs:
|
||||
@@ -362,6 +418,8 @@ def install_to_disk(imgpath):
|
||||
subprocess.check_call(['mount', vol['targetdisk'], '/run/imginst/targ'])
|
||||
source = vol['mount'].replace('/', '_')
|
||||
source = '/run/imginst/sources/' + source
|
||||
if not os.path.exists(source):
|
||||
source = '/run/imginst/sources/_' + vol['mount']
|
||||
blankfsstat = os.statvfs('/run/imginst/targ')
|
||||
blankused = (blankfsstat.f_blocks - blankfsstat.f_bfree) * blankfsstat.f_bsize
|
||||
sys.stdout.write('\nWriting {0}: '.format(vol['mount']))
|
||||
@@ -419,8 +477,14 @@ def install_to_disk(imgpath):
|
||||
|
||||
|
||||
|
||||
|
||||
subprocess.check_call(['umount', '/run/imginst/targ'])
|
||||
while True:
|
||||
try:
|
||||
subprocess.check_call(['umount', '/run/imginst/targ'])
|
||||
except subprocess.CalledProcessError:
|
||||
print("Failed to unmount /run/imginst/targ, retrying")
|
||||
time.sleep(1)
|
||||
else:
|
||||
break
|
||||
for vol in allvols:
|
||||
subprocess.check_call(['mount', vol['targetdisk'], '/run/imginst/targ/' + vol['mount']])
|
||||
fixup('/run/imginst/targ', allvols)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
# and existing mounts of image (to take advantage of caching)
|
||||
mount -o bind /sys /sysroot/sys
|
||||
mount -o bind /dev /sysroot/dev
|
||||
mount -o bind /dev/pts /sysroot/dev/pts
|
||||
mount -o bind /proc /sysroot/proc
|
||||
mount -o bind /run /sysroot/run
|
||||
|
||||
@@ -21,8 +22,14 @@ else
|
||||
done
|
||||
fi
|
||||
cd /sysroot/run
|
||||
cp /run/sshd.pid /tmp/dbgssh.pid
|
||||
chroot /sysroot/ bash -c "/usr/sbin/sshd"
|
||||
chroot /sysroot/ bash -c "source /etc/confluent/functions; run_remote_python getinstalldisk"
|
||||
chroot /sysroot/ bash -c "source /etc/confluent/functions; run_remote_parts pre.d"
|
||||
for nameserver in $(sed -n '/^nameservers:/,/^[^-]/p' /etc/confluent/confluent.deploycfg|grep ^- | cut -d ' ' -f 2|sed -e 's/ //'); do
|
||||
echo "nameserver $nameserver" >> /sysroot/etc/resolv.conf
|
||||
done
|
||||
#chroot /sysroot/ bash -c "source /etc/confluent/functions; run_remote_python confignet"
|
||||
if [ ! -f /sysroot/tmp/installdisk ]; then
|
||||
echo 'Unable to find a suitable installation target device, ssh to port 2222 to investigate'
|
||||
while [ ! -f /sysroot/tmp/installdisk ]; do
|
||||
@@ -39,7 +46,10 @@ chroot /sysroot bash -c "source /etc/confluent/functions; run_remote_python imag
|
||||
echo "Port 22" >> /etc/ssh/sshd_config
|
||||
echo 'Match LocalPort 22' >> /etc/ssh/sshd_config
|
||||
echo ' ChrootDirectory /sysroot/run/imginst/targ' >> /etc/ssh/sshd_config
|
||||
kill -HUP $(cat /run/sshd.pid)
|
||||
kill $(cat /sysroot/var/run/sshd.pid)
|
||||
kill -HUP $(cat /tmp/dbgssh.pid)
|
||||
cp /sysroot/etc/pki/ca-trust/source/anchors/* /sysroot/run/imginst/targ/etc/pki/ca-trust/source/anchors/
|
||||
chroot /sysroot/run/imginst/targ update-ca-trust
|
||||
|
||||
chroot /sysroot/run/imginst/targ bash -c "source /etc/confluent/functions; run_remote post.sh"
|
||||
chroot /sysroot bash -c "umount \$(tac /proc/mounts|awk '{print \$2}'|grep ^/run/imginst/targ)"
|
||||
|
||||
@@ -59,7 +59,7 @@ rpm --import /etc/pki/rpm-gpg/*
|
||||
|
||||
run_remote_python add_local_repositories
|
||||
run_remote_python syncfileclient
|
||||
run_remote_python confignet
|
||||
run_remote_python confignet -c $confluent_mgr
|
||||
|
||||
run_remote onboot.custom
|
||||
# onboot scripts may be placed into onboot.d, e.g. onboot.d/01-firstaction.sh, onboot.d/02-secondaction.sh
|
||||
|
||||
@@ -2,13 +2,17 @@
|
||||
|
||||
# This script is executed 'chrooted' into a cloned disk target before rebooting
|
||||
#
|
||||
|
||||
if [ -f /etc/dracut.conf.d/diskless.conf ]; then
|
||||
rm /etc/dracut.conf.d/diskless.conf
|
||||
fi
|
||||
for kver in /lib/modules/*; do kver=$(basename $kver); kernel-install add $kver /boot/vmlinuz-$kver; done
|
||||
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
|
||||
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
|
||||
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
export nodename confluent_mgr confluent_profile
|
||||
. /etc/confluent/functions
|
||||
run_remote setupssh
|
||||
mkdir -p /var/log/confluent
|
||||
chmod 700 /var/log/confluent
|
||||
exec >> /var/log/confluent/confluent-post.log
|
||||
@@ -33,6 +37,8 @@ run_remote_parts post.d
|
||||
# Induce execution of remote configuration, e.g. ansible plays in ansible/post.d/
|
||||
run_remote_config post.d
|
||||
|
||||
cd /root/
|
||||
fetch_remote confignet
|
||||
curl -sf -X POST -d 'status: staged' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus
|
||||
|
||||
kill $logshowpid
|
||||
|
||||
@@ -3,10 +3,12 @@ echo -n "" >> /tmp/net.ifaces
|
||||
echo -n "" > /tmp/01-autocons.devnode
|
||||
BUNDLENAME=/etc/pki/tls/certs/ca-bundle.crt
|
||||
if [ ! -e "$BUNDLENAME" ]; then
|
||||
BUNDLENAME=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
|
||||
mkdir -p /etc/pki/tls/certs
|
||||
ln -s $BUNDLENAME /etc/pki/tls/certs/ca-bundle.crt
|
||||
fi
|
||||
if [ -e /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem ]; then
|
||||
BUNDLENAME=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
|
||||
ln -s $BUNDLENAME /etc/pki/tls/certs/ca-bundle.crt
|
||||
fi
|
||||
fi
|
||||
cat /tls/*.0 >> $BUNDLENAME
|
||||
if ! grep console= /proc/cmdline >& /dev/null; then
|
||||
autocons=$(/opt/confluent/bin/autocons)
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
@@ -31,8 +31,10 @@ done
|
||||
if [ ! -f /etc/confluent/firstboot.ran ]; then
|
||||
touch /etc/confluent/firstboot.ran
|
||||
|
||||
cat /etc/confluent/tls/*.pem >> /etc/pki/tls/certs/ca-bundle.crt
|
||||
|
||||
cat /etc/confluent/tls/*.pem >> /etc/pki/tls/certs/ca-bundle.crt
|
||||
confluentpython /root/confignet
|
||||
rm /root/confignet
|
||||
run_remote firstboot.custom
|
||||
# Firstboot scripts may be placed into firstboot.d, e.g. firstboot.d/01-firstaction.sh, firstboot.d/02-secondaction.sh
|
||||
run_remote_parts firstboot.d
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@ import struct
|
||||
import sys
|
||||
import subprocess
|
||||
import traceback
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
yaml = None
|
||||
|
||||
bootuuid = None
|
||||
vgname = 'localstorage'
|
||||
@@ -66,9 +70,9 @@ def get_image_metadata(imgpath):
|
||||
yield md
|
||||
else:
|
||||
# plausible filesystem structure to apply to a nominally "diskless" image
|
||||
yield {'mount': '/', 'filesystem': 'xfs', 'minsize': 39513563136, 'initsize': 954128662528, 'flags': 'rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota', 'device': '/dev/mapper/root', 'compressed_size': 27022069760}
|
||||
yield {'mount': '/boot', 'filesystem': 'xfs', 'minsize': 232316928, 'initsize': 1006632960, 'flags': 'rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota', 'device': '/dev/nvme1n1p2', 'compressed_size': 171462656}
|
||||
yield {'mount': '/boot/efi', 'filesystem': 'vfat', 'minsize': 7835648, 'initsize': 627900416, 'flags': 'rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=ascii,shortname=winnt,errors=remount-ro', 'device': '/dev/nvme1n1p1', 'compressed_size': 1576960}
|
||||
yield {'mount': '/', 'filesystem': 'xfs', 'minsize': 4294967296, 'initsize': 954128662528, 'flags': 'rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota', 'device': '/dev/mapper/root', 'compressed_size': 27022069760}
|
||||
yield {'mount': '/boot', 'filesystem': 'xfs', 'minsize': 536870912, 'initsize': 1006632960, 'flags': 'rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota', 'device': '/dev/nvme1n1p2', 'compressed_size': 171462656}
|
||||
yield {'mount': '/boot/efi', 'filesystem': 'vfat', 'minsize': 33554432, 'initsize': 627900416, 'flags': 'rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=ascii,shortname=winnt,errors=remount-ro', 'device': '/dev/nvme1n1p1', 'compressed_size': 1576960}
|
||||
#raise Exception('Installation from single part image not supported')
|
||||
|
||||
class PartedRunner():
|
||||
@@ -166,6 +170,15 @@ def fixup(rootdir, vols):
|
||||
grubsyscfg = os.path.join(rootdir, 'etc/sysconfig/grub')
|
||||
if not os.path.exists(grubsyscfg):
|
||||
grubsyscfg = os.path.join(rootdir, 'etc/default/grub')
|
||||
currcmdline = []
|
||||
with open('/proc/cmdline') as cmdlinein:
|
||||
cmdline = cmdlinein.read().strip()
|
||||
for arg in cmdline.split():
|
||||
if arg.startswith('console='):
|
||||
currcmdline.append(arg)
|
||||
elif arg == 'quiet':
|
||||
currcmdline.append(arg)
|
||||
currcmdlinestr = ' '.join(currcmdline)
|
||||
kcmdline = os.path.join(rootdir, 'etc/kernel/cmdline')
|
||||
if os.path.exists(kcmdline):
|
||||
with open(kcmdline) as kcmdlinein:
|
||||
@@ -177,8 +190,10 @@ def fixup(rootdir, vols):
|
||||
elif ent.startswith('root='):
|
||||
newkcmdlineent.append('root={}'.format(newrootdev))
|
||||
elif ent.startswith('rd.lvm.lv='):
|
||||
ent = convert_lv(ent)
|
||||
if ent:
|
||||
nent = convert_lv(ent)
|
||||
if nent:
|
||||
newkcmdlineent.append(ent)
|
||||
else:
|
||||
newkcmdlineent.append(ent)
|
||||
else:
|
||||
newkcmdlineent.append(ent)
|
||||
@@ -200,8 +215,10 @@ def fixup(rootdir, vols):
|
||||
elif cfgpart.startswith('resume='):
|
||||
newcfgparts.append('resume={}'.format(newswapdev))
|
||||
elif cfgpart.startswith('rd.lvm.lv='):
|
||||
cfgpart = convert_lv(cfgpart)
|
||||
if cfgpart:
|
||||
ncfgpart = convert_lv(cfgpart)
|
||||
if ncfgpart:
|
||||
newcfgparts.append(ncfgpart)
|
||||
else:
|
||||
newcfgparts.append(cfgpart)
|
||||
else:
|
||||
newcfgparts.append(cfgpart)
|
||||
@@ -217,13 +234,13 @@ def fixup(rootdir, vols):
|
||||
'GRUB_DISABLE_SUBMENU=true',
|
||||
'GRUB_TERMINAL=""',
|
||||
'GRUB_SERIAL_COMMAND=""',
|
||||
'GRUB_CMDLINE_LINUX="crashkernel=1G-4G:192M,4G-64G:256M,64G-:512M rd.lvm.lv=vg/root rd.lvm.lv=vg/swap"',
|
||||
'GRUB_CMDLINE_LINUX="{}crashkernel=1G-4G:192M,4G-64G:256M,64G-:512M rd.lvm.lv=vg/root rd.lvm.lv=vg/swap"'.format(currcmdlinestr),
|
||||
'GRUB_DISABLE_RECOVERY="true"',
|
||||
'GRUB_ENABLE_BLSCFG=true',
|
||||
]
|
||||
if not os.path.exists(os.path.join(rootdir, "etc/kernel/cmdline")):
|
||||
with open(os.path.join(rootdir, "etc/kernel/cmdline"), "w") as cmdlineout:
|
||||
cmdlineout.write("root=/dev/mapper/localstorage-root rd.lvm.lv=localstorage/root")
|
||||
cmdlineout.write("{} root=/dev/mapper/localstorage-root rd.lvm.lv=localstorage/root".format(currcmdlinestr))
|
||||
with open(grubsyscfg, 'w') as defgrubout:
|
||||
for gline in defgrub:
|
||||
gline = gline.split()
|
||||
@@ -234,11 +251,11 @@ def fixup(rootdir, vols):
|
||||
elif ent.startswith('root='):
|
||||
newline.append('root={}'.format(newrootdev))
|
||||
elif ent.startswith('rd.lvm.lv='):
|
||||
ent = convert_lv(ent)
|
||||
if ent:
|
||||
nent = convert_lv(ent)
|
||||
if nent:
|
||||
newline.append(nent)
|
||||
else:
|
||||
newline.append(ent)
|
||||
elif '""' in ent:
|
||||
newline.append('""')
|
||||
else:
|
||||
newline.append(ent)
|
||||
defgrubout.write(' '.join(newline) + '\n')
|
||||
@@ -301,8 +318,8 @@ def fixup(rootdir, vols):
|
||||
for vol in vols:
|
||||
if vol['mount'] == '/boot/efi':
|
||||
targdev = vol['targetdisk']
|
||||
partnum = re.search('(\d+)$', targdev).group(1)
|
||||
targblock = re.search('(.*)\d+$', targdev).group(1)
|
||||
partnum = re.search(r'(\d+)$', targdev).group(1)
|
||||
targblock = re.search(r'(.*)\d+$', targdev).group(1)
|
||||
if targblock:
|
||||
if targblock.endswith('p') and 'nvme' in targblock:
|
||||
targblock = targblock[:-1]
|
||||
@@ -334,13 +351,16 @@ def had_swap():
|
||||
|
||||
newrootdev = None
|
||||
newswapdev = None
|
||||
vgmap = None
|
||||
def install_to_disk(imgpath):
|
||||
global vgmap
|
||||
global bootuuid
|
||||
global newrootdev
|
||||
global newswapdev
|
||||
global vgname
|
||||
global oldvgname
|
||||
lvmvols = {}
|
||||
vgmap = {}
|
||||
deftotsize = 0
|
||||
mintotsize = 0
|
||||
deflvmsize = 0
|
||||
@@ -365,24 +385,30 @@ def install_to_disk(imgpath):
|
||||
mintotsize = swapsize
|
||||
for fs in get_image_metadata(imgpath):
|
||||
allvols.append(fs)
|
||||
deftotsize += fs['initsize']
|
||||
mintotsize += fs['minsize']
|
||||
if fs['initsize'] > biggestsize:
|
||||
biggestfs = fs
|
||||
biggestsize = fs['initsize']
|
||||
|
||||
if fs['device'].startswith('/dev/mapper'):
|
||||
oldvgname = fs['device'].rsplit('/', 1)[-1]
|
||||
odevname = fs['device'].rsplit('/', 1)[-1]
|
||||
# if node has - then /dev/mapper will double up the hypen
|
||||
if '_' in oldvgname and '-' in oldvgname.split('_', 1)[-1]:
|
||||
oldvgname = oldvgname.rsplit('-', 1)[0].replace('--', '-')
|
||||
if '_' in odevname and '-' in odevname.split('_', 1)[-1]:
|
||||
oldvgname = odevname.rsplit('-', 1)[0].replace('--', '-')
|
||||
osname = oldvgname.split('_')[0]
|
||||
nodename = socket.gethostname().split('.')[0]
|
||||
vgname = '{}_{}'.format(osname, nodename)
|
||||
lvmvols[fs['device'].replace('/dev/mapper/', '')] = fs
|
||||
elif '-' in odevname: # unique one
|
||||
vgmap[odevname] = odevname.split('-')[0]
|
||||
lvmvols[odevname] = fs
|
||||
|
||||
continue
|
||||
lvmvols[odevname] = fs
|
||||
deflvmsize += fs['initsize']
|
||||
minlvmsize += fs['minsize']
|
||||
else:
|
||||
plainvols[int(re.search('(\d+)$', fs['device'])[0])] = fs
|
||||
plainvols[int(re.search(r'(\d+)$', fs['device'])[0])] = fs
|
||||
if fs['initsize'] > biggestsize:
|
||||
biggestfs = fs
|
||||
biggestsize = fs['initsize']
|
||||
deftotsize += fs['initsize']
|
||||
mintotsize += fs['minsize']
|
||||
with open('/tmp/installdisk') as diskin:
|
||||
instdisk = diskin.read()
|
||||
instdisk = '/dev/' + instdisk
|
||||
@@ -440,6 +466,28 @@ def install_to_disk(imgpath):
|
||||
lvmpart = get_partname(instdisk, volidx + 1)
|
||||
subprocess.check_call(['pvcreate', '-ff', '-y', lvmpart])
|
||||
subprocess.check_call(['vgcreate', vgname, lvmpart])
|
||||
vgroupmap = {}
|
||||
if yaml and vgmap:
|
||||
with open('/tmp/volumegroupmap.yml') as mapin:
|
||||
vgroupmap = yaml.safe_load(mapin)
|
||||
donedisks = {}
|
||||
for morevolname in vgmap:
|
||||
morevg = vgmap[morevolname]
|
||||
if morevg not in vgroupmap:
|
||||
raise Exception("No mapping defined to create volume group {}".format(morevg))
|
||||
targdisk = vgroupmap[morevg]
|
||||
if targdisk not in donedisks:
|
||||
moreparted = PartedRunner(targdisk)
|
||||
moreparted.run('mklabel gpt')
|
||||
moreparted.run('mkpart lvm 0% 100%')
|
||||
morelvmpart = get_partname(targdisk, 1)
|
||||
subprocess.check_call(['pvcreate', '-ff', '-y', morelvmpart])
|
||||
subprocess.check_call(['vgcreate', morevg, morelvmpart])
|
||||
donedisks[targdisk] = 1
|
||||
morelvname = morevolname.split('-', 1)[1]
|
||||
subprocess.check_call(['lvcreate', '-L', '{}b'.format(lvmvols[morevolname]['initsize']), '-y', '-n', morelvname, morevg])
|
||||
lvmvols[morevolname]['targetdisk'] = '/dev/{}/{}'.format(morevg, morelvname)
|
||||
|
||||
vginfo = subprocess.check_output(['vgdisplay', vgname, '--units', 'b']).decode('utf8')
|
||||
vginfo = vginfo.split('\n')
|
||||
pesize = 0
|
||||
@@ -452,6 +500,9 @@ def install_to_disk(imgpath):
|
||||
pes = int(infline[4])
|
||||
takeaway = swapsize // pesize
|
||||
for volidx in lvmvols:
|
||||
if volidx in vgmap:
|
||||
# was handled previously
|
||||
continue
|
||||
vol = lvmvols[volidx]
|
||||
if vol is biggestfs:
|
||||
continue
|
||||
@@ -460,6 +511,10 @@ def install_to_disk(imgpath):
|
||||
biggestextents = pes - takeaway
|
||||
for volidx in lvmvols:
|
||||
vol = lvmvols[volidx]
|
||||
if volidx in vgmap:
|
||||
# was handled previously
|
||||
continue
|
||||
|
||||
if vol is biggestfs:
|
||||
extents = biggestextents
|
||||
else:
|
||||
@@ -546,7 +601,13 @@ def install_to_disk(imgpath):
|
||||
|
||||
|
||||
|
||||
subprocess.check_call(['umount', '/run/imginst/targ'])
|
||||
while True:
|
||||
try:
|
||||
subprocess.check_call(['umount', '/run/imginst/targ'])
|
||||
break
|
||||
except subprocess.CalledProcessError:
|
||||
print("Failed to unmount /run/imginst/targ, retrying")
|
||||
time.sleep(1)
|
||||
for vol in allvols:
|
||||
subprocess.check_call(['mount', vol['targetdisk'], '/run/imginst/targ/' + vol['mount']])
|
||||
fixup('/run/imginst/targ', allvols)
|
||||
|
||||
@@ -129,5 +129,11 @@ mv /lib/modules/$(uname -r) /lib/modules/$(uname -r)-ramfs
|
||||
ln -s /sysroot/lib/modules/$(uname -r) /lib/modules/
|
||||
mv /lib/firmware /lib/firmware-ramfs
|
||||
ln -s /sysroot/lib/firmware /lib/firmware
|
||||
rm -f /sysroot/etc/dracut.conf.d/diskless.conf # remove diskless dracut from runtime, to make kdump happier
|
||||
kill $(grep -l ^/usr/lib/systemd/systemd-udevd /proc/*/cmdline|cut -d/ -f 3)
|
||||
exec /opt/confluent/bin/start_root
|
||||
if grep debugssh /proc/cmdline >& /dev/null; then
|
||||
exec /opt/confluent/bin/start_root
|
||||
else
|
||||
rm -rf /lib/modules/$(uname -r) /lib/modules/$(uname -r)-ramfs /lib/firmware-ramfs /usr/lib64/libcrypto.so* /usr/lib64/systemd/ /kernel/ /usr/bin/ /usr/sbin/ /usr/libexec/
|
||||
exec /opt/confluent/bin/start_root -s # share mount namespace, keep kernel callbacks intact
|
||||
fi
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
# and existing mounts of image (to take advantage of caching)
|
||||
mount -o bind /sys /sysroot/sys
|
||||
mount -o bind /dev /sysroot/dev
|
||||
mount -o bind /dev/pts /sysroot/dev/pts
|
||||
mount -o bind /proc /sysroot/proc
|
||||
mount -o bind /run /sysroot/run
|
||||
mount -t efivarfs none /sysroot/sys/firmware/efi/efivars
|
||||
|
||||
|
||||
|
||||
|
||||
if [ ! -f /tmp/mountparts.sh ]; then
|
||||
@@ -21,8 +25,16 @@ else
|
||||
done
|
||||
fi
|
||||
cd /sysroot/run
|
||||
[ -f /run/sshd.pid ] &&
|
||||
cp /run/sshd.pid /tmp/dbgssh.pid
|
||||
chmod 0600 /sysroot/etc/ssh/ssh*key
|
||||
chroot /sysroot/ bash -c "/usr/sbin/sshd"
|
||||
chroot /sysroot/ bash -c "source /etc/confluent/functions; run_remote_python getinstalldisk"
|
||||
chroot /sysroot/ bash -c "source /etc/confluent/functions; run_remote_parts pre.d"
|
||||
for nameserver in $(sed -n '/^nameservers:/,/^[^-]/p' /etc/confluent/confluent.deploycfg|grep ^- | cut -d ' ' -f 2|sed -e 's/ //'); do
|
||||
echo "nameserver $nameserver" >> /sysroot/etc/resolv.conf
|
||||
done
|
||||
|
||||
if [ ! -f /sysroot/tmp/installdisk ]; then
|
||||
echo 'Unable to find a suitable installation target device, ssh to port 2222 to investigate'
|
||||
while [ ! -f /sysroot/tmp/installdisk ]; do
|
||||
@@ -40,7 +52,8 @@ chroot /sysroot bash -c "source /etc/confluent/functions; run_remote_python imag
|
||||
echo "Port 22" >> /etc/ssh/sshd_config
|
||||
echo 'Match LocalPort 22' >> /etc/ssh/sshd_config
|
||||
echo ' ChrootDirectory /sysroot/run/imginst/targ' >> /etc/ssh/sshd_config
|
||||
kill -HUP $(cat /run/sshd.pid)
|
||||
kill $(cat /sysroot/var/run/sshd.pid)
|
||||
[ -f /tmp/dbgssh.pid ] && kill -HUP $(cat /tmp/dbgssh.pid)
|
||||
cp /sysroot/etc/pki/ca-trust/source/anchors/* /sysroot/run/imginst/targ/etc/pki/ca-trust/source/anchors/
|
||||
chroot /sysroot/run/imginst/targ update-ca-trust
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ rpm --import /etc/pki/rpm-gpg/*
|
||||
|
||||
run_remote_python add_local_repositories
|
||||
run_remote_python syncfileclient
|
||||
run_remote_python confignet
|
||||
run_remote_python confignet -c $confluent_mgr
|
||||
|
||||
run_remote onboot.custom
|
||||
# onboot scripts may be placed into onboot.d, e.g. onboot.d/01-firstaction.sh, onboot.d/02-secondaction.sh
|
||||
|
||||
@@ -43,7 +43,8 @@ run_remote_parts post.d
|
||||
|
||||
# Induce execution of remote configuration, e.g. ansible plays in ansible/post.d/
|
||||
run_remote_config post.d
|
||||
|
||||
cd /root/
|
||||
fetch_remote confignet
|
||||
# rebuild initrd, pick up new drivers if needed
|
||||
dracut -f /boot/initramfs-$(uname -r).img $(uname -r)
|
||||
|
||||
|
||||
@@ -62,8 +62,8 @@ if [ -e /tmp/confluentident/cnflnt.yml ]; then
|
||||
fi
|
||||
v4nm=$(grep ipv4_netmask: $tcfg)
|
||||
v4nm=${v4nm#ipv4_netmask: }
|
||||
localcli network ip interface ipv4 set -i vmk0 -I $v4addr -N $v4nm -g $v4gw -t static
|
||||
localcli network ip route ipv4 add -n default -g $v4gw
|
||||
while ! localcli network ip interface ipv4 set -i vmk0 -I $v4addr -N $v4nm -g $v4gw -t static; do echo "Retrying..."; sleep 5; done
|
||||
while ! localcli network ip route ipv4 add -n default -g $v4gw; do sleep 1; done
|
||||
fi
|
||||
hmackeyfile=$(mktemp)
|
||||
echo -n $(grep ^apitoken: /tmp/confluentident/cnflnt.yml|awk '{print $2}') > $hmackeyfile
|
||||
@@ -73,6 +73,20 @@ if [ -e /tmp/confluentident/cnflnt.yml ]; then
|
||||
hmacfile=$(mktemp)
|
||||
ln -s /opt/confluent/bin/clortho /opt/confluent/bin/genpasshmac
|
||||
/opt/confluent/bin/genpasshmac $passfile $passcrypt $hmacfile $hmackeyfile
|
||||
echo -n 'Checking connectivity to server: '
|
||||
maxwait=30
|
||||
while ! /opt/confluent/bin/apiclient -c >& /dev/null; do
|
||||
echo -n '.'
|
||||
sleep 1
|
||||
maxwait=$((maxwait - 1))
|
||||
if [ $maxwait -le 0 ]; then
|
||||
echo "Unable to contact deployment server, verify network connectivity"
|
||||
echo "A debug session has been made available on Alt-F1"
|
||||
sleep 30
|
||||
maxwait=30
|
||||
fi
|
||||
done
|
||||
echo
|
||||
echo -n 'Registering new API key with deployment server: '
|
||||
/opt/confluent/bin/apiclient -p $hmacfile /confluent-api/self/registerapikey $passcrypt
|
||||
echo
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
accepteula
|
||||
clearpart --firstdisk --overwritevmfs
|
||||
install --firstdisk --overwritevmfs
|
||||
%include /tmp/storagecfg
|
||||
%include /tmp/ksnet
|
||||
%include /tmp/rootpw
|
||||
reboot
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/python3
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname, devinfo):
|
||||
self.name = devname
|
||||
self.path = '/dev/' + devname
|
||||
self.wwn = None
|
||||
self.model = devinfo.get('model', 'Unknown')
|
||||
self.driver = devinfo.get('adapter_driver', 'Unknown')
|
||||
self.size = devinfo.get('size', 0) # in MiB
|
||||
if not devinfo.get('is_local', False):
|
||||
raise SilentException("Not local")
|
||||
if devinfo.get('is_removable', False):
|
||||
raise SilentException("Removable")
|
||||
if devinfo.get('is_usb', False):
|
||||
raise SilentException("USB device")
|
||||
if devinfo.get('type', '').lower() in ('cd-rom',):
|
||||
raise SilentException("CD-ROM device")
|
||||
if self.size < 2048:
|
||||
raise SilentException("Too small")
|
||||
|
||||
|
||||
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
if self.model.lower() in ('m.2 nvme 2-bay raid kit', 'thinksystem_m.2_vd', 'thinksystem m.2', 'thinksystem_m.2'):
|
||||
return 0
|
||||
if self.driver == 'vmw_ahci':
|
||||
return 2
|
||||
if self.driver == 'nvme_pcie':
|
||||
return 3
|
||||
return 99
|
||||
|
||||
def __repr__(self):
|
||||
return repr({
|
||||
'name': self.name,
|
||||
'path': self.path,
|
||||
'wwn': self.wwn,
|
||||
'driver': self.driver,
|
||||
'size': self.size,
|
||||
'model': self.model,
|
||||
})
|
||||
|
||||
def list_disks():
|
||||
current_dev = None
|
||||
disks = {}
|
||||
devlist = subprocess.check_output(['localcli', 'storage', 'core', 'device', 'list'])
|
||||
if not isinstance(devlist, str):
|
||||
devlist = devlist.decode('utf8')
|
||||
devbyadp = {}
|
||||
for line in devlist.split('\n'):
|
||||
if not line.strip():
|
||||
continue
|
||||
if not line.startswith(' '):
|
||||
current_dev = line.rsplit(':', 1)[0]
|
||||
if current_dev not in disks:
|
||||
disks[current_dev] = {}
|
||||
elif current_dev:
|
||||
if ' Model:' in line:
|
||||
disks[current_dev]['model'] = ' '.join(line.split()[1:])
|
||||
elif ' Driver:' in line:
|
||||
disks[current_dev]['driver'] = ' '.join(line.split()[1:])
|
||||
elif ' Is Local:' in line:
|
||||
disks[current_dev]['is_local'] = ' '.join(line.split()[2:]).lower() == 'true'
|
||||
elif ' Is Removable:' in line:
|
||||
disks[current_dev]['is_removable'] = ' '.join(line.split()[2:]).lower() == 'true'
|
||||
elif ' Size:' in line: # in MiB
|
||||
disks[current_dev]['size'] = int(line.split()[1])
|
||||
elif ' Is SSD:' in line:
|
||||
disks[current_dev]['is_ssd'] = ' '.join(line.split()[2:]).lower() == 'true'
|
||||
elif ' Is USB:' in line:
|
||||
disks[current_dev]['is_usb'] = ' '.join(line.split()[2:]).lower() == 'true'
|
||||
elif ' Is Removable:' in line:
|
||||
disks[current_dev]['is_removable'] = ' '.join(line.split()[2:]).lower() == 'true'
|
||||
elif 'Device Type:' in line:
|
||||
disks[current_dev]['type'] = ' '.join(line.split()[2:])
|
||||
for dev in disks:
|
||||
pathlist = subprocess.check_output(['localcli', 'storage', 'core', 'path', 'list', '--device', dev])
|
||||
if not isinstance(pathlist, str):
|
||||
pathlist = pathlist.decode('utf8')
|
||||
for line in pathlist.split('\n'):
|
||||
if not line.strip():
|
||||
continue
|
||||
if not line.startswith(' '):
|
||||
continue
|
||||
if ' Adapter Identifier:' in line:
|
||||
adpname = ' '.join(line.split()[2:])
|
||||
disks[dev]['adapter_id'] = adpname
|
||||
elif ' Adapter:' in line:
|
||||
adp = ' '.join(line.split()[1:])
|
||||
disks[dev]['adapter'] = adp
|
||||
devbyadp.setdefault(adp, []).append(dev)
|
||||
adapterlist = subprocess.check_output(['localcli', 'storage', 'core', 'adapter', 'list'])
|
||||
if not isinstance(adapterlist, str):
|
||||
adapterlist = adapterlist.decode('utf8')
|
||||
driverbyadp = {}
|
||||
linenum = 0
|
||||
for line in adapterlist.split('\n'):
|
||||
linenum += 1
|
||||
if not line.strip():
|
||||
continue
|
||||
if linenum < 3:
|
||||
continue
|
||||
parts = line.split()
|
||||
if len(parts) < 2:
|
||||
continue
|
||||
adp = parts[0]
|
||||
driver = parts[1]
|
||||
driverbyadp[adp] = driver
|
||||
for adp in devbyadp:
|
||||
driver = driverbyadp.get(adp, 'Unknown')
|
||||
for dev in devbyadp[adp]:
|
||||
disks[dev]['adapter_driver'] = driver
|
||||
return disks
|
||||
|
||||
def main():
|
||||
disks = []
|
||||
try:
|
||||
alldisks = list_disks()
|
||||
except Exception as e:
|
||||
print("Error listing disks: {0}".format(str(e)))
|
||||
alldisks = {}
|
||||
for disk in alldisks:
|
||||
try:
|
||||
disks.append(DiskInfo(disk, alldisks[disk]))
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
with open('/tmp/storagecfg', 'w') as sc:
|
||||
sc.write(f'clearpart --all --drives={nd[0]} --overwritevmfs\n')
|
||||
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')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -45,7 +45,10 @@ try:
|
||||
cfg['ipv4_gateway'] = ncfg['ipv4_gateway']
|
||||
except Exception:
|
||||
pass
|
||||
netline = 'network --hostname={0} --bootproto={1}'.format(nodename, cfg['ipv4_method'])
|
||||
if cfg['ipv4_method'] == 'static':
|
||||
netline = 'network --hostname={0} --bootproto={1}'.format(nodename, cfg['ipv4_method'])
|
||||
else:
|
||||
netline = 'network --bootproto=dhcp'
|
||||
if vmnic:
|
||||
netline += ' --device={0}'.format(vmnic)
|
||||
if cfg['ipv4_method'] == 'static':
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#!/bin/sh
|
||||
mv /etc/confluent/confluent.deploycfg /etc/confluent/confluent.newdeploycfg
|
||||
/opt/confluent/bin/apiclient /confluent-public/os/$profile/scripts/makeksnet >> /tmp/makeksnet
|
||||
/opt/confluent/bin/apiclient /confluent-public/os/$profile/scripts/getinstalldisk >> /tmp/getinstalldisk
|
||||
mv /etc/confluent/confluent.newdeploycfg /etc/confluent/confluent.deploycfg
|
||||
chmod +x /tmp/makeksnet
|
||||
/tmp/makeksnet > /tmp/ksnet
|
||||
localcli system hostname set --host $node
|
||||
python3 /tmp/getinstalldisk
|
||||
rootpw=$(grep ^rootpassword: /etc/confluent/confluent.deploycfg|sed -e 's/^rootpassword: //')
|
||||
echo rootpw --iscrypted $rootpw > /tmp/rootpw
|
||||
export BOOT_CMDLINE=ks=/etc/confluent/ks.cfg
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
@@ -52,13 +52,40 @@ while ! grep NODENAME /custom-installation/confluent/confluent.info; do
|
||||
ipconfig -d $MYIP::$MYGW:$MYNM::$NIC
|
||||
echo $NIC > /tmp/autodetectnic
|
||||
else
|
||||
configure_networking
|
||||
for dsrv in $deploysrvs; do
|
||||
if openssl s_client -connect $dsrv:443 > /dev/null 2>&1; then
|
||||
deploysrvs=$dsrv
|
||||
break
|
||||
rmmod cdc_ether 2> /dev/null
|
||||
while [ ! -f /run/confirmednic ]; do
|
||||
ALLNETDEVS=$(ip a|grep LOWER_UP|grep MULTICAST|awk '{print $2}'|sed -e s/://)
|
||||
rm -rf /run/net* /run/dhcpcd /var/lib/dhcpcd
|
||||
for dev in $(ip a|grep MULTICAST|awk '{print $2}'|sed -e s/://); do
|
||||
ip a flush $dev
|
||||
echo 1 > /proc/sys/net/ipv6/conf/$dev/addr_gen_mode
|
||||
echo 0 > /proc/sys/net/ipv6/conf/$dev/addr_gen_mode
|
||||
done
|
||||
unset DEVICE DEVICE6 IP IP6 dev
|
||||
[ -z "$1" ] || DEVICE=$1
|
||||
shift
|
||||
configure_networking
|
||||
echo $DEVICE > /tmp/autodetectnic
|
||||
for dsrv in $deploysrvs; do
|
||||
(if openssl s_client -connect $dsrv:443 > /dev/null 2>&1; then
|
||||
echo $dsrv > /run/confirmednic
|
||||
break
|
||||
fi) &
|
||||
chkpid=$!
|
||||
( sleep 10 && kill $chkpid ) &
|
||||
timeoutpid=$!
|
||||
wait $chkpid
|
||||
kill $timeoutpid 2> /dev/null
|
||||
unset chkpid timeoutpid
|
||||
done
|
||||
if [ ! -f /run/confirmednic ]; then
|
||||
echo "No connectivity to deployment servers, retrying..."
|
||||
[ -z "$1" ] && set -- $ALLNETDEVS
|
||||
fi
|
||||
done
|
||||
deploysrvs=$(cat /run/confirmednic)
|
||||
rm /run/confirmednic
|
||||
|
||||
fi
|
||||
MGR=$deploysrvs
|
||||
NODENAME=$(grep ^nodename: /tmp/idntmnt/cnflnt.yml | awk '{print $2}')
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
@@ -53,13 +53,40 @@ while ! grep NODENAME /custom-installation/confluent/confluent.info; do
|
||||
ipconfig -d $MYIP::$MYGW:$MYNM::$NIC
|
||||
echo $NIC > /tmp/autodetectnic
|
||||
else
|
||||
configure_networking
|
||||
for dsrv in $deploysrvs; do
|
||||
if openssl s_client -connect $dsrv:443 > /dev/null 2>&1; then
|
||||
deploysrvs=$dsrv
|
||||
break
|
||||
rmmod cdc_ether 2> /dev/null
|
||||
while [ ! -f /run/confirmednic ]; do
|
||||
ALLNETDEVS=$(ip a|grep LOWER_UP|grep MULTICAST|awk '{print $2}'|sed -e s/://)
|
||||
|
||||
rm -rf /run/net* /run/dhcpcd /var/lib/dhcpcd
|
||||
for dev in $(ip a|grep MULTICAST|awk '{print $2}'|sed -e s/://); do
|
||||
ip a flush $dev
|
||||
echo 1 > /proc/sys/net/ipv6/conf/$dev/addr_gen_mode
|
||||
echo 0 > /proc/sys/net/ipv6/conf/$dev/addr_gen_mode
|
||||
done
|
||||
unset DEVICE DEVICE6 IP IP6 dev
|
||||
[ -z "$1" ] || DEVICE=$1
|
||||
shift
|
||||
configure_networking
|
||||
echo $DEVICE > /tmp/autodetectnic
|
||||
for dsrv in $deploysrvs; do
|
||||
(if openssl s_client -connect $dsrv:443 > /dev/null 2>&1; then
|
||||
echo $dsrv > /run/confirmednic
|
||||
break
|
||||
fi) &
|
||||
chkpid=$!
|
||||
( sleep 10 && kill $chkpid ) &
|
||||
timeoutpid=$!
|
||||
wait $chkpid
|
||||
kill $timeoutpid 2> /dev/null
|
||||
unset chkpid timeoutpid
|
||||
done
|
||||
if [ ! -f /run/confirmednic ]; then
|
||||
echo "No connectivity to deployment servers, retrying..."
|
||||
[ -z "$1" ] && set -- $ALLNETDEVS
|
||||
fi
|
||||
done
|
||||
deploysrvs=$(cat /run/confirmednic)
|
||||
rm /run/confirmednic
|
||||
fi
|
||||
MGR=$deploysrvs
|
||||
NODENAME=$(grep ^nodename: /tmp/idntmnt/cnflnt.yml | awk '{print $2}')
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
#!/bin/sh
|
||||
sed -i 's/label: ubuntu/label: Ubuntu/' $2/profile.yaml && \
|
||||
ln -s $1/casper/vmlinuz $2/boot/kernel && \
|
||||
ln -s $1/casper/initrd $2/boot/initramfs/distribution && \
|
||||
mkdir -p $2/boot/efi/boot && \
|
||||
#!/bin/bash
|
||||
set -e
|
||||
sed -i 's/label: ubuntu/label: Ubuntu/' $2/profile.yaml
|
||||
#if [ -e $1/casper/hwe-vmlinuz ]; then
|
||||
# ln -s $1/casper/hwe-vmlinuz $2/boot/kernel
|
||||
#else
|
||||
ln -s $1/casper/vmlinuz $2/boot/kernel
|
||||
#fi
|
||||
#if [ -e $1/casper/hwe-initrd ]; then
|
||||
# ln -s $1/casper/hwe-initrd $2/boot/initramfs/distribution
|
||||
#else
|
||||
ln -s $1/casper/initrd $2/boot/initramfs/distribution
|
||||
#fi
|
||||
mkdir -p $2/boot/efi/boot
|
||||
if [ -d $1/EFI/boot/ ]; then
|
||||
ln -s $1/EFI/boot/* $2/boot/efi/boot
|
||||
elif [ -d $1/efi/boot/ ]; then
|
||||
ln -s $1/efi/boot/* $2/boot/efi/boot
|
||||
else
|
||||
echo "Unrecogrized boot contents in media" >&2
|
||||
echo "Unrecognized boot contents in media" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
@@ -2,20 +2,23 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"net"
|
||||
"net/http"
|
||||
"crypto/x509"
|
||||
"crypto/tls"
|
||||
"os"
|
||||
"strings"
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ApiClient struct {
|
||||
server string
|
||||
server string
|
||||
urlserver string
|
||||
apikey string
|
||||
nodename string
|
||||
apikey string
|
||||
nodename string
|
||||
webclient *http.Client
|
||||
}
|
||||
|
||||
@@ -24,7 +27,7 @@ func NewApiClient(cafile string, keyfile string, nodename string, server string)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cacerts := x509.NewCertPool()
|
||||
cacerts := x509.NewCertPool()
|
||||
cacerts.AppendCertsFromPEM(currcacerts)
|
||||
apikey := []byte("")
|
||||
if keyfile != "" {
|
||||
@@ -32,7 +35,7 @@ func NewApiClient(cafile string, keyfile string, nodename string, server string)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if apikey[len(apikey) - 1] == 0xa {
|
||||
if apikey[len(apikey)-1] == 0xa {
|
||||
apikey = apikey[:len(apikey)-1]
|
||||
}
|
||||
}
|
||||
@@ -40,7 +43,9 @@ func NewApiClient(cafile string, keyfile string, nodename string, server string)
|
||||
cinfo, err := os.ReadFile("/etc/confluent/confliuent.info")
|
||||
if err != nil {
|
||||
nodename, err = os.Hostname()
|
||||
if err != nil { return nil, err }
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
cinfolines := bytes.Split(cinfo, []byte("\n"))
|
||||
if bytes.Contains(cinfolines[0], []byte("NODENAME")) {
|
||||
@@ -48,6 +53,20 @@ func NewApiClient(cafile string, keyfile string, nodename string, server string)
|
||||
nodename = string(cnodebytes[0])
|
||||
}
|
||||
}
|
||||
// Test connectivity with up to 3 retries
|
||||
var conn net.Conn
|
||||
for i := 0; i < 3; i++ {
|
||||
conn, err = net.Dial("tcp", net.JoinHostPort(server, "443"))
|
||||
if err == nil {
|
||||
conn.Close()
|
||||
break
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
fmt.Print("Connection attempt failed, retrying...\n")
|
||||
if i == 2 {
|
||||
return nil, fmt.Errorf("failed to connect after 3 attempts: %v", err)
|
||||
}
|
||||
}
|
||||
urlserver := server
|
||||
if strings.Contains(server, ":") {
|
||||
if strings.Contains(server, "%") && !strings.Contains(server, "%25") {
|
||||
@@ -58,10 +77,11 @@ func NewApiClient(cafile string, keyfile string, nodename string, server string)
|
||||
server = server[:strings.Index(server, "%")]
|
||||
}
|
||||
}
|
||||
|
||||
webclient := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: cacerts,
|
||||
RootCAs: cacerts,
|
||||
ServerName: server,
|
||||
},
|
||||
},
|
||||
@@ -70,34 +90,42 @@ func NewApiClient(cafile string, keyfile string, nodename string, server string)
|
||||
return &vc, nil
|
||||
}
|
||||
|
||||
func (apiclient *ApiClient) RegisterKey(crypted string, hmac string) (error) {
|
||||
func (apiclient *ApiClient) RegisterKey(crypted string, hmac string) error {
|
||||
cryptbytes := []byte(crypted)
|
||||
cryptbuffer := bytes.NewBuffer(cryptbytes)
|
||||
_, err := apiclient.request("/confluent-api/self/registerapikey", "", cryptbuffer, "", hmac)
|
||||
return err
|
||||
}
|
||||
|
||||
func (apiclient *ApiClient) Fetch(url string, outputfile string, mime string, body io.Reader) (error) {
|
||||
func (apiclient *ApiClient) Fetch(url string, outputfile string, mime string, body io.Reader) error {
|
||||
outp, err := os.Create(outputfile)
|
||||
if err != nil { return err }
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer outp.Close()
|
||||
rsp, err := apiclient.request(url, mime, body, "", "")
|
||||
if err != nil { return err }
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(outp, rsp)
|
||||
return err
|
||||
}
|
||||
|
||||
func (apiclient *ApiClient) GrabText(url string, mime string, body io.Reader) (string, error){
|
||||
func (apiclient *ApiClient) GrabText(url string, mime string, body io.Reader) (string, error) {
|
||||
rsp, err := apiclient.request(url, mime, body, "", "")
|
||||
if err != nil { return "", err }
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
rspdata, err := io.ReadAll(rsp)
|
||||
if err != nil { return "", err }
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
rsptxt := string(rspdata)
|
||||
return rsptxt, nil
|
||||
}
|
||||
|
||||
func (apiclient *ApiClient) request(url string, mime string, body io.Reader, method string, hmac string) (io.ReadCloser, error) {
|
||||
if ! strings.Contains(url, "https://") {
|
||||
if !strings.Contains(url, "https://") {
|
||||
url = fmt.Sprintf("https://%s%s", apiclient.urlserver, url)
|
||||
}
|
||||
if method == "" {
|
||||
@@ -114,8 +142,12 @@ func (apiclient *ApiClient) request(url string, mime string, body io.Reader, met
|
||||
} else {
|
||||
rq, err = http.NewRequest(method, url, body)
|
||||
}
|
||||
if err != nil { return nil, err }
|
||||
if (mime != "") { rq.Header.Set("Accept", mime) }
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if mime != "" {
|
||||
rq.Header.Set("Accept", mime)
|
||||
}
|
||||
rq.Header.Set("CONFLUENT_NODENAME", apiclient.nodename)
|
||||
if len(hmac) > 0 {
|
||||
rq.Header.Set("CONFLUENT_CRYPTHMAC", hmac)
|
||||
@@ -124,11 +156,12 @@ func (apiclient *ApiClient) request(url string, mime string, body io.Reader, met
|
||||
rq.Header.Set("CONFLUENT_APIKEY", apiclient.apikey)
|
||||
}
|
||||
rsp, err := apiclient.webclient.Do(rq)
|
||||
if err != nil { return nil, err }
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if rsp.StatusCode >= 300 {
|
||||
err = errors.New(rsp.Status)
|
||||
return nil, err
|
||||
}
|
||||
return rsp.Body, err
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
#include <sys/mount.h>
|
||||
#define __USE_GNU
|
||||
#include <sched.h>
|
||||
#include <string.h>
|
||||
int main(int argc, char* argv[]) {
|
||||
unshare(CLONE_NEWNS);
|
||||
if (argc < 2 || strcmp(argv[1], "-s")) {
|
||||
unshare(CLONE_NEWNS);
|
||||
}
|
||||
mount("/dev", "/sysroot/dev", NULL, MS_MOVE, NULL);
|
||||
mount("/proc", "/sysroot/proc", NULL, MS_MOVE, NULL);
|
||||
mount("/sys", "/sysroot/sys", NULL, MS_MOVE, NULL);
|
||||
|
||||
@@ -27,6 +27,16 @@ import signal
|
||||
import confluent.collective.manager as collective
|
||||
import confluent.noderange as noderange
|
||||
|
||||
def check_sysctl_tuning():
|
||||
with open('/proc/sys/net/ipv4/tcp_sack', 'r') as f:
|
||||
value = f.read().strip()
|
||||
if value == '1':
|
||||
print('OK')
|
||||
return
|
||||
else:
|
||||
emprint('TCP SACK is disabled, network operations to BMCs may be particularly impacted, including firmware updates and virtual media')
|
||||
|
||||
|
||||
def check_neigh_overflow():
|
||||
dmesgout = subprocess.check_output(['dmesg'])
|
||||
if b'_cache: neighbor table overflow!' in subprocess.check_output(['dmesg']):
|
||||
@@ -216,6 +226,8 @@ if __name__ == '__main__':
|
||||
emprint('ARP/Neighbor table problem detected, evaluate and increase net.ipv*.neigh.default.gc_thresh*')
|
||||
else:
|
||||
print('OK')
|
||||
fprint('Checking sysctl tunables: ')
|
||||
check_sysctl_tuning()
|
||||
fprint('TFTP Status: ')
|
||||
if tftp_works():
|
||||
print('OK')
|
||||
@@ -223,7 +235,26 @@ if __name__ == '__main__':
|
||||
emprint('TFTP failure, PXE will not work, though media and HTTP boot can still work. (Example resolution: osdeploy initialize -p)')
|
||||
fprint('SSH root user public key: ')
|
||||
if glob.glob('/var/lib/confluent/public/site/ssh/*.rootpubkey'):
|
||||
print('OK')
|
||||
if not glob.glob('/root/.ssh/id_*.pub'):
|
||||
emprint('No SSH keys for root user, passwordless SSH from managers to nodes may not work (Example resolution: osdeploy initialize -u)')
|
||||
for userpub in glob.glob('/root/.ssh/id_*.pub'):
|
||||
with open(userpub) as f:
|
||||
pubkey = f.read().strip()
|
||||
for sitepubkey in glob.glob('/var/lib/confluent/public/site/ssh/*.rootpubkey'):
|
||||
with open(sitepubkey) as sf:
|
||||
spubkey = sf.read().strip()
|
||||
for keyline in spubkey.split('\n'):
|
||||
if keyline == pubkey:
|
||||
print('OK')
|
||||
break
|
||||
else:
|
||||
continue
|
||||
break
|
||||
else:
|
||||
continue
|
||||
break
|
||||
else:
|
||||
emprint('No matching public key found for root user (Example resolution: osdeploy initialize -u)')
|
||||
else:
|
||||
emprint('No trusted ssh keys for root user, passwordless SSH from managers to nodes may not work (Example resolution: osdeploy initialize -u)')
|
||||
if sshutil.sshver() > 7.6:
|
||||
@@ -254,6 +285,8 @@ if __name__ == '__main__':
|
||||
emprint('Permissions incorrect on /etc/confluent/ssh/automation (Example resolution: chmod 600 /etc/confluent/ssh/automation)')
|
||||
else:
|
||||
emprint('Failed to load confluent automation key, syncfiles and profile ansible plays will not work (Example resolution: osdeploy initialize -a)')
|
||||
if sshutil.agent_pid:
|
||||
os.kill(int(sshutil.agent_pid), signal.SIGTERM)
|
||||
sys.exit(0)
|
||||
fprint('Checking for blocked insecure boot: ')
|
||||
if insecure_boot_attempts():
|
||||
@@ -402,7 +435,9 @@ if __name__ == '__main__':
|
||||
else:
|
||||
emprint('Unknown error attempting confluent automation ssh:')
|
||||
sys.stderr.buffer.write(srun.stderr)
|
||||
os.kill(int(sshutil.agent_pid), signal.SIGTERM)
|
||||
if sshutil.agent_pid:
|
||||
os.kill(int(sshutil.agent_pid), signal.SIGTERM)
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("Skipping node checks, no node specified (Example: confluent_selfcheck -n n1)")
|
||||
# possible checks:
|
||||
|
||||
@@ -53,22 +53,61 @@ def get_ip_addresses():
|
||||
def check_apache_config(path):
|
||||
keypath = None
|
||||
certpath = None
|
||||
chainpath = None
|
||||
with open(path, 'r') as openf:
|
||||
webconf = openf.read()
|
||||
insection = False
|
||||
# we always manipulate the first VirtualHost section
|
||||
# since we are managing IP based SANs, then SNI
|
||||
# can never match anything but the first VirtualHost
|
||||
for line in webconf.split('\n'):
|
||||
line = line.strip()
|
||||
line = line.split('#')[0]
|
||||
if line.startswith('SSLCertificateFile'):
|
||||
_, certpath = line.split(None, 1)
|
||||
if line.startswith('SSLCertificateKeyFile'):
|
||||
if not certpath and line.startswith('SSLCertificateFile'):
|
||||
insection = True
|
||||
if not certpath:
|
||||
_, certpath = line.split(None, 1)
|
||||
if not keypath and line.startswith('SSLCertificateKeyFile'):
|
||||
insection = True
|
||||
_, keypath = line.split(None, 1)
|
||||
if not chainpath and line.startswith('SSLCertificateChainFile'):
|
||||
insection = True
|
||||
_, chainpath = line.split(None, 1)
|
||||
if insection and line.startswith('</VirtualHost>'):
|
||||
break
|
||||
return keypath, certpath, chainpath
|
||||
|
||||
def check_nginx_config(path):
|
||||
keypath = None
|
||||
certpath = None
|
||||
# again, we only care about the first server section
|
||||
# since IP won't trigger SNI matches down the configuration
|
||||
with open(path, 'r') as openf:
|
||||
webconf = openf.read()
|
||||
for line in webconf.split('\n'):
|
||||
if keypath and certpath:
|
||||
break
|
||||
line = line.strip()
|
||||
line = line.split('#')[0]
|
||||
for segment in line.split(';'):
|
||||
if not certpath and segment.startswith('ssl_certificate'):
|
||||
_, certpath = segment.split(None, 1)
|
||||
if not keypath and segment.startswith('ssl_certificate_key'):
|
||||
_, keypath = segment.split(None, 1)
|
||||
if keypath:
|
||||
keypath = keypath.strip('"')
|
||||
if certpath:
|
||||
certpath = certpath.strip('"')
|
||||
return keypath, certpath
|
||||
|
||||
def get_certificate_paths():
|
||||
keypath = None
|
||||
certpath = None
|
||||
chainpath = None
|
||||
ngkeypath = None
|
||||
ngbundlepath = None
|
||||
if os.path.exists('/etc/httpd/conf.d/ssl.conf'): # redhat way
|
||||
keypath, certpath = check_apache_config('/etc/httpd/conf.d/ssl.conf')
|
||||
keypath, certpath, chainpath = check_apache_config('/etc/httpd/conf.d/ssl.conf')
|
||||
if not keypath and os.path.exists('/etc/apache2'): # suse way
|
||||
for currpath, _, files in os.walk('/etc/apache2'):
|
||||
for fname in files:
|
||||
@@ -77,11 +116,32 @@ def get_certificate_paths():
|
||||
kploc = check_apache_config(os.path.join(currpath,
|
||||
fname))
|
||||
if keypath and kploc[0] and keypath != kploc[0]:
|
||||
return None, None # Ambiguous...
|
||||
return {'error': 'Ambiguous...'}
|
||||
if kploc[0]:
|
||||
keypath, certpath = kploc
|
||||
|
||||
return keypath, certpath
|
||||
keypath, certpath, chainpath = kploc
|
||||
if os.path.exists('/etc/nginx'): # nginx way
|
||||
for currpath, _, files in os.walk('/etc/nginx'):
|
||||
if ngkeypath:
|
||||
break
|
||||
for fname in files:
|
||||
if not fname.endswith('.conf'):
|
||||
continue
|
||||
ngkeypath, ngbundlepath = check_nginx_config(os.path.join(currpath,
|
||||
fname))
|
||||
if ngkeypath:
|
||||
break
|
||||
tlsmateriallocation = {}
|
||||
if keypath:
|
||||
tlsmateriallocation.setdefault('keys', []).append(keypath)
|
||||
if ngkeypath:
|
||||
tlsmateriallocation.setdefault('keys', []).append(ngkeypath)
|
||||
if certpath:
|
||||
tlsmateriallocation.setdefault('certs', []).append(certpath)
|
||||
if chainpath:
|
||||
tlsmateriallocation.setdefault('chains', []).append(chainpath)
|
||||
if ngbundlepath:
|
||||
tlsmateriallocation.setdefault('bundles', []).append(ngbundlepath)
|
||||
return tlsmateriallocation
|
||||
|
||||
def assure_tls_ca():
|
||||
keyout, certout = ('/etc/confluent/tls/cakey.pem', '/etc/confluent/tls/cacert.pem')
|
||||
@@ -208,8 +268,12 @@ def create_simple_ca(keyout, certout):
|
||||
|
||||
def create_certificate(keyout=None, certout=None, csrout=None):
|
||||
if not keyout:
|
||||
keyout, certout = get_certificate_paths()
|
||||
if not keyout:
|
||||
tlsmateriallocation = get_certificate_paths()
|
||||
keyout = tlsmateriallocation.get('keys', [None])[0]
|
||||
certout = tlsmateriallocation.get('certs', [None])[0]
|
||||
if not certout:
|
||||
certout = tlsmateriallocation.get('bundles', [None])[0]
|
||||
if not keyout or not certout:
|
||||
raise Exception('Unable to locate TLS certificate path automatically')
|
||||
assure_tls_ca()
|
||||
shortname = socket.gethostname().split('.')[0]
|
||||
@@ -291,6 +355,29 @@ def create_certificate(keyout=None, certout=None, csrout=None):
|
||||
'-startdate', '19700101010101Z', '-enddate', '21000101010101Z',
|
||||
'-extfile', extconfig
|
||||
])
|
||||
for keycopy in tlsmateriallocation.get('keys', []):
|
||||
if keycopy != keyout:
|
||||
shutil.copy2(keyout, keycopy)
|
||||
for certcopy in tlsmateriallocation.get('certs', []):
|
||||
if certcopy != certout:
|
||||
shutil.copy2(certout, certcopy)
|
||||
cacert = None
|
||||
with open('/etc/confluent/tls/cacert.pem', 'rb') as cacertfile:
|
||||
cacert = cacertfile.read()
|
||||
for bundlecopy in tlsmateriallocation.get('bundles', []):
|
||||
if bundlecopy != certout:
|
||||
shutil.copy2(certout, bundlecopy)
|
||||
with open(bundlecopy, 'ab') as bundlefile:
|
||||
bundlefile.write(b'\n')
|
||||
bundlefile.write(cacert)
|
||||
for chaincopy in tlsmateriallocation.get('chains', []):
|
||||
if chaincopy != certout:
|
||||
with open(chaincopy, 'wb') as chainfile:
|
||||
chainfile.write(cacert)
|
||||
else:
|
||||
with open(chaincopy, 'ab') as chainfile:
|
||||
chainfile.write(b'\n')
|
||||
chainfile.write(cacert)
|
||||
finally:
|
||||
os.remove(tmpconfig)
|
||||
if needcsr:
|
||||
|
||||
@@ -215,6 +215,9 @@ node = {
|
||||
'Using this requires that collective members be '
|
||||
'defined as nodes for noderange expansion')
|
||||
},
|
||||
'deployment.client_ip': {
|
||||
'description': ('Client IP used when most recently reporting state.')
|
||||
},
|
||||
'deployment.lock': {
|
||||
'description': ('Indicates whether deployment actions should be impeded. '
|
||||
'If locked, it indicates that a pending profile should not be applied. '
|
||||
@@ -376,7 +379,7 @@ node = {
|
||||
'the managed node. If not specified, then console '
|
||||
'is disabled. "ipmi" should be specified for most '
|
||||
'systems if console is desired.'),
|
||||
'validvalues': ('ssh', 'ipmi', 'openbmc', 'tsmsol', 'vcenter'),
|
||||
'validvalues': ('ssh', 'ipmi', 'openbmc', 'tsmsol', 'vcenter', 'proxmox'),
|
||||
},
|
||||
# 'virtualization.host': {
|
||||
# 'description': ('Hypervisor where this node does/should reside'),
|
||||
@@ -478,6 +481,9 @@ node = {
|
||||
'This would be the default name per the deployed OS and can be a comma delimited list to denote members of '
|
||||
'a team or a single interface for VLAN/PKEY connections.'
|
||||
},
|
||||
'net.mtu': {
|
||||
'description': 'MTU to apply to this connection',
|
||||
},
|
||||
'net.vlan_id': {
|
||||
'description': 'Ethernet VLAN or InfiniBand PKEY to use for this connection. '
|
||||
'Specify the parent device using net.interface_names.'
|
||||
|
||||
@@ -300,6 +300,10 @@ def _init_core():
|
||||
'default': 'ipmi',
|
||||
}),
|
||||
},
|
||||
'certificate_authorities': PluginCollection({
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'ipmi',
|
||||
}),
|
||||
'clear': PluginRoute({
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'ipmi',
|
||||
@@ -498,6 +502,22 @@ def _init_core():
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'ipmi',
|
||||
}),
|
||||
'core': PluginCollection({
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'ipmi',
|
||||
}),
|
||||
'adapters': PluginCollection({
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'ipmi',
|
||||
}),
|
||||
'disks': PluginCollection({
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'ipmi',
|
||||
}),
|
||||
'misc': PluginCollection({
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'ipmi',
|
||||
}),
|
||||
'updatestatus': PluginRoute({
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'ipmi',
|
||||
|
||||
@@ -43,6 +43,8 @@ libc = ctypes.CDLL(ctypes.util.find_library('c'))
|
||||
|
||||
|
||||
def address_is_somewhat_trusted(address, nodename, cfm):
|
||||
if netutil.ip_on_same_subnet(address.split('%')[0], 'fe80::', 64):
|
||||
return True
|
||||
if netutil.address_is_local(address):
|
||||
return True
|
||||
authnets = cfm.get_node_attributes(nodename, 'trusted.subnets')
|
||||
|
||||
@@ -316,6 +316,8 @@ def list_matching_nodes(criteria):
|
||||
retnodes = []
|
||||
for node in known_nodes:
|
||||
for mac in known_nodes[node]:
|
||||
if mac not in known_info:
|
||||
continue
|
||||
info = known_info[mac]
|
||||
if _info_matches(info, criteria):
|
||||
retnodes.append(node)
|
||||
@@ -613,7 +615,11 @@ def handle_api_request(configmanager, inputdata, operation, pathcomponents):
|
||||
return [msg.AssignedResource(inputdata['node'])]
|
||||
elif operation == 'delete':
|
||||
mac = _get_mac_from_query(pathcomponents)
|
||||
del known_info[mac]
|
||||
for node in known_nodes:
|
||||
if mac in known_nodes[node]:
|
||||
del known_nodes[node][mac]
|
||||
if mac in known_info:
|
||||
del known_info[mac]
|
||||
return [msg.DeletedResource(mac)]
|
||||
raise exc.NotImplementedException(
|
||||
'Unable to {0} to {1}'.format(operation, '/'.join(pathcomponents)))
|
||||
@@ -1356,7 +1362,8 @@ def eval_node(cfg, handler, info, nodename, manual=False):
|
||||
errorstr = 'The detected node {0} was detected using switch, ' \
|
||||
'however the relevant port has too many macs learned ' \
|
||||
'for this type of device ({1}) to be discovered by ' \
|
||||
'switch.'.format(nodename, handler.devname)
|
||||
'switch. If this should be an enclosure, make sure there are ' \
|
||||
'defined nodes for the enclosure'.format(nodename, handler.devname)
|
||||
log.log({'error': errorstr})
|
||||
return
|
||||
if not discover_node(cfg, handler, info, nodename, manual):
|
||||
|
||||
@@ -68,14 +68,19 @@ class NodeHandler(generic.NodeHandler):
|
||||
self._srvroot = srvroot
|
||||
return self._srvroot
|
||||
|
||||
def get_manager_url(self, wc):
|
||||
mgrs = self.srvroot(wc).get('Managers', {}).get('@odata.id', None)
|
||||
if not mgrs:
|
||||
raise Exception("No Managers resource on BMC")
|
||||
rsp = wc.grab_json_response(mgrs)
|
||||
if len(rsp.get('Members', [])) != 1:
|
||||
raise Exception("Can not handle multiple Managers")
|
||||
mgrurl = rsp['Members'][0]['@odata.id']
|
||||
return mgrurl
|
||||
|
||||
def mgrinfo(self, wc):
|
||||
if not self._mgrinfo:
|
||||
mgrs = self.srvroot(wc)['Managers']['@odata.id']
|
||||
rsp = wc.grab_json_response(mgrs)
|
||||
if len(rsp['Members']) != 1:
|
||||
raise Exception("Can not handle multiple Managers")
|
||||
mgrurl = rsp['Members'][0]['@odata.id']
|
||||
self._mgrinfo = wc.grab_json_response(mgrurl)
|
||||
self._mgrinfo = wc.grab_json_response(self.get_manager_url(wc))
|
||||
return self._mgrinfo
|
||||
|
||||
|
||||
@@ -281,7 +286,7 @@ class NodeHandler(generic.NodeHandler):
|
||||
compip = compip.split('%')[0]
|
||||
ipkey = 'IPv6Addresses'
|
||||
else:
|
||||
ipkey = 'IPv6Addresses'
|
||||
ipkey = 'IPv4Addresses'
|
||||
actualnic = None
|
||||
for curractnic in actualnics:
|
||||
currnicinfo = wc.grab_json_response(curractnic)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import base64
|
||||
import codecs
|
||||
import confluent.discovery.handlers.imm as immhandler
|
||||
import confluent.discovery.handlers.xcc3 as xcc3handler
|
||||
import confluent.exceptions as exc
|
||||
import confluent.netutil as netutil
|
||||
import confluent.util as util
|
||||
@@ -477,12 +478,19 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
tmpaccount = None
|
||||
while status != 200:
|
||||
tries -= 1
|
||||
rsp, status = wc.grab_json_response_with_status(
|
||||
'/redfish/v1/AccountService/Accounts/{0}'.format(uid))
|
||||
if status >= 500:
|
||||
if tries < 0:
|
||||
raise Exception('Redfish account management failure')
|
||||
eventlet.sleep(30)
|
||||
continue
|
||||
rsp, status = wc.grab_json_response_with_status(
|
||||
'/redfish/v1/AccountService/Accounts/{0}'.format(uid),
|
||||
{'UserName': username}, method='PATCH')
|
||||
if status != 200:
|
||||
rsp = json.loads(rsp)
|
||||
if rsp.get('error', {}).get('code', 'Unknown') in ('Base.1.8.GeneralError', 'Base.1.12.GeneralError', 'Base.1.14.GeneralError', 'Base.1.18.GeneralError'):
|
||||
if rsp.get('error', {}).get('code', 'Unknown') in ('Base.1.8.GeneralError', 'Base.1.12.GeneralError', 'Base.1.14.GeneralError', 'Base.1.18.GeneralError', 'Base.1.21.GeneralError'):
|
||||
if tries:
|
||||
eventlet.sleep(4)
|
||||
elif tmpaccount:
|
||||
@@ -514,7 +522,7 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
if userent['users_user_name'] == user:
|
||||
curruser = userent
|
||||
break
|
||||
if curruser.get('users_pass_is_sha256', 0):
|
||||
if curruser and curruser.get('users_pass_is_sha256', 0):
|
||||
self._wc = None
|
||||
wc = self.wc
|
||||
nwc = wc.dupe()
|
||||
@@ -708,6 +716,13 @@ def remote_nodecfg(nodename, cfm):
|
||||
raise Exception('Cannot remote configure a system without known '
|
||||
'address')
|
||||
info = {'addresses': [ipaddr]}
|
||||
nh = NodeHandler(info, cfm)
|
||||
ipaddr = ipaddr[0]
|
||||
wc = webclient.SecureHTTPConnection(
|
||||
ipaddr, 443, verifycallback=lambda x: True)
|
||||
rsp = wc.grab_json_response('/DeviceDescription.json')
|
||||
if isinstance(rsp, list):
|
||||
nh = NodeHandler(info, cfm)
|
||||
else:
|
||||
nh = xcc3handler.NodeHandler(info, cfm)
|
||||
nh.config(nodename)
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@ class NodeHandler(redfishbmc.NodeHandler):
|
||||
def get_firmware_default_account_info(self):
|
||||
return ('USERID', 'PASSW0RD')
|
||||
|
||||
def get_manager_url(self, wc):
|
||||
return '/redfish/v1/Managers/1'
|
||||
|
||||
def scan(self):
|
||||
ip, port = self.get_web_port_and_ip()
|
||||
c = webclient.SecureHTTPConnection(ip, port,
|
||||
|
||||
@@ -844,7 +844,7 @@ def reply_dhcp4(node, info, packet, cfg, reqview, httpboot, cfd, profile, sock=N
|
||||
log.log({'error': 'Unable to serve {0} due to duplicated address between node and interface index "{}"'.format(node, info['netinfo']['ifidx'])})
|
||||
return
|
||||
can302 = True
|
||||
if httpboot:
|
||||
if isboot and httpboot:
|
||||
proto = 'https' if insecuremode == 'never' else 'http'
|
||||
bootfile = '{0}://{1}/confluent-public/os/{2}/boot.img'.format(
|
||||
proto, myipn, profile
|
||||
@@ -865,13 +865,16 @@ def reply_dhcp4(node, info, packet, cfg, reqview, httpboot, cfd, profile, sock=N
|
||||
node, profile, len(bootfile) - 127)})
|
||||
return
|
||||
repview[108:108 + len(bootfile)] = bootfile
|
||||
elif info.get('architecture', None) == 'uefi-aarch64' and packet.get(77, None) == b'iPXE':
|
||||
if not profile:
|
||||
profile, stgprofile = get_deployment_profile(node, cfg)
|
||||
if not profile:
|
||||
log.log({'info': 'No pending profile for {0}, skipping proxyDHCP eply'.format(node)})
|
||||
return
|
||||
bootfile = 'http://{0}/confluent-public/os/{1}/boot.ipxe'.format(myipn, profile).encode('utf8')
|
||||
elif isboot and info.get('architecture', None) == 'uefi-aarch64':
|
||||
if packet.get(77, None) == b'iPXE':
|
||||
if not profile:
|
||||
profile, stgprofile = get_deployment_profile(node, cfg)
|
||||
if not profile:
|
||||
log.log({'info': 'No pending profile for {0}, skipping proxyDHCP eply'.format(node)})
|
||||
return
|
||||
bootfile = 'http://{0}/confluent-public/os/{1}/boot.ipxe'.format(myipn, profile).encode('utf8')
|
||||
else:
|
||||
bootfile = b'confluent/aarch64/ipxe.efi'
|
||||
repview[108:108 + len(bootfile)] = bootfile
|
||||
myip = myipn
|
||||
myipn = socket.inet_aton(myipn)
|
||||
|
||||
@@ -316,7 +316,7 @@ def snoop(handler, byehandler=None, protocol=None, uuidlookup=None):
|
||||
if not mac:
|
||||
continue
|
||||
_process_snoop(peer, rsp, mac, known_peers, newmacs, peerbymacaddress, byehandler, machandlers, handler)
|
||||
for mac in newmacs:
|
||||
for mac in list(newmacs):
|
||||
thehandler = machandlers.get(mac, None)
|
||||
if thehandler:
|
||||
thehandler(peerbymacaddress[mac])
|
||||
|
||||
@@ -386,14 +386,18 @@ class TimedAndSizeRotatingFileHandler(BaseRotatingHandler):
|
||||
f['time'] in t_list[:-(self.backupCount - 1)]]
|
||||
return result
|
||||
|
||||
dirContents = {}
|
||||
def initSizeRollingCount(self):
|
||||
"""
|
||||
Init the max number of log files for current time.
|
||||
"""
|
||||
dirName, baseName = os.path.split(self.textpath)
|
||||
prefix = baseName + "."
|
||||
filePaths = glob.glob(os.path.join(dirName, "%s*" % prefix))
|
||||
fileNames = [os.path.split(f)[1] for f in filePaths]
|
||||
if dirName not in self.dirContents or self.dirContents[dirName][1] < time.time():
|
||||
self.dirContents[dirName] = (os.listdir(dirName), time.time() + 5)
|
||||
matchexp = re.compile(f'^{prefix}\.\d+$')
|
||||
fileNames = [f for f in self.dirContents[dirName][0]
|
||||
if matchexp.match(f)]
|
||||
plen = len(prefix)
|
||||
for fileName in fileNames:
|
||||
suffix = fileName[plen:]
|
||||
|
||||
@@ -94,6 +94,7 @@ def _daemonize():
|
||||
|
||||
def _redirectoutput():
|
||||
os.umask(63)
|
||||
configmanager.set_global('logdirectory', _get_logdirectory())
|
||||
sys.stdout = log.Logger('stdout', buffered=False)
|
||||
sys.stderr = log.Logger('stderr', buffered=False)
|
||||
|
||||
@@ -340,3 +341,6 @@ def _get_connector_config(session):
|
||||
host = conf.get_option(session, 'bindhost')
|
||||
port = conf.get_int_option(session, 'bindport')
|
||||
return (host, port)
|
||||
|
||||
def _get_logdirectory():
|
||||
return conf.get_option('globals', 'logdirectory')
|
||||
@@ -517,6 +517,8 @@ def get_input_message(path, operation, inputdata, nodes=None, multinode=False,
|
||||
path[:4] == ['configuration', 'management_controller', 'alerts',
|
||||
'destinations'] and operation != 'retrieve'):
|
||||
return InputAlertDestination(path, nodes, inputdata, multinode)
|
||||
elif len(path) == 3 and path[:3] == ['configuration', 'management_controller', 'certificate_authorities'] and operation not in ('retrieve', 'delete'):
|
||||
return InputCertificateAuthority(path, nodes, inputdata)
|
||||
elif path == ['identify'] and operation != 'retrieve':
|
||||
return InputIdentifyMessage(path, nodes, inputdata)
|
||||
elif path == ['events', 'hardware', 'decode']:
|
||||
@@ -955,6 +957,16 @@ class ConfluentInputMessage(ConfluentMessage):
|
||||
return key in self.valid_values
|
||||
|
||||
|
||||
class InputCertificateAuthority(ConfluentInputMessage):
|
||||
keyname = 'pem'
|
||||
# anything is valid, since it is a blob of text
|
||||
|
||||
def get_pem(self, node):
|
||||
return self.inputbynode[node]
|
||||
|
||||
def is_valid_key(self, key):
|
||||
return key.strip().startswith('-----BEGIN') and '-----END' in key
|
||||
|
||||
class InputIdentImage(ConfluentInputMessage):
|
||||
keyname = 'ident_image'
|
||||
valid_values = ['create']
|
||||
@@ -1148,6 +1160,9 @@ class InputNetworkConfiguration(ConfluentInputMessage):
|
||||
if 'ipv4_gateway' not in inputdata:
|
||||
inputdata['ipv4_gateway'] = None
|
||||
|
||||
if 'vlan_id' not in inputdata:
|
||||
inputdata['vlan_id'] = None
|
||||
|
||||
if 'ipv4_configuration' in inputdata and inputdata['ipv4_configuration']:
|
||||
if inputdata['ipv4_configuration'].lower() not in ['dhcp','static']:
|
||||
raise exc.InvalidArgumentException(
|
||||
@@ -1342,6 +1357,11 @@ class ReseatResult(ConfluentChoiceMessage):
|
||||
keyname = 'reseat'
|
||||
|
||||
|
||||
class CertificateAuthority(ConfluentMessage):
|
||||
def __init__(self, node, pem, subject, san):
|
||||
self.myargs = (node, pem, subject, san)
|
||||
self.kvpairs = {node: {'pem': {'value': pem}, 'subject': {'value': subject}, 'san': {'value': san}}}
|
||||
|
||||
class PowerState(ConfluentChoiceMessage):
|
||||
valid_values = set([
|
||||
'on',
|
||||
@@ -1736,8 +1756,8 @@ class NetworkConfiguration(ConfluentMessage):
|
||||
desc = 'Network configuration'
|
||||
|
||||
def __init__(self, name=None, ipv4addr=None, ipv4gateway=None,
|
||||
ipv4cfgmethod=None, hwaddr=None, staticv6addrs=(), staticv6gateway=None):
|
||||
self.myargs = (name, ipv4addr, ipv4gateway, ipv4cfgmethod, hwaddr)
|
||||
ipv4cfgmethod=None, hwaddr=None, staticv6addrs=(), staticv6gateway=None, vlan_id=None):
|
||||
self.myargs = (name, ipv4addr, ipv4gateway, ipv4cfgmethod, hwaddr, staticv6addrs, staticv6gateway, vlan_id)
|
||||
self.notnode = name is None
|
||||
self.stripped = False
|
||||
v6addrs = ','.join(staticv6addrs)
|
||||
@@ -1748,7 +1768,8 @@ class NetworkConfiguration(ConfluentMessage):
|
||||
'ipv4_configuration': {'value': ipv4cfgmethod},
|
||||
'hw_addr': {'value': hwaddr},
|
||||
'static_v6_addresses': {'value': v6addrs},
|
||||
'static_v6_gateway': {'value': staticv6gateway}
|
||||
'static_v6_gateway': {'value': staticv6gateway},
|
||||
'vlan_id': {'value': vlan_id}
|
||||
}
|
||||
if self.notnode:
|
||||
self.kvpairs = kvpairs
|
||||
|
||||
@@ -243,6 +243,9 @@ class NetManager(object):
|
||||
vlanid = attribs.get('vlan_id', None)
|
||||
if vlanid:
|
||||
myattribs['vlan_id'] = vlanid
|
||||
mtuinfo = attribs.get('mtu', None)
|
||||
if mtuinfo:
|
||||
myattribs['mtu'] = int(mtuinfo)
|
||||
teammod = attribs.get('team_mode', None)
|
||||
if teammod:
|
||||
myattribs['team_mode'] = teammod
|
||||
@@ -497,6 +500,8 @@ def get_nic_config(configmanager, node, ip=None, mac=None, ifidx=None,
|
||||
#TODO(jjohnson2): ip address, prefix length, mac address,
|
||||
# join a bond/bridge, vlan configs, etc.
|
||||
# also other nic criteria, physical location, driver and index...
|
||||
if not onlyfamily:
|
||||
onlyfamily = 0
|
||||
clientfam = None
|
||||
clientipn = None
|
||||
serverfam = None
|
||||
@@ -527,11 +532,13 @@ def get_nic_config(configmanager, node, ip=None, mac=None, ifidx=None,
|
||||
bmc6 = None
|
||||
if bmc:
|
||||
try:
|
||||
bmc4 = socket.getaddrinfo(bmc, 0, socket.AF_INET, socket.SOCK_DGRAM)[0][-1][0]
|
||||
if onlyfamily in (0, socket.AF_INET):
|
||||
bmc4 = socket.getaddrinfo(bmc, 0, socket.AF_INET, socket.SOCK_DGRAM)[0][-1][0]
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
bmc6 = socket.getaddrinfo(bmc, 0, socket.AF_INET6, socket.SOCK_DGRAM)[0][-1][0]
|
||||
if onlyfamily in (0, socket.AF_INET6):
|
||||
bmc6 = socket.getaddrinfo(bmc, 0, socket.AF_INET6, socket.SOCK_DGRAM)[0][-1][0]
|
||||
except Exception:
|
||||
pass
|
||||
cfgbyname = {}
|
||||
@@ -555,8 +562,6 @@ def get_nic_config(configmanager, node, ip=None, mac=None, ifidx=None,
|
||||
'ipv6_method': None,
|
||||
}
|
||||
myaddrs = []
|
||||
if onlyfamily is None:
|
||||
onlyfamily = 0
|
||||
if ifidx is not None:
|
||||
dhcprequested = False
|
||||
myaddrs = get_my_addresses(ifidx, family=onlyfamily)
|
||||
@@ -591,13 +596,15 @@ def get_nic_config(configmanager, node, ip=None, mac=None, ifidx=None,
|
||||
ipbynodename = None
|
||||
ip6bynodename = None
|
||||
try:
|
||||
for addr in socket.getaddrinfo(node, 0, socket.AF_INET, socket.SOCK_DGRAM):
|
||||
ipbynodename = addr[-1][0]
|
||||
if onlyfamily in (socket.AF_INET, 0):
|
||||
for addr in socket.getaddrinfo(node, 0, socket.AF_INET, socket.SOCK_DGRAM):
|
||||
ipbynodename = addr[-1][0]
|
||||
except socket.gaierror:
|
||||
pass
|
||||
try:
|
||||
for addr in socket.getaddrinfo(node, 0, socket.AF_INET6, socket.SOCK_DGRAM):
|
||||
ip6bynodename = addr[-1][0]
|
||||
if onlyfamily in (socket.AF_INET6, 0):
|
||||
for addr in socket.getaddrinfo(node, 0, socket.AF_INET6, socket.SOCK_DGRAM):
|
||||
ip6bynodename = addr[-1][0]
|
||||
except socket.gaierror:
|
||||
pass
|
||||
if myaddrs:
|
||||
@@ -753,7 +760,7 @@ def get_addresses_by_serverip(serverip):
|
||||
elif ':' in serverip:
|
||||
fam = socket.AF_INET6
|
||||
else:
|
||||
raise ValueError('"{0}" is not a valid ip argument')
|
||||
raise ValueError('"{0}" is not a valid ip argument'.format(serverip))
|
||||
ipbytes = socket.inet_pton(fam, serverip)
|
||||
if ipbytes[:8] == b'\xfe\x80\x00\x00\x00\x00\x00\x00':
|
||||
myaddrs = get_my_addresses(matchlla=ipbytes)
|
||||
|
||||
@@ -535,7 +535,10 @@ def _full_updatemacmap(configmanager):
|
||||
if incollective:
|
||||
candmgrs = cfg.get('collective.managercandidates', {}).get('value', None)
|
||||
if candmgrs:
|
||||
candmgrs = noderange.NodeRange(candmgrs, configmanager).nodes
|
||||
try:
|
||||
candmgrs = noderange.NodeRange(candmgrs, configmanager).nodes
|
||||
except Exception:
|
||||
candmgrs = noderange.NodeRange(candmgrs).nodes
|
||||
if mycollectivename not in candmgrs:
|
||||
# do not think about trying to find nodes that we aren't possibly
|
||||
# supposed to be a manager for in a collective
|
||||
|
||||
@@ -29,7 +29,10 @@ def get_switchcreds(configmanager, switches):
|
||||
continue
|
||||
candmgrs = switchcfg.get(switch, {}).get('collective.managercandidates', {}).get('value', None)
|
||||
if candmgrs:
|
||||
candmgrs = noderange.NodeRange(candmgrs, configmanager).nodes
|
||||
try:
|
||||
candmgrs = noderange.NodeRange(candmgrs, configmanager).nodes
|
||||
except Exception:
|
||||
candmgrs = noderange.NodeRange(candmgrs).nodes
|
||||
if collective.get_myname() not in candmgrs:
|
||||
continue
|
||||
switchparms = switchcfg.get(switch, {})
|
||||
@@ -81,4 +84,4 @@ def get_portnamemap(conn):
|
||||
ifidx, ifname = vb
|
||||
ifidx = int(str(ifidx).rsplit('.', 1)[1])
|
||||
ifnamemap[ifidx] = str(ifname)
|
||||
return ifnamemap
|
||||
return ifnamemap
|
||||
|
||||
@@ -9,6 +9,11 @@ logging.getLogger('libarchive').addHandler(logging.NullHandler())
|
||||
import libarchive
|
||||
import hashlib
|
||||
import os
|
||||
try:
|
||||
from io import BytesIO
|
||||
import pycdlib
|
||||
except ImportError:
|
||||
pycdlib = None
|
||||
import shutil
|
||||
import sys
|
||||
import time
|
||||
@@ -24,12 +29,14 @@ import confluent.messages as msg
|
||||
|
||||
COPY = 1
|
||||
EXTRACT = 2
|
||||
EXTRACTUDF = 4
|
||||
READFILES = set([
|
||||
'.disk/info',
|
||||
'media.1/products',
|
||||
'media.2/products',
|
||||
'.DISCINFO',
|
||||
'.discinfo',
|
||||
'ISOLINUX.CFG',
|
||||
'zipl.prm',
|
||||
'sources/idwbinfo.txt',
|
||||
])
|
||||
@@ -65,7 +72,7 @@ def symlink(src, targ):
|
||||
raise
|
||||
|
||||
|
||||
def update_boot(profilename):
|
||||
def update_boot(profilename, initialimport=False):
|
||||
if profilename.startswith('/var/lib/confluent/public'):
|
||||
profiledir = profilename
|
||||
else:
|
||||
@@ -82,6 +89,21 @@ def update_boot(profilename):
|
||||
update_boot_linux(profiledir, profile, label)
|
||||
elif ostype == 'esxi':
|
||||
update_boot_esxi(profiledir, profile, label)
|
||||
elif ostype == 'windows':
|
||||
update_boot_windows(profiledir, profile, label, initialimport)
|
||||
|
||||
def update_boot_windows(profiledir, profile, label, initialimport):
|
||||
profname = os.path.basename(profiledir)
|
||||
try:
|
||||
subprocess.check_call(
|
||||
['/usr/bin/genisoimage', '-o',
|
||||
'{0}/boot.iso'.format(profiledir), '-udf', '-b', 'dvd/etfsboot.com',
|
||||
'-no-emul-boot', '-eltorito-alt-boot', '-eltorito-boot',
|
||||
'dvd/efisys_noprompt.bin', '{0}/boot'.format(profiledir)], preexec_fn=relax_umask)
|
||||
except Exception:
|
||||
if initialimport:
|
||||
return
|
||||
raise
|
||||
|
||||
def update_boot_esxi(profiledir, profile, label):
|
||||
profname = os.path.basename(profiledir)
|
||||
@@ -188,7 +210,7 @@ def update_boot_linux(profiledir, profile, label):
|
||||
needefi = True
|
||||
lincmd = 'linuxefi' if needefi else 'linux'
|
||||
initrdcmd = 'initrdefi' if needefi else 'initrd'
|
||||
grubcfg = "set timeout=5\nmenuentry '"
|
||||
grubcfg = "set timeout=0\nmenuentry '"
|
||||
grubcfg += label
|
||||
grubcfg += "' {\n " + lincmd + " /kernel " + kernelargs + "\n"
|
||||
initrds = []
|
||||
@@ -244,8 +266,13 @@ def extract_entries(entries, flags=0, callback=None, totalsize=None, extractlist
|
||||
for entry in entries:
|
||||
if str(entry).endswith('TRANS.TBL'):
|
||||
continue
|
||||
if extractlist and str(entry).lower() not in extractlist:
|
||||
continue
|
||||
if extractlist:
|
||||
normname = str(entry).lower()
|
||||
for extent in extractlist:
|
||||
if fnmatch(normname, extent):
|
||||
break
|
||||
else:
|
||||
continue
|
||||
write_header(write_p, entry._entry_p)
|
||||
read_p = entry._archive_p
|
||||
while 1:
|
||||
@@ -268,8 +295,33 @@ def extract_entries(entries, flags=0, callback=None, totalsize=None, extractlist
|
||||
return float(sizedone) / float(totalsize)
|
||||
|
||||
|
||||
def extract_file(archfile, flags=0, callback=lambda x: None, imginfo=(), extractlist=None):
|
||||
def extract_udf(archfile, callback=lambda x: None):
|
||||
"""Extracts a UDF archive from a file into the current directory."""
|
||||
dfd = os.dup(archfile.fileno())
|
||||
os.lseek(dfd, 0, 0)
|
||||
fp = os.fdopen(dfd, 'rb')
|
||||
udf = pycdlib.PyCdlib()
|
||||
udf.open_fp(fp)
|
||||
for dirent in udf.walk(udf_path='/'):
|
||||
for filent in dirent[2]:
|
||||
currfile = os.path.join(dirent[0], filent)
|
||||
relfile = currfile
|
||||
if currfile[0] == '/':
|
||||
relfile = currfile[1:]
|
||||
targfile = os.path.join('.', relfile)
|
||||
if os.path.exists(targfile):
|
||||
os.unlink(targfile)
|
||||
os.makedirs(os.path.dirname(targfile), exist_ok=True)
|
||||
udf.get_file_from_iso(targfile, udf_path=currfile)
|
||||
udf.close()
|
||||
fp.close()
|
||||
return True
|
||||
|
||||
|
||||
def extract_file(archfile, flags=0, callback=lambda x: None, imginfo=(), extractlist=None, method=EXTRACT):
|
||||
"""Extracts an archive from a file into the current directory."""
|
||||
if EXTRACTUDF & method:
|
||||
return extract_udf(archfile, callback)
|
||||
totalsize = 0
|
||||
for img in imginfo:
|
||||
if not imginfo[img]:
|
||||
@@ -287,6 +339,16 @@ def extract_file(archfile, flags=0, callback=lambda x: None, imginfo=(), extract
|
||||
return pctdone
|
||||
|
||||
|
||||
def check_openeuler(isoinfo):
|
||||
for entry in isoinfo[0]:
|
||||
if 'openEuler-release-24.03' in entry:
|
||||
ver = entry.split('-')[2]
|
||||
arch = entry.split('.')[-2]
|
||||
cat = 'el9'
|
||||
break
|
||||
else:
|
||||
return None
|
||||
return {'name': 'openeuler-{0}-{1}'.format(ver, arch), 'method': EXTRACT, 'category': cat}
|
||||
def check_rocky(isoinfo):
|
||||
ver = None
|
||||
arch = None
|
||||
@@ -327,6 +389,8 @@ def check_fedora(isoinfo):
|
||||
prodlines = prodinfo.split(b'\n')
|
||||
if len(prodlines) < 3:
|
||||
return None
|
||||
if not prodlines[1].split():
|
||||
return None
|
||||
prod = prodlines[1].split()[0]
|
||||
if prod != b'Fedora':
|
||||
return None
|
||||
@@ -437,9 +501,24 @@ def check_esxi(isoinfo):
|
||||
_, version = line.split(b' ', 1)
|
||||
if not isinstance(version, str):
|
||||
version = version.decode('utf8')
|
||||
edition = ''
|
||||
if isesxi and version:
|
||||
if 'ISOLINUX.CFG' in isoinfo[1]:
|
||||
for line in isoinfo[1]['ISOLINUX.CFG'].split(b'\n'):
|
||||
if line.startswith(b'MENU TITLE'):
|
||||
words = line.split()
|
||||
if len(words) > 2:
|
||||
edition = words[2].decode('utf8')
|
||||
break
|
||||
if edition:
|
||||
for vnd in ('LNV', 'LVO', 'LVN'):
|
||||
if edition.startswith(vnd):
|
||||
edition = '_' + edition.split('-', 1)[1].strip()
|
||||
break
|
||||
else:
|
||||
edition = ''
|
||||
return {
|
||||
'name': 'esxi-{0}'.format(version),
|
||||
'name': 'esxi-{0}{1}'.format(version, edition),
|
||||
'method': EXTRACT,
|
||||
'category': 'esxi{0}'.format(version.split('.', 1)[0])
|
||||
}
|
||||
@@ -467,10 +546,15 @@ def check_debian(isoinfo):
|
||||
raise Exception("Unsupported debian architecture {}".format(arch))
|
||||
arch = 'x86_64'
|
||||
name = 'debian-{0}-{1}'.format(version, arch)
|
||||
major = int(major)
|
||||
if major > 12:
|
||||
category = 'debian13'
|
||||
else:
|
||||
category = 'debian'
|
||||
return {
|
||||
'name': name,
|
||||
'method': EXTRACT,
|
||||
'category': 'debian',
|
||||
'category': category,
|
||||
}
|
||||
|
||||
|
||||
@@ -505,11 +589,11 @@ def check_ubuntu(isoinfo):
|
||||
'method': EXTRACT,
|
||||
'category': 'ubuntu{0}'.format(major)}
|
||||
elif 'efi/boot/bootaa64.efi' in isoinfo[0]:
|
||||
exlist = ['casper/vmlinuz', 'casper/initrd',
|
||||
exlist = ['casper/*vmlinuz', 'casper/*initrd',
|
||||
'efi/boot/bootaa64.efi', 'efi/boot/grubaa64.efi'
|
||||
]
|
||||
else:
|
||||
exlist = ['casper/vmlinuz', 'casper/initrd',
|
||||
exlist = ['casper/*vmlinuz', 'casper/*initrd',
|
||||
'efi/boot/bootx64.efi', 'efi/boot/grubx64.efi'
|
||||
]
|
||||
return {'name': 'ubuntu-{0}-{1}'.format(ver, arch),
|
||||
@@ -587,6 +671,33 @@ def fixup_coreos(targpath):
|
||||
bootimg.write(b'\x01')
|
||||
|
||||
|
||||
def is_windows_executable(filename):
|
||||
with open(filename, 'rb') as f:
|
||||
header = f.read(2)
|
||||
if header == b'MZ':
|
||||
# seems to be DOS, but let's also make sure it is PE32
|
||||
f.seek(0x3c)
|
||||
pe_offset = f.read(4)
|
||||
offset = int.from_bytes(pe_offset, byteorder='little')
|
||||
f.seek(offset)
|
||||
pe_header = f.read(4)
|
||||
if pe_header == b'PE\x00\x00':
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def fixup_windows(targpath):
|
||||
# windows needs the executable file to be executable, which samba
|
||||
# manifests as following the executable bit
|
||||
for root, _, files in os.walk(targpath):
|
||||
for fname in files:
|
||||
for ext in ('.exe', '.dll', '.sys', '.mui', '.efi'):
|
||||
if fname.endswith(ext):
|
||||
fpath = os.path.join(root, fname)
|
||||
if is_windows_executable(fpath):
|
||||
st = os.stat(fpath)
|
||||
os.chmod(fpath, st.st_mode | 0o111)
|
||||
|
||||
def check_coreos(isoinfo):
|
||||
arch = 'x86_64' # TODO: would check magic of vmlinuz to see which arch
|
||||
if 'zipl.prm' in isoinfo[1]:
|
||||
@@ -604,7 +715,28 @@ def check_coreos(isoinfo):
|
||||
'method': EXTRACT, 'category': 'coreos'}
|
||||
|
||||
|
||||
|
||||
def check_windows(isoinfo):
|
||||
idwbinfo = isoinfo[1].get('sources/idwbinfo.txt', b'')
|
||||
idwbinfo = idwbinfo.decode()
|
||||
idwbinfo = idwbinfo.split('\n')
|
||||
version = ''
|
||||
for line in idwbinfo:
|
||||
if 'BuildBranch=' in line:
|
||||
branch = line.strip().split('=')[1]
|
||||
if branch == 'rs5_release':
|
||||
version = '2019'
|
||||
elif branch == 'fe_release':
|
||||
version = '2022'
|
||||
elif branch == 'ge_release':
|
||||
version = '2025'
|
||||
category = f'windows{version}'
|
||||
if version:
|
||||
defprofile = '/opt/confluent/lib/osdeploy/{0}'.format(category)
|
||||
if not os.path.exists(defprofile):
|
||||
return None
|
||||
return {'name': 'windows-{0}-x86_64'.format(version), 'method': EXTRACTUDF, 'category': category}
|
||||
return None
|
||||
|
||||
def check_rhel(isoinfo):
|
||||
ver = None
|
||||
arch = None
|
||||
@@ -657,27 +789,106 @@ def check_rhel(isoinfo):
|
||||
major = ver.split('.', 1)[0]
|
||||
return {'name': 'rhel-{0}-{1}'.format(ver, arch), 'method': EXTRACT, 'category': 'el{0}'.format(major)}
|
||||
|
||||
def fingerprint_initramfs(archive):
|
||||
curroffset = archive.tell()
|
||||
dfd = os.dup(archive.fileno())
|
||||
os.lseek(dfd, curroffset, 0)
|
||||
try:
|
||||
with libarchive.fd_reader(dfd) as reader:
|
||||
for ent in reader:
|
||||
if str(ent) == 'usr/lib/initrd-release':
|
||||
osrelcontents = b''
|
||||
for block in ent.get_blocks():
|
||||
osrelcontents += bytes(block)
|
||||
osrelease = osrelcontents.decode('utf-8').strip()
|
||||
osid = ''
|
||||
osver = ''
|
||||
for line in osrelease.split('\n'):
|
||||
if line.startswith('ID='):
|
||||
osid = line.split('=', 1)[1].strip().strip('"')
|
||||
if line.startswith('VERSION_ID='):
|
||||
osver = line.split('=', 1)[1].strip().strip('"')
|
||||
if osid and osver:
|
||||
return (osid, osver)
|
||||
finally:
|
||||
os.close(dfd)
|
||||
return None
|
||||
|
||||
|
||||
def scan_iso(archive):
|
||||
scanudf = False
|
||||
filesizes = {}
|
||||
filecontents = {}
|
||||
dfd = os.dup(archive.fileno())
|
||||
os.lseek(dfd, 0, 0)
|
||||
try:
|
||||
with libarchive.fd_reader(dfd) as reader:
|
||||
with libarchive.fd_reader(dfd, ) as reader:
|
||||
for ent in reader:
|
||||
if str(ent).endswith('TRANS.TBL'):
|
||||
continue
|
||||
eventlet.sleep(0)
|
||||
filesizes[str(ent)] = ent.size
|
||||
if str(ent) == 'README.TXT':
|
||||
readmecontents = b''
|
||||
for block in ent.get_blocks():
|
||||
readmecontents += bytes(block)
|
||||
if b'ISO-13346' in readmecontents:
|
||||
scanudf = True
|
||||
if str(ent) in READFILES:
|
||||
filecontents[str(ent)] = b''
|
||||
for block in ent.get_blocks():
|
||||
filecontents[str(ent)] += bytes(block)
|
||||
if scanudf:
|
||||
ndfd = os.dup(archive.fileno())
|
||||
os.lseek(ndfd, 0, 0)
|
||||
return scan_udf(ndfd)
|
||||
finally:
|
||||
os.close(dfd)
|
||||
return filesizes, filecontents
|
||||
|
||||
def scan_udf(dfd):
|
||||
fp = os.fdopen(dfd, 'rb')
|
||||
iso = pycdlib.PyCdlib()
|
||||
iso.open_fp(fp)
|
||||
imginfo = {}
|
||||
try:
|
||||
extracted = BytesIO()
|
||||
iso.get_file_from_iso_fp(extracted, udf_path='/sources/idwbinfo.txt')
|
||||
idwbinfo = extracted.getvalue()
|
||||
imginfo = {'sources/idwbinfo.txt': idwbinfo}
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
iso.close()
|
||||
fp.close()
|
||||
return {}, imginfo
|
||||
|
||||
|
||||
def parse_bfb(archive):
|
||||
currtype = 0
|
||||
# we want to find the initramfs image (id 63) and dig around to see the OS version
|
||||
while currtype != 63:
|
||||
currhdr = archive.read(24)
|
||||
if currhdr[:5] != b'Bf\x02\x13!':
|
||||
return None
|
||||
currsize = int.from_bytes(currhdr[8:12], byteorder='little')
|
||||
# currsize needs to be rounded up to nearest 8 byte boundary
|
||||
if currsize % 8:
|
||||
currsize += 8 - (currsize % 8)
|
||||
currtype = currhdr[7]
|
||||
if currtype == 63:
|
||||
ossig = fingerprint_initramfs(archive)
|
||||
if ossig:
|
||||
osinfo = {
|
||||
'name': f'bluefield_{ossig[0]}-{ossig[1]}-aarch64',
|
||||
'method': COPY,
|
||||
'category': f'bluefield_{ossig[0]}{ossig[1]}'
|
||||
}
|
||||
if os.path.exists(f'/opt/confluent/lib/osdeploy/{osinfo["category"]}'):
|
||||
return osinfo
|
||||
else:
|
||||
archive.seek(currsize, os.SEEK_CUR)
|
||||
return None
|
||||
|
||||
def fingerprint(archive):
|
||||
archive.seek(0)
|
||||
@@ -693,6 +904,12 @@ def fingerprint(archive):
|
||||
if name:
|
||||
return name, isoinfo[0], fun.replace('check_', '')
|
||||
return None
|
||||
elif header[:4] == b'Bf\x02\x13':
|
||||
# BFB payload for Bluefield
|
||||
archive.seek(0)
|
||||
imginfo = parse_bfb(archive)
|
||||
if imginfo:
|
||||
return imginfo, None, 'bluefield'
|
||||
else:
|
||||
sum = hashlib.sha256(header)
|
||||
if sum.digest() in HEADERSUMS:
|
||||
@@ -736,9 +953,9 @@ def import_image(filename, callback, backend=False, mfd=None, custtargpath=None,
|
||||
print('Importing OS to ' + targpath + ':')
|
||||
callback({'progress': 0.0})
|
||||
pct = 0.0
|
||||
if EXTRACT & identity['method']:
|
||||
if EXTRACT & identity['method'] or EXTRACTUDF & identity['method']:
|
||||
pct = extract_file(archive, callback=callback, imginfo=imginfo,
|
||||
extractlist=identity.get('extractlist', None))
|
||||
extractlist=identity.get('extractlist', None), method=identity['method'])
|
||||
if COPY & identity['method']:
|
||||
basename = identity.get('copyto', os.path.basename(filename))
|
||||
targiso = os.path.join(targpath, basename)
|
||||
@@ -935,7 +1152,7 @@ def generate_stock_profiles(defprofile, distpath, targpath, osname,
|
||||
subprocess.check_call(
|
||||
['sh', '{0}/initprofile.sh'.format(dirname),
|
||||
targpath, dirname])
|
||||
bootupdates.append(eventlet.spawn(update_boot, dirname))
|
||||
bootupdates.append(eventlet.spawn(update_boot, dirname, True))
|
||||
profilelist.append(profname)
|
||||
for upd in bootupdates:
|
||||
upd.wait()
|
||||
|
||||
@@ -319,6 +319,12 @@ def update_nodes(nodes, element, configmanager, inputdata):
|
||||
if fnmatch(candattrib, attrib):
|
||||
clearattribs.append(candattrib)
|
||||
foundattrib = True
|
||||
currnodeattrs = configmanager.get_node_attributes(node, attrib)
|
||||
for matchattrib in currnodeattrs.get(node, {}):
|
||||
if matchattrib != attrib:
|
||||
continue
|
||||
clearattribs.append(matchattrib)
|
||||
foundattrib = True
|
||||
if not foundattrib:
|
||||
raise exc.InvalidArgumentException("No attribute matches '" + attrib + "' (try wildcard if trying to clear a group)")
|
||||
elif '*' in attrib:
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
import confluent.messages as msg
|
||||
import confluent.netutil as netutil
|
||||
import eventlet.green.subprocess as subprocess
|
||||
import confluent.config.configmanager as cfm
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
@@ -51,6 +52,7 @@ def create_ident_image(node, configmanager):
|
||||
# It would be a reasonable enhancement to list all collective server addresses
|
||||
# restricted by 'managercandidates'
|
||||
ident['deploy_servers'] = []
|
||||
ident['confluent_uuid'] = cfm.get_global('confluent_uuid')
|
||||
for myaddr in netutil.get_my_addresses():
|
||||
myaddr = socket.inet_ntop(myaddr[0], myaddr[1])
|
||||
ident['deploy_servers'].append(myaddr)
|
||||
@@ -60,6 +62,9 @@ def create_ident_image(node, configmanager):
|
||||
with open(os.path.join(tmpd, 'cnflnt.jsn'), 'w') as jsonout:
|
||||
json.dump(ident, jsonout)
|
||||
shutil.copytree('/var/lib/confluent/public/site/tls', os.path.join(tmpd, 'tls'))
|
||||
mkdirp('/var/lib/confluent/private/identity_files/')
|
||||
shutil.copy(os.path.join(tmpd, 'cnflnt.yml'), '/var/lib/confluent/private/identity_files/{0}.yml'.format(node))
|
||||
shutil.copy(os.path.join(tmpd, 'cnflnt.jsn'), '/var/lib/confluent/private/identity_files/{0}.json'.format(node))
|
||||
mkdirp('/var/lib/confluent/private/identity_images/')
|
||||
imgname = '/var/lib/confluent/private/identity_images/{0}.img'.format(node)
|
||||
if os.path.exists(imgname):
|
||||
|
||||
@@ -773,6 +773,7 @@ class IpmiHandler(object):
|
||||
hwaddr=lancfg['mac_address'],
|
||||
staticv6addrs=v6cfg.get('static_addrs', ''),
|
||||
staticv6gateway=v6cfg.get('static_gateway', ''),
|
||||
vlan_id=lancfg.get('vlan_id', None)
|
||||
))
|
||||
elif self.op == 'update':
|
||||
config = self.inputdata.netconfig(self.node)
|
||||
@@ -780,7 +781,8 @@ class IpmiHandler(object):
|
||||
self.ipmicmd.set_net_configuration(
|
||||
ipv4_address=config['ipv4_address'],
|
||||
ipv4_configuration=config['ipv4_configuration'],
|
||||
ipv4_gateway=config['ipv4_gateway'])
|
||||
ipv4_gateway=config['ipv4_gateway'],
|
||||
vlan_id=config.get('vlan_id', None))
|
||||
v6addrs = config.get('static_v6_addresses', None)
|
||||
if v6addrs is not None:
|
||||
v6addrs = v6addrs.split(',')
|
||||
@@ -973,12 +975,12 @@ class IpmiHandler(object):
|
||||
for id, data in self.ipmicmd.get_firmware():
|
||||
self.output.put(msg.ChildCollection(simplify_name(id)))
|
||||
|
||||
def read_firmware(self, component):
|
||||
def read_firmware(self, component, category):
|
||||
items = []
|
||||
errorneeded = False
|
||||
try:
|
||||
complist = () if component == 'all' else (component,)
|
||||
for id, data in self.ipmicmd.get_firmware(complist):
|
||||
for id, data in self.ipmicmd.get_firmware(complist, category):
|
||||
if (component in ('core', 'all') or
|
||||
component == simplify_name(id) or
|
||||
match_aliases(component, simplify_name(id))):
|
||||
@@ -1014,7 +1016,7 @@ class IpmiHandler(object):
|
||||
if len(self.element) == 3:
|
||||
return self.list_firmware()
|
||||
elif len(self.element) == 4:
|
||||
return self.read_firmware(self.element[-1])
|
||||
return self.read_firmware(self.element[-1], self.element[-2])
|
||||
elif self.element[1] == 'hardware':
|
||||
if len(self.element) == 3: # list things in inventory
|
||||
return self.list_inventory()
|
||||
|
||||
@@ -42,6 +42,8 @@ def retrieve(nodes, element, configmanager, inputdata):
|
||||
inletname = element[-1]
|
||||
outlets = get_outlets(nodes, emebs, inletname)
|
||||
for node in outlets:
|
||||
if not outlets[node]:
|
||||
yield msg.ConfluentTargetNotFound(node, 'No matching inlets defined for node in "power.*" attributes')
|
||||
for pgroup in outlets[node]:
|
||||
pdu = outlets[node][pgroup]['pdu']
|
||||
outlet = outlets[node][pgroup]['outlet']
|
||||
@@ -109,6 +111,8 @@ def update(nodes, element, configmanager, inputdata):
|
||||
gp = greenpool.GreenPool(64)
|
||||
outlets = get_outlets(nodes, emebs, inletname)
|
||||
for node in outlets:
|
||||
if not outlets[node]:
|
||||
yield msg.ConfluentTargetNotFound(node, 'No matching inlets defined for node in "power.*" attributes')
|
||||
for pgroup in outlets[node]:
|
||||
pdu = outlets[node][pgroup]['pdu']
|
||||
outlet = outlets[node][pgroup]['outlet']
|
||||
|
||||
@@ -454,8 +454,8 @@ def create(nodes, element, configmanager, inputdata):
|
||||
yield msg.ChildCollection(url)
|
||||
return
|
||||
serialdata = clientsbynode[node].get_vm_serial(node)
|
||||
return PmxConsole(serialdata, node, configmanager, clientsbynode[node])
|
||||
|
||||
yield PmxConsole(serialdata, node, configmanager, clientsbynode[node])
|
||||
return
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -526,6 +526,8 @@ class IpmiHandler(object):
|
||||
def handle_configuration(self):
|
||||
if self.element[1:3] == ['management_controller', 'alerts']:
|
||||
return self.handle_alerts()
|
||||
elif self.element[1:3] == ['management_controller', 'certificate_authorities']:
|
||||
return self.handle_cert_authorities()
|
||||
elif self.element[1:3] == ['management_controller', 'users']:
|
||||
return self.handle_users()
|
||||
elif self.element[1:3] == ['management_controller', 'net_interfaces']:
|
||||
@@ -576,6 +578,28 @@ class IpmiHandler(object):
|
||||
self.pyghmi_event_to_confluent(event)
|
||||
self.output.put(msg.EventCollection((event,), name=self.node))
|
||||
|
||||
def handle_cert_authorities(self):
|
||||
if len(self.element) == 3:
|
||||
if self.op == 'read':
|
||||
for cert in self.ipmicmd.get_trusted_cas():
|
||||
self.output.put(msg.ChildCollection(cert['id']))
|
||||
elif self.op == 'update':
|
||||
cert = self.inputdata.get_pem(self.node)
|
||||
self.ipmicmd.add_trusted_ca(cert)
|
||||
elif len(self.element) == 4:
|
||||
certid = self.element[-1]
|
||||
if self.op == 'read':
|
||||
for certdata in self.ipmicmd.get_trusted_cas():
|
||||
if certdata['id'] == certid:
|
||||
self.output.put(msg.CertificateAuthority(
|
||||
pem=certdata['pem'],
|
||||
node=self.node,
|
||||
subject=certdata['subject'],
|
||||
san=certdata.get('san', None)))
|
||||
elif self.op == 'delete':
|
||||
self.ipmicmd.del_trusted_ca(certid)
|
||||
return
|
||||
|
||||
def handle_alerts(self):
|
||||
if self.element[3] == 'destinations':
|
||||
if len(self.element) == 4:
|
||||
@@ -632,7 +656,8 @@ class IpmiHandler(object):
|
||||
ipv4cfgmethod=lancfg['ipv4_configuration'],
|
||||
hwaddr=lancfg['mac_address'],
|
||||
staticv6addrs=v6cfg['static_addrs'],
|
||||
staticv6gateway=v6cfg['static_gateway']
|
||||
staticv6gateway=v6cfg.get('static_gateway', None),
|
||||
vlan_id=lancfg.get('vlan_id', None)
|
||||
))
|
||||
elif self.op == 'update':
|
||||
config = self.inputdata.netconfig(self.node)
|
||||
@@ -640,7 +665,8 @@ class IpmiHandler(object):
|
||||
self.ipmicmd.set_net_configuration(
|
||||
ipv4_address=config['ipv4_address'],
|
||||
ipv4_configuration=config['ipv4_configuration'],
|
||||
ipv4_gateway=config['ipv4_gateway'])
|
||||
ipv4_gateway=config['ipv4_gateway'],
|
||||
vlan_id=config.get('vlan_id', None))
|
||||
v6addrs = config.get('static_v6_addresses', None)
|
||||
if v6addrs is not None:
|
||||
v6addrs = v6addrs.split(',')
|
||||
@@ -830,12 +856,12 @@ class IpmiHandler(object):
|
||||
for id, data in self.ipmicmd.get_firmware():
|
||||
self.output.put(msg.ChildCollection(simplify_name(id)))
|
||||
|
||||
def read_firmware(self, component):
|
||||
def read_firmware(self, component, category):
|
||||
items = []
|
||||
errorneeded = False
|
||||
try:
|
||||
complist = () if component == 'all' else (component,)
|
||||
for id, data in self.ipmicmd.get_firmware(complist):
|
||||
for id, data in self.ipmicmd.get_firmware(complist, category):
|
||||
if (component in ('core', 'all') or
|
||||
component == simplify_name(id) or
|
||||
match_aliases(component, simplify_name(id))):
|
||||
@@ -871,7 +897,7 @@ class IpmiHandler(object):
|
||||
if len(self.element) == 3:
|
||||
return self.list_firmware()
|
||||
elif len(self.element) == 4:
|
||||
return self.read_firmware(self.element[-1])
|
||||
return self.read_firmware(self.element[-1], self.element[-2])
|
||||
elif self.element[1] == 'hardware':
|
||||
if len(self.element) == 3: # list things in inventory
|
||||
return self.list_inventory()
|
||||
|
||||
@@ -372,7 +372,8 @@ def create(nodes, element, configmanager, inputdata):
|
||||
clientsbynode = prep_vcsa_clients(nodes, configmanager)
|
||||
for node in nodes:
|
||||
serialdata = clientsbynode[node].get_vm_serial(node)
|
||||
return VmConsole(serialdata['server'], serialdata['port'], serialdata['tls'], configmanager)
|
||||
yield VmConsole(serialdata['server'], serialdata['port'], serialdata['tls'], configmanager)
|
||||
return
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ class PlayRunner(object):
|
||||
return avail
|
||||
|
||||
def dump_text(self):
|
||||
stderr = self.stderr
|
||||
retinfo = self.dump_dict()
|
||||
textout = ''
|
||||
for result in retinfo['results']:
|
||||
@@ -64,9 +65,9 @@ class PlayRunner(object):
|
||||
else:
|
||||
textout += result['state'] + '\n'
|
||||
textout += '\n'
|
||||
if self.stderr:
|
||||
textout += "ERRORS **********************************\n"
|
||||
textout += self.stderr
|
||||
if stderr:
|
||||
textout += "ERRORS **********************************\n"
|
||||
textout += stderr
|
||||
return textout
|
||||
|
||||
def dump_json(self):
|
||||
@@ -80,32 +81,34 @@ class PlayRunner(object):
|
||||
|
||||
def _really_run_playbooks(self):
|
||||
global anspypath
|
||||
mypath = anspypath
|
||||
if not mypath:
|
||||
ansloc = shutil.which('ansible')
|
||||
if ansloc:
|
||||
with open(ansloc, 'r') as onsop:
|
||||
shebang = onsop.readline()
|
||||
anspypath = shebang.strip().replace('#!', '')
|
||||
mypath = anspypath
|
||||
if not mypath:
|
||||
mypath = sys.executable
|
||||
with open(os.devnull, 'w+') as devnull:
|
||||
targnodes = ','.join(self.nodes)
|
||||
for playfilename in self.playfiles:
|
||||
worker = subprocess.Popen(
|
||||
[mypath, __file__, targnodes, playfilename],
|
||||
stdin=devnull, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
stdout, stder = worker.communicate()
|
||||
self.stderr += stder.decode('utf8')
|
||||
current = memoryview(stdout)
|
||||
while len(current):
|
||||
sz = struct.unpack('=q', current[:8])[0]
|
||||
result = msgpack.unpackb(current[8:8+sz], raw=False)
|
||||
self.results.append(result)
|
||||
current = current[8+sz:]
|
||||
self.complete = True
|
||||
try:
|
||||
mypath = anspypath
|
||||
if not mypath:
|
||||
ansloc = shutil.which('ansible')
|
||||
if ansloc:
|
||||
with open(ansloc, 'r') as onsop:
|
||||
shebang = onsop.readline()
|
||||
anspypath = shebang.strip().replace('#!', '')
|
||||
mypath = anspypath
|
||||
if not mypath:
|
||||
mypath = sys.executable
|
||||
with open(os.devnull, 'w+') as devnull:
|
||||
targnodes = ','.join(self.nodes)
|
||||
for playfilename in self.playfiles:
|
||||
worker = subprocess.Popen(
|
||||
[mypath, __file__, targnodes, playfilename],
|
||||
stdin=devnull, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
stdout, stder = worker.communicate()
|
||||
self.stderr += stder.decode('utf8')
|
||||
current = memoryview(stdout)
|
||||
while len(current):
|
||||
sz = struct.unpack('=q', current[:8])[0]
|
||||
result = msgpack.unpackb(current[8:8+sz], raw=False)
|
||||
self.results.append(result)
|
||||
current = current[8+sz:]
|
||||
finally:
|
||||
self.complete = True
|
||||
|
||||
|
||||
def run_playbooks(playfiles, nodes):
|
||||
@@ -143,6 +146,7 @@ if __name__ == '__main__':
|
||||
from ansible import context
|
||||
from ansible.module_utils.common.collections import ImmutableDict
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
import ansible.plugins.loader
|
||||
import yaml
|
||||
|
||||
class ResultsCollector(CallbackBase):
|
||||
@@ -161,7 +165,10 @@ if __name__ == '__main__':
|
||||
become=None, become_method=None, become_user=None, check=False,
|
||||
diff=False, verbosity=0, remote_user='root')
|
||||
|
||||
|
||||
try:
|
||||
ansible.plugins.loader.init_plugin_loader()
|
||||
except AttributeError:
|
||||
pass
|
||||
loader = DataLoader()
|
||||
invman = None
|
||||
if os.path.exists('/etc/ansible/hosts'):
|
||||
|
||||
@@ -261,6 +261,10 @@ def handle_request(env, start_response):
|
||||
res['bmcvlan'] = vlan
|
||||
bmcaddr = hmattr.get('hardwaremanagement.manager', {}).get('value',
|
||||
None)
|
||||
if not bmcaddr:
|
||||
start_response('500 Internal Server Error', [])
|
||||
yield 'Missing value in hardwaremanagement.manager'
|
||||
return
|
||||
bmcaddr = bmcaddr.split('/', 1)[0]
|
||||
bmcaddr = socket.getaddrinfo(bmcaddr, 0)[0]
|
||||
bmcaddr = bmcaddr[-1][0]
|
||||
@@ -462,6 +466,9 @@ def handle_request(env, start_response):
|
||||
statusstr = update.get('state', None)
|
||||
statusdetail = update.get('state_detail', None)
|
||||
didstateupdate = False
|
||||
if statusstr or 'status' in update:
|
||||
cfg.set_node_attributes({nodename: {
|
||||
'deployment.client_ip': {'value': clientip}}})
|
||||
if statusstr:
|
||||
cfg.set_node_attributes({nodename: {'deployment.state': statusstr}})
|
||||
didstateupdate = True
|
||||
|
||||
@@ -5,9 +5,10 @@ import confluent.config.configmanager as cfm
|
||||
import confluent.collective.manager as collective
|
||||
import confluent.util as util
|
||||
import eventlet.green.subprocess as subprocess
|
||||
import eventlet.green.socket as socket
|
||||
import eventlet
|
||||
import glob
|
||||
import os
|
||||
import eventlet.green.os as os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
@@ -34,6 +35,7 @@ def normalize_uid():
|
||||
return curruid
|
||||
|
||||
agent_starting = False
|
||||
|
||||
def assure_agent():
|
||||
global agent_starting
|
||||
global agent_pid
|
||||
@@ -54,7 +56,7 @@ def assure_agent():
|
||||
k = k.decode('utf8')
|
||||
v = v.decode('utf8')
|
||||
if k == 'SSH_AGENT_PID':
|
||||
agent_pid = v
|
||||
agent_pid = int(v)
|
||||
os.environ[k] = v
|
||||
finally:
|
||||
agent_starting = False
|
||||
@@ -113,9 +115,23 @@ def initialize_ca():
|
||||
adding_key = False
|
||||
def prep_ssh_key(keyname):
|
||||
global adding_key
|
||||
global agent_pid
|
||||
while adding_key:
|
||||
eventlet.sleep(0.1)
|
||||
adding_key = True
|
||||
if agent_pid:
|
||||
if os.path.exists(os.environ['SSH_AUTH_SOCK']):
|
||||
try:
|
||||
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
sock.connect(os.environ['SSH_AUTH_SOCK'])
|
||||
except Exception:
|
||||
os.unlink(os.environ['SSH_AUTH_SOCK'])
|
||||
os.rmdir(os.path.dirname(os.environ['SSH_AUTH_SOCK']))
|
||||
finally:
|
||||
sock.close()
|
||||
if not os.path.exists(os.environ['SSH_AUTH_SOCK']):
|
||||
agent_pid = None
|
||||
ready_keys.clear()
|
||||
if keyname in ready_keys:
|
||||
adding_key = False
|
||||
return
|
||||
|
||||
@@ -26,7 +26,7 @@ dracut_install poweroff date /etc/nsswitch.conf /etc/services /etc/protocols
|
||||
dracut_install /usr/share/terminfo/x/xterm /usr/share/terminfo/l/linux /usr/share/terminfo/v/vt100 /usr/share/terminfo/x/xterm-color /usr/share/terminfo/s/screen /usr/share/terminfo/x/xterm-256color /usr/share/terminfo/p/putty-256color /usr/share/terminfo/p/putty /usr/share/terminfo/d/dumb
|
||||
dracut_install chmod whoami head tail basename ping tr /usr/share/hwdata/usb.ids
|
||||
if [ -e /etc/redhat-release ]; then
|
||||
dracut_install /etc/redhat_release
|
||||
dracut_install /etc/redhat-release
|
||||
fi
|
||||
dracut_install dmidecode /usr/$IMPLIBDIR/libstdc++.so.6
|
||||
dracut_install ps free find
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user