mirror of
https://github.com/xcat2/confluent.git
synced 2026-09-29 00:31:09 +00:00
Compare commits
81 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 | |||
| 816f3be2ed |
@@ -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
|
||||
|
||||
@@ -18,11 +18,6 @@ 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():
|
||||
@@ -59,7 +54,6 @@ def main():
|
||||
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 = ''
|
||||
|
||||
@@ -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'),
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
@@ -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,6 +16,13 @@ import sys
|
||||
import struct
|
||||
import time
|
||||
import re
|
||||
import hashlib
|
||||
try:
|
||||
import json
|
||||
import hmac
|
||||
except ImportError:
|
||||
json = None
|
||||
hmac = None
|
||||
|
||||
class InvalidApiKey(Exception):
|
||||
pass
|
||||
@@ -72,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)
|
||||
@@ -84,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()
|
||||
@@ -126,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)
|
||||
@@ -133,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
|
||||
@@ -148,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])
|
||||
@@ -195,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()
|
||||
@@ -204,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:
|
||||
@@ -231,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)
|
||||
@@ -294,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()
|
||||
@@ -342,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):
|
||||
@@ -416,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)
|
||||
@@ -545,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')
|
||||
@@ -558,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:
|
||||
@@ -566,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:
|
||||
|
||||
@@ -460,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:
|
||||
@@ -545,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()
|
||||
@@ -567,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,6 +7,7 @@ for pubkey in /etc/ssh/ssh_host*key.pub; do
|
||||
continue
|
||||
fi
|
||||
certfile=${pubkey/.pub/-cert.pub}
|
||||
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
|
||||
@@ -16,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)
|
||||
@@ -24,12 +32,17 @@ 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
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -70,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():
|
||||
@@ -170,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:
|
||||
@@ -181,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)
|
||||
@@ -204,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)
|
||||
@@ -221,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()
|
||||
@@ -238,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')
|
||||
@@ -305,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]
|
||||
@@ -390,7 +403,7 @@ def install_to_disk(imgpath):
|
||||
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']
|
||||
@@ -588,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,6 +129,7 @@ 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)
|
||||
if grep debugssh /proc/cmdline >& /dev/null; then
|
||||
exec /opt/confluent/bin/start_root
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,16 +1,16 @@
|
||||
#!/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
|
||||
#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
|
||||
#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
|
||||
#fi
|
||||
mkdir -p $2/boot/efi/boot
|
||||
if [ -d $1/EFI/boot/ ]; then
|
||||
ln -s $1/EFI/boot/* $2/boot/efi/boot
|
||||
|
||||
@@ -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,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
|
||||
}
|
||||
|
||||
|
||||
@@ -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')
|
||||
@@ -273,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():
|
||||
@@ -421,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:
|
||||
|
||||
@@ -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. '
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -1362,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
|
||||
@@ -489,7 +490,7 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
{'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:
|
||||
@@ -521,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()
|
||||
@@ -715,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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -36,6 +36,7 @@ READFILES = set([
|
||||
'media.2/products',
|
||||
'.DISCINFO',
|
||||
'.discinfo',
|
||||
'ISOLINUX.CFG',
|
||||
'zipl.prm',
|
||||
'sources/idwbinfo.txt',
|
||||
])
|
||||
@@ -71,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:
|
||||
@@ -88,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)
|
||||
@@ -194,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 = []
|
||||
@@ -485,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])
|
||||
}
|
||||
@@ -640,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]:
|
||||
@@ -731,6 +789,31 @@ 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
|
||||
@@ -756,7 +839,9 @@ def scan_iso(archive):
|
||||
for block in ent.get_blocks():
|
||||
filecontents[str(ent)] += bytes(block)
|
||||
if scanudf:
|
||||
return scan_udf(dfd)
|
||||
ndfd = os.dup(archive.fileno())
|
||||
os.lseek(ndfd, 0, 0)
|
||||
return scan_udf(ndfd)
|
||||
finally:
|
||||
os.close(dfd)
|
||||
return filesizes, filecontents
|
||||
@@ -765,16 +850,46 @@ 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()
|
||||
return {}, {'sources/idwbinfo.txt': idwbinfo}
|
||||
imginfo = {'sources/idwbinfo.txt': idwbinfo}
|
||||
except Exception:
|
||||
return {}, {}
|
||||
|
||||
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)
|
||||
header = archive.read(32768)
|
||||
@@ -789,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:
|
||||
@@ -1031,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()
|
||||
|
||||
@@ -62,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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
%define arch x86_64
|
||||
Version: 3.13.0
|
||||
Version: 3.14.2
|
||||
Release: 1
|
||||
Name: confluent-genesis-%{arch}
|
||||
BuildArch: noarch
|
||||
|
||||
@@ -39,6 +39,6 @@ cp -a /root/rpmbuild/BUILD/kernel-*/linux-*/LICENSES/* /usr/share/licenses/kerne
|
||||
cp /usr/share/licenses/krb5-libs/LICENSE /usr/share/licenses/krb5-libs/NOTICE
|
||||
mkdir -p /usr/share/licenses/libdb
|
||||
cp /root/rpmbuild/BUILD/db-5.3.28/lang/sql/odbc/debian/copyright /usr/share/licenses/libdb/copyright
|
||||
head -n 105 $(pwd)/util-linux-2.37.4/sys-utils/hwclock-parse-date.c|tail -n 34 > /usr/share/licenses/util-linux/COPYING.GPLv3
|
||||
head -n 105 /root/rpmbuild/BUILD/util-linux-2.37.4/sys-utils/hwclock-parse-date.c|tail -n 34 > /usr/share/licenses/util-linux/COPYING.GPLv3
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ instmods nvme
|
||||
instmods cdc_ether r8152
|
||||
instmods r8169
|
||||
instmods vmxnet3 virtio_net
|
||||
instmods virtio_scsi vmw_pvscsi
|
||||
instmods mptctl
|
||||
instmods mlx4_ib mlx5_ub ib_umad ib_ipoib
|
||||
instmods ice i40e hfi1 bnxt_en qed qede
|
||||
|
||||
+1
-1
@@ -894,7 +894,7 @@ def main():
|
||||
buildp.add_argument('-p', '--packagelist', help='Filename of package list to replace default pkglist', default='')
|
||||
buildp.add_argument('-a', '--addpackagelist', action='append', default=[],
|
||||
help='A list of additional packages to include, may be specified multiple times')
|
||||
buildp.add_argument('-s', '--source', help='Directory to pull installation from, typically a subdirectory of /var/lib/confluent/distributions. By default, the repositories for the build system are used.')
|
||||
buildp.add_argument('-s', '--source', help='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.')
|
||||
buildp.add_argument('-y', '--non-interactive', help='Avoid prompting for confirmation', action='store_true')
|
||||
buildp.add_argument('-v', '--volume',
|
||||
help='Directory to make available in the build environment. -v / will '
|
||||
|
||||
+6
-2
@@ -4,10 +4,10 @@ mdadm --detail /dev/md*|grep 'Version : 1.0' >& /dev/null || (
|
||||
lvm vgchange -a n
|
||||
mdadm -S -s
|
||||
NUMDEVS=$(for dev in $DEVICES; do
|
||||
echo wipefs -a $dev
|
||||
echo wipefs -a -f $dev
|
||||
done|wc -l)
|
||||
for dev in $DEVICES; do
|
||||
wipefs -a $dev
|
||||
wipefs -a -f $dev
|
||||
done
|
||||
# must use older metadata format to leave disks looking normal for uefi
|
||||
mdadm -C /dev/md/raid $DEVICES -n $NUMDEVS -e 1.0 -l $RAIDLEVEL
|
||||
@@ -15,5 +15,9 @@ mdadm -C /dev/md/raid $DEVICES -n $NUMDEVS -e 1.0 -l $RAIDLEVEL
|
||||
mdadm -S -s
|
||||
mdadm --assemble --scan
|
||||
)
|
||||
while [ ! -e /dev/md/raid ]; do
|
||||
echo 'Waiting on array to be linked...'
|
||||
sleep 0.5
|
||||
done
|
||||
readlink /dev/md/raid|sed -e 's/.*\///' > /tmp/installdisk
|
||||
|
||||
|
||||
Reference in New Issue
Block a user