mirror of
https://github.com/xcat2/confluent.git
synced 2026-09-29 00:31:09 +00:00
Compare commits
159 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a43d7e11e2 | |||
| c5896c056e | |||
| a73dced80b | |||
| b6188683b8 | |||
| 50243b67d5 | |||
| 7cdfcd4913 | |||
| 179ad4e196 | |||
| be2ae57a38 | |||
| f34395648e | |||
| 3f5d96788e | |||
| 17866d7657 | |||
| a1144fd49a | |||
| c472d96406 | |||
| 02791418bc | |||
| 2d29813320 | |||
| a9d15de156 | |||
| a4ba92a2e7 | |||
| 6938bba2d3 | |||
| 871685ea20 | |||
| a480cc73df | |||
| 39eb32df38 | |||
| 3505fe36e6 | |||
| 29accaa494 | |||
| f66093680b | |||
| 97d4015b09 | |||
| 184132c398 | |||
| ac7fdb3ef7 | |||
| d7879bad5b | |||
| 8911193aca | |||
| e7e8daafea | |||
| 3f9a13ed6f | |||
| 500cdf7535 | |||
| 22c8921455 | |||
| ebcf7d7bf8 | |||
| 7a2cb80f6a | |||
| dd2b7be2ca | |||
| 678bd6052a | |||
| cb5fcf077a | |||
| 5f26fb73e6 | |||
| c9ca199b16 | |||
| 8109adaabf | |||
| 29c6ce230f | |||
| 87a6891eff | |||
| a112297e60 | |||
| c567bfbd17 | |||
| 6d2146f252 | |||
| 5045b46014 | |||
| 5905510a32 | |||
| f321f56109 | |||
| 9defc47474 | |||
| 595b628e08 | |||
| 710b24e9f5 | |||
| c26fba74e7 | |||
| a01eb64adc | |||
| ac8179b867 | |||
| 87990c72c3 | |||
| a6a57e8590 | |||
| 6be98c7e60 | |||
| 1a64768fca | |||
| 157641e37a | |||
| 63bbe53448 | |||
| ec3fcee7d7 | |||
| b2b2b5710b | |||
| b32ded9c6a | |||
| 75c228dae4 | |||
| afd2b6c219 | |||
| 9a85b9ee94 | |||
| c9c5165245 | |||
| d4e91b1c7e | |||
| 98e78dd43c | |||
| e7606e69bd | |||
| 580c451945 | |||
| a71804a13b | |||
| dbda4f45a1 | |||
| 5ac0cccc4d | |||
| 465e985cc7 | |||
| 836b629986 | |||
| 58b6a2d317 | |||
| fc6c1495d3 | |||
| c9b9275bb1 | |||
| c0a99f63a5 | |||
| 51afcc68a7 | |||
| 902ff43a9b | |||
| e01701bcf1 | |||
| a1cf8023c6 | |||
| 960a890530 | |||
| d43de05b09 | |||
| 36ce0922fc | |||
| aafa65274c | |||
| 63bb5f4d1b | |||
| d99689f84b | |||
| 816f3be2ed | |||
| bab169269c | |||
| 85ddf528a2 | |||
| 8cfbf40a2e | |||
| 48a0c21300 | |||
| 2c43055aec | |||
| 16a1c4d598 | |||
| 97e4d7c3d0 | |||
| cfa16237e1 | |||
| 7066f85520 | |||
| 8c6f36adf3 | |||
| 33cee4174e | |||
| 47710756a5 | |||
| 21429c6d7d | |||
| ff0c11e919 | |||
| bf209a8009 | |||
| 6ec072be9d | |||
| 79e44e420a | |||
| 5a0b2468f6 | |||
| 13d9fe2712 | |||
| 5028ed9f07 | |||
| 05dbbd6ce0 | |||
| 61749c3649 | |||
| 1f3b84cc9d | |||
| ac42c1b4c7 | |||
| e489d2d532 | |||
| 7bde5c4291 | |||
| 4009aa1aa1 | |||
| 935691d1f3 | |||
| 7fd9a207b1 | |||
| 551862e85e | |||
| a36040fa92 | |||
| b74732ecfa | |||
| bb7e0d1d1e | |||
| cb1f06fecf | |||
| dcd59667e4 | |||
| 5a96c7a20c | |||
| 7d49c5f9be | |||
| 0ec5cf5c5e | |||
| 6b94a8fa22 | |||
| 45fa229f9f | |||
| 071433a60a | |||
| 7dd5c36e78 | |||
| 26f3ee539f | |||
| dcfb028ba9 | |||
| 59dc7b5426 | |||
| 65b613219e | |||
| c7d41f8a4b | |||
| 5f9250c492 | |||
| 169fd976ce | |||
| d063f50a9c | |||
| 6a90e1cc77 | |||
| 7aaa350679 | |||
| a1a144d211 | |||
| 8d8db070eb | |||
| 4fd7021581 | |||
| 250de6133d | |||
| 40f3ca73c4 | |||
| 19c4dc71db | |||
| 94dc266cd4 | |||
| 1a679ab6eb | |||
| 8722e66583 | |||
| 6bebae1d0b | |||
| 11939c4d57 | |||
| ee53ee47c1 | |||
| 8111a13554 | |||
| ef46b6cabd | |||
| 08738713c9 |
@@ -13,7 +13,7 @@ If you're coming from xCAT, check out [this comparison](https://hpc.lenovo.com/u
|
||||
|
||||
# Documentation
|
||||
|
||||
Confluent documentation is hosted on hpc.lenovo.com: https://hpc.lenovo.com/users/documentation/
|
||||
Confluent documentation is hosted on: https://xcat2.github.io/confluent-docs/
|
||||
|
||||
# Download
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ import math
|
||||
import getpass
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
import select
|
||||
import shlex
|
||||
import signal
|
||||
@@ -969,8 +970,15 @@ def main():
|
||||
sys.stdout.write('Lost connection to server')
|
||||
quitconfetty(fullexit=True)
|
||||
|
||||
sgr_re = re.compile(r'(\x1b\[[0-9;]*m)')
|
||||
sgr_parameters_re = re.compile(r'\x1b\[([0-9;]*)m')
|
||||
fgcolor = None
|
||||
bgcolor = None
|
||||
fgshifted = False
|
||||
pendseq = ''
|
||||
def consume_termdata(fh, bufferonly=False):
|
||||
global clearpowermessage
|
||||
global fgcolor, bgcolor, fgshifted, pendseq
|
||||
try:
|
||||
data = tlvdata.recv(fh)
|
||||
except Exception:
|
||||
@@ -979,7 +987,59 @@ def consume_termdata(fh, bufferonly=False):
|
||||
updatestatus(data)
|
||||
return ''
|
||||
if data is not None:
|
||||
data = client.stringify(data)
|
||||
indata = pendseq + client.stringify(data)
|
||||
pendseq = ''
|
||||
data = ''
|
||||
for segment in sgr_re.split(indata):
|
||||
if sgr_re.match(segment): # it is an sgr, analyze, maybe replace
|
||||
params = []
|
||||
for parameters in sgr_parameters_re.findall(segment):
|
||||
for param in parameters.split(';'):
|
||||
params.append(param)
|
||||
if param == '0':
|
||||
fgcolor = None
|
||||
bgcolor = None
|
||||
try:
|
||||
ival = int(param)
|
||||
except ValueError:
|
||||
continue
|
||||
if 40 <= ival <= 47 or 100 <= ival <= 107:
|
||||
bgcolor = ival
|
||||
if 30 <= ival <= 37 or 90 <= ival <= 97:
|
||||
fgcolor = ival
|
||||
if bgcolor is not None:
|
||||
fgindicated = False
|
||||
for idx, param in enumerate(params):
|
||||
try:
|
||||
ival = int(param)
|
||||
except ValueError:
|
||||
continue
|
||||
if 30 <= ival <= 37 and (bgcolor % 10 == ival % 10):
|
||||
fgindicated = True
|
||||
fgshifted = True
|
||||
ival += 60
|
||||
params[idx] = str(ival)
|
||||
if not fgindicated and fgcolor is not None:
|
||||
if bgcolor and (bgcolor % 10) == (fgcolor % 10):
|
||||
fgshifted = True
|
||||
params.append(str((fgcolor % 10) + 90))
|
||||
elif fgshifted:
|
||||
params.append(str(fgcolor))
|
||||
segment = '\x1b[' + ';'.join(str(p) for p in params) + 'm'
|
||||
data += segment
|
||||
# defer any partial ansi escape sequence for a later pass
|
||||
escidx = segment.rfind('\x1b[')
|
||||
if escidx >= 0:
|
||||
for chr in segment[escidx + 2:]:
|
||||
if 0x40 <= ord(chr) <= 0x7e:
|
||||
break
|
||||
else:
|
||||
# incomplete escape sequence, don't print it yet
|
||||
data = data[:-len(segment) + escidx]
|
||||
pendseq = segment[escidx:]
|
||||
if not pendseq and segment and segment[-1] == '\x1b':
|
||||
data = data[:-1]
|
||||
pendseq = '\x1b'
|
||||
if clearpowermessage:
|
||||
sys.stdout.write("\x1b[2J\x1b[;H")
|
||||
clearpowermessage = False
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
import optparse
|
||||
import signal
|
||||
import sys
|
||||
import os
|
||||
|
||||
try:
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
except AttributeError:
|
||||
@@ -17,18 +18,16 @@ import confluent.client as client
|
||||
import confluent.sortutil as sortutil
|
||||
|
||||
|
||||
def lookupdata(data, key):
|
||||
ret = data.get(key, {}).get('value', '')
|
||||
if ret is None:
|
||||
ret = ''
|
||||
return ret
|
||||
|
||||
|
||||
def main():
|
||||
argparser = optparse.OptionParser(
|
||||
usage='''\n %prog noderange -o ansible.hosts
|
||||
\n ''')
|
||||
usage='''\n %prog noderange -o ansible.hosts -a
|
||||
''')
|
||||
argparser.add_option('-o', '--output',
|
||||
help='Ansible hosts file')
|
||||
help='Writes an Ansible hosts file')
|
||||
argparser.add_option('-a', '--append', action='store_true',
|
||||
help='Appends to existing hosts file')
|
||||
(options, args) = argparser.parse_args()
|
||||
try:
|
||||
noderange = args[0]
|
||||
@@ -45,24 +44,42 @@ def main():
|
||||
if node not in databynode:
|
||||
databynode[node] = {}
|
||||
databynode[node].update(res['databynode'][node])
|
||||
|
||||
nodesbygroup = {}
|
||||
with open(options.output, 'w') as importfile:
|
||||
needempty = False
|
||||
for node in sortutil.natural_sort(databynode):
|
||||
data = databynode[node]
|
||||
if not data.get('groups', []):
|
||||
importfile.write(node + '\n')
|
||||
needempty = True
|
||||
for g in data.get('groups', []):
|
||||
if g not in nodesbygroup:
|
||||
nodesbygroup[g] = set([node])
|
||||
for node in sortutil.natural_sort(databynode):
|
||||
data = databynode[node]
|
||||
groups = data.get('groups', [])
|
||||
if not groups:
|
||||
nodesbygroup.setdefault('', set()).add(node.strip().lower())
|
||||
else:
|
||||
for g in groups:
|
||||
nodesbygroup.setdefault(g, set()).add(node.strip().lower())
|
||||
existing_data = {}
|
||||
if options.append and os.path.exists(options.output):
|
||||
current_group = ''
|
||||
with open(options.output, 'r') as f:
|
||||
for line in f:
|
||||
line = line.strip().lower()
|
||||
if not line:
|
||||
continue
|
||||
if line.startswith('[') and line.endswith(']'):
|
||||
current_group = line[1:-1]
|
||||
existing_data.setdefault(current_group, set())
|
||||
else:
|
||||
nodesbygroup[g].add(node)
|
||||
if needempty:
|
||||
importfile.write('\n')
|
||||
for group in sortutil.natural_sort(nodesbygroup):
|
||||
importfile.write('[{0}]\n'.format(group))
|
||||
for node in sortutil.natural_sort(nodesbygroup[group]):
|
||||
existing_data.setdefault(current_group, set()).add(line)
|
||||
|
||||
for group, nodes in nodesbygroup.items():
|
||||
nodes = {n.strip().lower() for n in nodes}
|
||||
current_nodes = existing_data.get(group, set())
|
||||
new_nodes = nodes - current_nodes
|
||||
if new_nodes:
|
||||
existing_data.setdefault(group, set()).update(nodes)
|
||||
|
||||
with open(options.output, 'w') as importfile:
|
||||
for group in sortutil.natural_sort(existing_data.keys()):
|
||||
if group:
|
||||
importfile.write('[{0}]\n'.format(group))
|
||||
for node in sortutil.natural_sort(existing_data[group]):
|
||||
importfile.write('{0}\n'.format(node))
|
||||
importfile.write('\n')
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ def run():
|
||||
|
||||
currprocs = 0
|
||||
all = set([])
|
||||
poller = select.epoll()
|
||||
pipedesc = {}
|
||||
pendingexecs = deque()
|
||||
exitcode = 0
|
||||
@@ -102,19 +103,23 @@ def run():
|
||||
cmdv = ['ssh', sshnode] + cmdvbase + cmdstorun[0]
|
||||
if currprocs < concurrentprocs:
|
||||
currprocs += 1
|
||||
run_cmdv(sshnode, cmdv, all, pipedesc)
|
||||
run_cmdv(sshnode, cmdv, all, poller, pipedesc)
|
||||
else:
|
||||
pendingexecs.append((sshnode, cmdv))
|
||||
if not all or exitcode:
|
||||
sys.exit(exitcode)
|
||||
rdy, _, _ = select.select(all, [], [], 10)
|
||||
rdy = poller.poll(10)
|
||||
while all:
|
||||
pernodeout = {}
|
||||
for r in rdy:
|
||||
r = r[0]
|
||||
desc = pipedesc[r]
|
||||
r = desc['file']
|
||||
node = desc['node']
|
||||
data = True
|
||||
while data and select.select([r], [], [], 0)[0]:
|
||||
singlepoller = select.epoll()
|
||||
singlepoller.register(r, select.EPOLLIN)
|
||||
while data and singlepoller.poll(0):
|
||||
data = r.readline()
|
||||
if data:
|
||||
if desc['type'] == 'stdout':
|
||||
@@ -131,15 +136,17 @@ def run():
|
||||
if ret is not None:
|
||||
exitcode = exitcode | ret
|
||||
all.discard(r)
|
||||
poller.unregister(r)
|
||||
r.close()
|
||||
if desc['type'] == 'stdout':
|
||||
if idxbynode[node] < len(cmdstorun):
|
||||
cmdv = ['ssh', sshnode] + cmdvbase + cmdstorun[idxbynode[node]]
|
||||
idxbynode[node] += 1
|
||||
run_cmdv(node, cmdv, all, pipedesc)
|
||||
run_cmdv(node, cmdv, all, poller, pipedesc)
|
||||
elif pendingexecs:
|
||||
node, cmdv = pendingexecs.popleft()
|
||||
run_cmdv(node, cmdv, all, pipedesc)
|
||||
run_cmdv(node, cmdv, all, poller. pipedesc)
|
||||
singlepoller.close()
|
||||
for node in sortutil.natural_sort(pernodeout):
|
||||
for line in pernodeout[node]:
|
||||
line = client.stringify(line)
|
||||
@@ -147,19 +154,21 @@ def run():
|
||||
sys.stdout.write('{0}: {1}'.format(node, line))
|
||||
sys.stdout.flush()
|
||||
if all:
|
||||
rdy, _, _ = select.select(all, [], [], 10)
|
||||
rdy = poller.poll(10)
|
||||
sys.exit(exitcode)
|
||||
|
||||
|
||||
def run_cmdv(node, cmdv, all, pipedesc):
|
||||
def run_cmdv(node, cmdv, all, poller, pipedesc):
|
||||
nopen = subprocess.Popen(
|
||||
cmdv, stdin=devnull, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
pipedesc[nopen.stdout] = {'node': node, 'popen': nopen,
|
||||
'type': 'stdout'}
|
||||
pipedesc[nopen.stderr] = {'node': node, 'popen': nopen,
|
||||
'type': 'stderr'}
|
||||
pipedesc[nopen.stdout.fileno()] = {'node': node, 'popen': nopen,
|
||||
'type': 'stdout', 'file': nopen.stdout}
|
||||
pipedesc[nopen.stderr.fileno()] = {'node': node, 'popen': nopen,
|
||||
'type': 'stderr', 'file': nopen.stderr}
|
||||
all.add(nopen.stdout)
|
||||
poller.register(nopen.stdout, select.EPOLLIN)
|
||||
all.add(nopen.stderr)
|
||||
poller.register(nopen.stderr, select.EPOLLIN)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
from cryptography import x509
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
|
||||
path = os.path.dirname(os.path.realpath(__file__))
|
||||
path = os.path.realpath(os.path.join(path, '..', 'lib', 'python'))
|
||||
if path.startswith('/opt'):
|
||||
sys.path.append(path)
|
||||
import confluent.client as client
|
||||
|
||||
def removebmccacert(noderange, certid, cmd):
|
||||
for res in cmd.delete(f'/noderange/{noderange}/configuration/management_controller/certificate_authorities/{certid}'):
|
||||
print(repr(res))
|
||||
|
||||
def listbmccacerts(noderange, cmd):
|
||||
certids = []
|
||||
for res in cmd.read(f'/noderange/{noderange}/configuration/management_controller/certificate_authorities'):
|
||||
certids.append(res.get('item', {}).get('href', ''))
|
||||
for certid in certids:
|
||||
for res in cmd.read(f'/noderange/{noderange}/configuration/management_controller/certificate_authorities/{certid}'):
|
||||
for node in res.get('databynode', {}):
|
||||
certdata = res['databynode'][node].get('pem', {}).get('value', '')
|
||||
summary = ''
|
||||
if not certdata:
|
||||
continue
|
||||
san = res['databynode'][node].get('san', {}).get('value', '')
|
||||
if san:
|
||||
summary += f" SANs: {san}"
|
||||
subject = res['databynode'][node].get('subject', {}).get('value', '')
|
||||
if subject:
|
||||
summary = subject
|
||||
try:
|
||||
cert = x509.load_pem_x509_certificate(certdata.encode())
|
||||
sha256 = cert.fingerprint(hashes.SHA256()).hex().upper()
|
||||
except Exception as e:
|
||||
print(f"Error processing certificate for {node}: {e}", file=sys.stderr)
|
||||
continue
|
||||
summary += f" (SHA256={sha256})"
|
||||
print(f"{node}: {certid}: {summary}")
|
||||
|
||||
def installbmccacert(noderange, certfile, cmd):
|
||||
if certfile:
|
||||
try:
|
||||
with open(certfile, 'r') as f:
|
||||
certdata = f.read()
|
||||
except Exception as e:
|
||||
print(f"Error reading certificate file: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Simple validation: check if it starts and ends with the correct PEM markers
|
||||
if not (certdata.startswith("-----BEGIN CERTIFICATE-----") and certdata.strip().endswith("-----END CERTIFICATE-----")):
|
||||
print("Invalid certificate format. Must be a PEM encoded certificate.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
payload = {'pem': certdata}
|
||||
for res in cmd.update(f'/noderange/{noderange}/configuration/management_controller/certificate_authorities', payload):
|
||||
print(repr(res))
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description='Node certificate utility')
|
||||
parser.add_argument('noderange', help='Node range to operate on')
|
||||
subparsers = parser.add_subparsers(dest='command', help='Available commands')
|
||||
|
||||
# installbmccacert subcommand
|
||||
install_parser = subparsers.add_parser('installbmccacert', help='Install BMC CA certificate')
|
||||
install_parser.add_argument('filename', help='Certificate file to install')
|
||||
|
||||
remove_parser = subparsers.add_parser('removebmccacert', help='Remove BMC CA certificate')
|
||||
remove_parser.add_argument('id', help='Certificate id to remove')
|
||||
|
||||
list_parser = subparsers.add_parser('listbmccacerts', help='List BMC CA certificates')
|
||||
|
||||
args = parser.parse_args()
|
||||
c = client.Command()
|
||||
if args.command == 'installbmccacert':
|
||||
installbmccacert(args.noderange, args.filename, c)
|
||||
elif args.command == 'removebmccacert':
|
||||
removebmccacert(args.noderange, args.id, c)
|
||||
elif args.command == 'listbmccacerts':
|
||||
listbmccacerts(args.noderange, c)
|
||||
else:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2017 Lenovo
|
||||
# Copyright 2025 Lenovo
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -96,6 +96,12 @@ cfgpaths = {
|
||||
'bmc.static_ipv6_gateway': (
|
||||
'configuration/management_controller/net_interfaces/management',
|
||||
'static_v6_gateway'),
|
||||
'bmc.vlan_id': (
|
||||
'configuration/management_controller/net_interfaces/management',
|
||||
'vlan_id'),
|
||||
'bmc.mac_address': (
|
||||
'configuration/management_controller/net_interfaces/management',
|
||||
'hw_addr'),
|
||||
'bmc.hostname': (
|
||||
'configuration/management_controller/hostname', 'hostname'),
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ import termios
|
||||
import fcntl
|
||||
import confluent.screensqueeze as sq
|
||||
try:
|
||||
from PIL import Image, ImageDraw
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
except ImportError:
|
||||
Image = None
|
||||
|
||||
@@ -213,29 +213,71 @@ def cursor_hide():
|
||||
def cursor_show():
|
||||
sys.stdout.write('\x1b[?25h')
|
||||
|
||||
def get_pix_dimensions(width, height):
|
||||
cheight, cwidth, pixwidth, pixheight = sq.get_screengeom()
|
||||
imgwidth = int(pixwidth / cwidth * width)
|
||||
imgheight = int(pixheight / cheight * height)
|
||||
return imgwidth, imgheight
|
||||
|
||||
def draw_text(text, width, height):
|
||||
if Image:
|
||||
maxfntsize = 256
|
||||
nerr = Image.new(mode='RGB', size=(1024,768), color='green')
|
||||
imgwidth, imgheight = get_pix_dimensions(width, height)
|
||||
nerr = Image.new(mode='RGB', size=(imgwidth, imgheight), color='green')
|
||||
nd = ImageDraw.Draw(nerr)
|
||||
for txtpiece in text.split('\n'):
|
||||
fntsize = 8
|
||||
while nd.textlength(txtpiece, font_size=fntsize) < 896:
|
||||
txtfont = ImageFont.truetype('DejaVuSans.ttf', size=fntsize)
|
||||
while nd.textlength(txtpiece, font=txtfont) < int(imgwidth * 0.90):
|
||||
fntsize += 1
|
||||
txtfont = ImageFont.truetype('DejaVuSans.ttf', size=fntsize)
|
||||
fntsize -= 1
|
||||
if fntsize < maxfntsize:
|
||||
maxfntsize = fntsize
|
||||
nd.text((64, 64), text, font_size=maxfntsize)
|
||||
hmargin = int(imgwidth * 0.05)
|
||||
vmargin = int(imgheight * 0.10)
|
||||
nd.text((hmargin, vmargin), text, font=txtfont)
|
||||
nd.rectangle((0, 0, nerr.width - 1, nerr.height -1), outline='white')
|
||||
outfile = io.BytesIO()
|
||||
nerr.save(outfile, format='PNG')
|
||||
data = base64.b64encode(outfile.getbuffer())
|
||||
draw_image(data, width, height)
|
||||
draw_image(data, width, height, doscale=False)
|
||||
else:
|
||||
sys.stdout.write(text)
|
||||
cursor_left(len(txt))
|
||||
cursor_left(len(text))
|
||||
|
||||
def draw_image(data, width, height):
|
||||
def draw_image(data, width, height, doscale=True):
|
||||
imageformat = os.environ.get('CONFLUENT_IMAGE_PROTOCOL', 'kitty')
|
||||
if doscale and Image and width:
|
||||
bindata = base64.b64decode(data)
|
||||
binfile = io.BytesIO()
|
||||
binfile.write(bindata)
|
||||
binfile.seek(0)
|
||||
try:
|
||||
img = Image.open(binfile)
|
||||
except Exception as e:
|
||||
errstr = 'Error rendering image:\n' + str(e)
|
||||
return draw_text(errstr, width, height)
|
||||
imgwidth, imgheight = get_pix_dimensions(width, height)
|
||||
nimg = Image.new(mode='RGBA', size=(imgwidth, imgheight))
|
||||
imgwidth -= 4
|
||||
imgheight -= 4
|
||||
hscalefact = imgwidth / img.width
|
||||
vscalefact = imgheight / img.height
|
||||
if hscalefact < vscalefact:
|
||||
rzwidth = imgwidth
|
||||
rzheight = int(img.height * hscalefact)
|
||||
else:
|
||||
rzwidth = int(img.width * vscalefact)
|
||||
rzheight = imgheight
|
||||
img = img.resize((rzwidth, rzheight))
|
||||
nd = ImageDraw.Draw(nimg)
|
||||
nd.rectangle((1, 1, rzwidth + 2, rzheight + 2), outline='black')
|
||||
nd.rectangle((0, 0, rzwidth + 3, rzheight + 3), outline='white')
|
||||
nimg.paste(img, box=(2, 2))
|
||||
outfile = io.BytesIO()
|
||||
nimg.save(outfile, format='PNG')
|
||||
data = base64.b64encode(outfile.getbuffer())
|
||||
if imageformat == 'sixel':
|
||||
sixel_draw(data)
|
||||
elif imageformat == 'iterm':
|
||||
@@ -266,23 +308,6 @@ def iterm_draw(data, width, height):
|
||||
|
||||
|
||||
def kitty_draw(data, width, height):
|
||||
if Image:
|
||||
bindata = base64.b64decode(data)
|
||||
binfile = io.BytesIO()
|
||||
binfile.write(bindata)
|
||||
binfile.seek(0)
|
||||
try:
|
||||
img = Image.open(binfile)
|
||||
except Exception as e:
|
||||
errstr = 'Error rendering image:\n' + str(e)
|
||||
return draw_text(errstr, width, height)
|
||||
nimg = Image.new(mode='RGB', size=(img.width + 4, img.height + 4), color='black')
|
||||
nd = ImageDraw.Draw(nimg)
|
||||
nd.rectangle((0, 0, nimg.width - 1, nimg.height -1), outline='white', width=1)
|
||||
nimg.paste(img, box=(2, 2))
|
||||
outfile = io.BytesIO()
|
||||
nimg.save(outfile, format='PNG')
|
||||
data = base64.b64encode(outfile.getbuffer())
|
||||
preamble = '\x1b_Ga=T,f=100'
|
||||
if height:
|
||||
preamble += f',r={height},c={width}'
|
||||
@@ -381,8 +406,9 @@ def redraw():
|
||||
else:
|
||||
sys.stdout.write('\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
resized = False
|
||||
def do_screenshot():
|
||||
global resized
|
||||
global numrows
|
||||
sess = client.Command()
|
||||
if options.tile:
|
||||
@@ -395,8 +421,11 @@ def do_screenshot():
|
||||
for res in sess.read('/noderange/{}/nodes/'.format(args[0])):
|
||||
allnodes.append(res['item']['href'].replace('/', ''))
|
||||
numnodes += 1
|
||||
resized = False
|
||||
def do_resize(a=None, b=None):
|
||||
global resized
|
||||
if a:
|
||||
resized = True
|
||||
# on a window resize, clear the old stuff
|
||||
# ideally we'd retain the images and redraw them
|
||||
sys.stdout.write('\x1bc')
|
||||
@@ -424,38 +453,38 @@ def do_screenshot():
|
||||
sys.stdout.write('\x1bc')
|
||||
firstnodename = None
|
||||
dorefresh = True
|
||||
vnconly = set([])
|
||||
while dorefresh:
|
||||
for res in sess.read('/noderange/{}/console/ikvm_screenshot'.format(args[0])):
|
||||
for node in res.get('databynode', {}):
|
||||
errorstr = ''
|
||||
if not firstnodename:
|
||||
firstnodename = node
|
||||
error = res['databynode'][node].get('error')
|
||||
if error and 'vnc available' in error:
|
||||
vnconly.add(node)
|
||||
continue
|
||||
elif error:
|
||||
errorstr = error
|
||||
imgdata = res['databynode'][node].get('image', {}).get('imgdata', None)
|
||||
if imgdata:
|
||||
errorstr = ''
|
||||
if len(imgdata) < 32: # We were subjected to error
|
||||
errorstr = f'Unable to get screenshot'
|
||||
imagedatabynode[node] = imgdata
|
||||
if node in nodepositions:
|
||||
prep_node_tile(node)
|
||||
cursor_save()
|
||||
else:
|
||||
if options.interval is not None:
|
||||
if node != firstnodename:
|
||||
sys.stderr.write('Multiple nodes not supported for interval')
|
||||
sys.exit(1)
|
||||
sticky_cursor()
|
||||
sys.stdout.write('{}: '.format(node))
|
||||
# one row is used by our own name, so cheight - 1 for that allowance
|
||||
if errorstr:
|
||||
draw_text(errorstr, cwidth, cheight -1 if cheight else cheight)
|
||||
else:
|
||||
draw_image(imgdata.encode(), cwidth, cheight - 1 if cheight else cheight)
|
||||
if node in nodepositions:
|
||||
cursor_restore()
|
||||
reset_cursor(node)
|
||||
else:
|
||||
sys.stdout.write('\n')
|
||||
sys.stdout.flush()
|
||||
if errorstr or imgdata:
|
||||
draw_node(node, imgdata, errorstr, firstnodename, cwidth, cheight)
|
||||
if asyncvnc:
|
||||
urlbynode = {}
|
||||
for node in vnconly:
|
||||
for res in sess.update(f'/nodes/{node}/console/ikvm', {'method': 'unix'}):
|
||||
url = res.get('item', {}).get('href')
|
||||
if url:
|
||||
urlbynode[node] = url
|
||||
draw_vnc_grabs(urlbynode, cwidth, cheight)
|
||||
if resized:
|
||||
do_resize(True)
|
||||
resized = False
|
||||
elif vnconly:
|
||||
sys.stderr.write("Require asyncvnc installed to do VNC screenshotting\n")
|
||||
if options.interval is None:
|
||||
dorefresh = False
|
||||
else:
|
||||
@@ -463,6 +492,60 @@ def do_screenshot():
|
||||
time.sleep(options.interval)
|
||||
sys.exit(0)
|
||||
|
||||
try:
|
||||
import asyncio, asyncvnc
|
||||
except ImportError:
|
||||
asyncvnc = None
|
||||
|
||||
def draw_vnc_grabs(urlbynode, cwidth, cheight):
|
||||
asyncio.run(grab_vncs(urlbynode, cwidth, cheight))
|
||||
async def grab_vncs(urlbynode, cwidth, cheight):
|
||||
tasks = []
|
||||
for node in urlbynode:
|
||||
url = urlbynode[node]
|
||||
tasks.append(asyncio.create_task(do_vnc_screenshot(node, url, cwidth, cheight)))
|
||||
await asyncio.gather(*tasks)
|
||||
|
||||
async def my_opener(host, port):
|
||||
# really, host is the unix
|
||||
return await asyncio.open_unix_connection(host)
|
||||
|
||||
async def do_vnc_screenshot(node, url, cwidth, cheight):
|
||||
async with asyncvnc.connect(url, opener=my_opener) as client:
|
||||
# Retrieve pixels as a 3D numpy array
|
||||
pixels = await client.screenshot()
|
||||
# Save as PNG using PIL/pillow
|
||||
image = Image.fromarray(pixels)
|
||||
outfile = io.BytesIO()
|
||||
image.save(outfile, format='PNG')
|
||||
imgdata = base64.b64encode(outfile.getbuffer()).decode()
|
||||
if imgdata:
|
||||
draw_node(node, imgdata, '', '', cwidth, cheight)
|
||||
|
||||
def draw_node(node, imgdata, errorstr, firstnodename, cwidth, cheight):
|
||||
imagedatabynode[node] = imgdata
|
||||
if node in nodepositions:
|
||||
prep_node_tile(node)
|
||||
cursor_save()
|
||||
else:
|
||||
if options.interval is not None:
|
||||
if node != firstnodename:
|
||||
sys.stderr.write('Multiple nodes not supported for interval')
|
||||
sys.exit(1)
|
||||
sticky_cursor()
|
||||
sys.stdout.write('{}: '.format(node))
|
||||
# one row is used by our own name, so cheight - 1 for that allowance
|
||||
if errorstr:
|
||||
draw_text(errorstr, cwidth, cheight -1 if cheight else cheight)
|
||||
else:
|
||||
draw_image(imgdata.encode(), cwidth, cheight - 1 if cheight else cheight)
|
||||
if node in nodepositions:
|
||||
cursor_restore()
|
||||
reset_cursor(node)
|
||||
else:
|
||||
sys.stdout.write('\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
if options.screenshot:
|
||||
try:
|
||||
cursor_hide()
|
||||
@@ -620,7 +703,6 @@ if options.windowed:
|
||||
screenheight -= wmyo
|
||||
currx = window_width
|
||||
curry = 0
|
||||
maxcol = int(screenwidth/window_width)
|
||||
|
||||
for node in sortutil.natural_sort(nodes):
|
||||
if options.tile and envlist[0] == 'xterm':
|
||||
@@ -628,7 +710,7 @@ if options.windowed:
|
||||
corrected_y = curry
|
||||
xgeometry = '{0}+{1}+{2}'.format(sizegeometry, corrected_x, corrected_y)
|
||||
currx += window_width
|
||||
if currx >= screenwidth:
|
||||
if currx + window_width >= screenwidth:
|
||||
currx=0
|
||||
curry += window_height
|
||||
if curry > screenheight:
|
||||
|
||||
@@ -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)):
|
||||
|
||||
@@ -123,7 +123,7 @@ def process_header(header):
|
||||
fields.append('serial')
|
||||
elif datum == 'uuid':
|
||||
fields.append('uuid')
|
||||
elif datum in ('bmc', 'imm', 'xcc'):
|
||||
elif datum in ('bmc', 'imm', 'xcc', 'ip'):
|
||||
fields.append('hardwaremanagement.manager')
|
||||
elif datum in ('bmc gateway', 'xcc gateway', 'imm gateway'):
|
||||
fields.append('net.bmc.ipv4_gateway')
|
||||
@@ -191,6 +191,7 @@ def import_csv(options, session):
|
||||
if field in unique_fields:
|
||||
unique_data[field] = set([])
|
||||
broken = False
|
||||
alldata=[]
|
||||
for record in records:
|
||||
currfields = list(fields)
|
||||
nodedatum = {}
|
||||
@@ -207,9 +208,15 @@ def import_csv(options, session):
|
||||
nodedatum[currfield] = datum
|
||||
if not datum_complete(nodedatum):
|
||||
sys.exit(1)
|
||||
alldata.append(nodedatum)
|
||||
allthere = True
|
||||
for nodedatum in alldata:
|
||||
if not search_record(nodedatum, options, session) and not broken:
|
||||
allthere = False
|
||||
blocking_scan(session)
|
||||
if not search_record(nodedatum, options, session):
|
||||
break
|
||||
for nodedatum in alldata:
|
||||
if not allthere and not search_record(nodedatum, options, session):
|
||||
sys.stderr.write(
|
||||
"Could not match the following data: " +
|
||||
repr(nodedatum) + '\n')
|
||||
@@ -230,8 +237,12 @@ def import_csv(options, session):
|
||||
print('Defined ' + res['created'])
|
||||
else:
|
||||
print(repr(res))
|
||||
child = os.fork()
|
||||
if child:
|
||||
continue
|
||||
for mac in maclist:
|
||||
for res in session.update('/discovery/by-mac/{0}'.format(mac),
|
||||
mysess = client.Command()
|
||||
for res in mysess.update('/discovery/by-mac/{0}'.format(mac),
|
||||
{'node': nodename}):
|
||||
if 'error' in res:
|
||||
sys.stderr.write(res['error'] + '\n')
|
||||
@@ -241,6 +252,12 @@ def import_csv(options, session):
|
||||
print('Discovered ' + res['assigned'])
|
||||
else:
|
||||
print(repr(res))
|
||||
sys.exit(0)
|
||||
while True:
|
||||
try:
|
||||
os.wait()
|
||||
except ChildProcessError:
|
||||
break
|
||||
if exitcode:
|
||||
sys.exit(exitcode)
|
||||
|
||||
|
||||
@@ -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} '
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -58,6 +58,7 @@ def run():
|
||||
|
||||
currprocs = 0
|
||||
all = set([])
|
||||
poller = select.epoll()
|
||||
pipedesc = {}
|
||||
pendingexecs = deque()
|
||||
exitcode = 0
|
||||
@@ -86,21 +87,28 @@ def run():
|
||||
if currprocs < concurrentprocs:
|
||||
currprocs += 1
|
||||
if options.origname:
|
||||
run_cmdv(node, cmdv, all, pipedesc)
|
||||
run_cmdv(node, cmdv, all, poller, pipedesc)
|
||||
else:
|
||||
run_cmdv(pingnode, cmdv, all, pipedesc)
|
||||
run_cmdv(pingnode, cmdv, all, poller, pipedesc)
|
||||
else:
|
||||
pendingexecs.append((pingnode, cmdv))
|
||||
if options.origname:
|
||||
pendingexecs.append((node, cmdv))
|
||||
else:
|
||||
pendingexecs.append((pingnode, cmdv))
|
||||
if not all or exitcode:
|
||||
sys.exit(exitcode)
|
||||
rdy, _, _ = select.select(all, [], [], 10)
|
||||
rdy = poller.poll(10)
|
||||
while all:
|
||||
pernodeout = {}
|
||||
for r in rdy:
|
||||
r = r[0]
|
||||
desc = pipedesc[r]
|
||||
r = desc['file']
|
||||
node = desc['node']
|
||||
data = True
|
||||
while data and select.select([r], [], [], 0)[0]:
|
||||
singlepoller = select.epoll()
|
||||
singlepoller.register(r, select.EPOLLIN)
|
||||
while data and singlepoller.poll(0):
|
||||
data = r.readline()
|
||||
if not data:
|
||||
pop = desc['popen']
|
||||
@@ -108,6 +116,7 @@ def run():
|
||||
if ret is not None:
|
||||
exitcode = exitcode | ret
|
||||
all.discard(r)
|
||||
poller.unregister(r)
|
||||
r.close()
|
||||
if desc['type'] == 'stdout':
|
||||
if ret:
|
||||
@@ -116,7 +125,8 @@ def run():
|
||||
print('{0}: ping'.format(node))
|
||||
if pendingexecs:
|
||||
node, cmdv = pendingexecs.popleft()
|
||||
run_cmdv(node, cmdv, all, pipedesc)
|
||||
run_cmdv(node, cmdv, all, poller, pipedesc)
|
||||
singlepoller.close()
|
||||
for node in sortutil.natural_sort(pernodeout):
|
||||
for line in pernodeout[node]:
|
||||
line = client.stringify(line)
|
||||
@@ -126,19 +136,21 @@ def run():
|
||||
sys.stdout.write('{0}: {1}'.format(node, line))
|
||||
sys.stdout.flush()
|
||||
if all:
|
||||
rdy, _, _ = select.select(all, [], [], 10)
|
||||
rdy = poller.poll(10)
|
||||
sys.exit(exitcode)
|
||||
|
||||
|
||||
def run_cmdv(node, cmdv, all, pipedesc):
|
||||
def run_cmdv(node, cmdv, all, poller, pipedesc):
|
||||
nopen = subprocess.Popen(
|
||||
cmdv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
pipedesc[nopen.stdout] = {'node': node, 'popen': nopen,
|
||||
'type': 'stdout'}
|
||||
pipedesc[nopen.stderr] = {'node': node, 'popen': nopen,
|
||||
'type': 'stderr'}
|
||||
pipedesc[nopen.stdout.fileno()] = {'node': node, 'popen': nopen,
|
||||
'type': 'stdout', 'file': nopen.stdout}
|
||||
pipedesc[nopen.stderr.fileno()] = {'node': node, 'popen': nopen,
|
||||
'type': 'stderr', 'file': nopen.stderr}
|
||||
all.add(nopen.stdout)
|
||||
poller.register(nopen.stdout, select.EPOLLIN)
|
||||
all.add(nopen.stderr)
|
||||
poller.register(nopen.stderr, select.EPOLLIN)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2016-2017 Lenovo
|
||||
@@ -67,6 +67,7 @@ def run():
|
||||
|
||||
currprocs = 0
|
||||
all = set([])
|
||||
poller = select.epoll()
|
||||
pipedesc = {}
|
||||
pendingexecs = deque()
|
||||
exitcode = 0
|
||||
@@ -84,19 +85,23 @@ def run():
|
||||
cmdv = shlex.split(cmd)
|
||||
if currprocs < concurrentprocs:
|
||||
currprocs += 1
|
||||
run_cmdv(node, cmdv, all, pipedesc)
|
||||
run_cmdv(node, cmdv, all, poller, pipedesc)
|
||||
else:
|
||||
pendingexecs.append((node, cmdv))
|
||||
if not all or exitcode:
|
||||
sys.exit(exitcode)
|
||||
rdy, _, _ = select.select(all, [], [], 10)
|
||||
rdy = poller.poll(10)
|
||||
while all:
|
||||
pernodeout = {}
|
||||
for r in rdy:
|
||||
r = r[0]
|
||||
desc = pipedesc[r]
|
||||
r = desc['file']
|
||||
node = desc['node']
|
||||
data = True
|
||||
while data and select.select([r], [], [], 0)[0]:
|
||||
singlepoller = select.epoll()
|
||||
singlepoller.register(r, select.EPOLLIN)
|
||||
while data and singlepoller.poll(0):
|
||||
data = r.readline()
|
||||
if data:
|
||||
if desc['type'] == 'stdout':
|
||||
@@ -116,10 +121,12 @@ def run():
|
||||
if ret is not None:
|
||||
exitcode = exitcode | ret
|
||||
all.discard(r)
|
||||
poller.unregister(r)
|
||||
r.close()
|
||||
if desc['type'] == 'stdout' and pendingexecs:
|
||||
node, cmdv = pendingexecs.popleft()
|
||||
run_cmdv(node, cmdv, all, pipedesc)
|
||||
run_cmdv(node, cmdv, all, poller, pipedesc)
|
||||
singlepoller.close()
|
||||
for node in sortutil.natural_sort(pernodeout):
|
||||
for line in pernodeout[node]:
|
||||
line = client.stringify(line)
|
||||
@@ -129,11 +136,11 @@ def run():
|
||||
sys.stdout.write('{0}: {1}'.format(node, line))
|
||||
sys.stdout.flush()
|
||||
if all:
|
||||
rdy, _, _ = select.select(all, [], [], 10)
|
||||
rdy = poller.poll(10)
|
||||
sys.exit(exitcode)
|
||||
|
||||
|
||||
def run_cmdv(node, cmdv, all, pipedesc):
|
||||
def run_cmdv(node, cmdv, all, poller, pipedesc):
|
||||
try:
|
||||
nopen = subprocess.Popen(
|
||||
cmdv, stdin=devnull, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
@@ -142,13 +149,14 @@ def run_cmdv(node, cmdv, all, pipedesc):
|
||||
sys.stderr.write('{0}: Unable to find local executable file "{1}"'.format(node, cmdv[0]))
|
||||
return
|
||||
raise
|
||||
pipedesc[nopen.stdout] = {'node': node, 'popen': nopen,
|
||||
'type': 'stdout'}
|
||||
pipedesc[nopen.stderr] = {'node': node, 'popen': nopen,
|
||||
'type': 'stderr'}
|
||||
pipedesc[nopen.stdout.fileno()] = {'node': node, 'popen': nopen,
|
||||
'type': 'stdout', 'file': nopen.stdout}
|
||||
pipedesc[nopen.stderr.fileno()] = {'node': node, 'popen': nopen,
|
||||
'type': 'stderr', 'file': nopen.stderr}
|
||||
all.add(nopen.stdout)
|
||||
poller.register(nopen.stdout, select.EPOLLIN)
|
||||
all.add(nopen.stderr)
|
||||
|
||||
poller.register(nopen.stderr, select.EPOLLIN)
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2016-2017 Lenovo
|
||||
@@ -109,6 +109,7 @@ def run():
|
||||
ex = exp.get('databynode', ())
|
||||
for node in ex:
|
||||
cmdparms.append((node, ex[node]['value']))
|
||||
poller = select.epoll()
|
||||
for node, cmd in cmdparms:
|
||||
sshnode = nodemap.get(node, node)
|
||||
if not isinstance(cmd, str) and not isinstance(cmd, bytes):
|
||||
@@ -121,19 +122,23 @@ def run():
|
||||
cmdv += [sshnode, cmd]
|
||||
if currprocs < concurrentprocs:
|
||||
currprocs += 1
|
||||
run_cmdv(node, cmdv, all, pipedesc)
|
||||
run_cmdv(node, cmdv, all, poller, pipedesc)
|
||||
else:
|
||||
pendingexecs.append((node, cmdv))
|
||||
if not all or exitcode:
|
||||
sys.exit(exitcode)
|
||||
rdy, _, _ = select.select(all, [], [], 10)
|
||||
rdy = poller.poll(10)
|
||||
while all:
|
||||
pernodeout = {}
|
||||
for r in rdy:
|
||||
r = r[0]
|
||||
desc = pipedesc[r]
|
||||
r = desc['file']
|
||||
node = desc['node']
|
||||
data = True
|
||||
while data and select.select([r], [], [], 0)[0]:
|
||||
singlepoller = select.epoll()
|
||||
singlepoller.register(r, select.EPOLLIN)
|
||||
while data and singlepoller.poll(0):
|
||||
data = r.readline()
|
||||
if data:
|
||||
if desc['type'] == 'stdout':
|
||||
@@ -153,10 +158,12 @@ def run():
|
||||
if ret is not None:
|
||||
exitcode = exitcode | ret
|
||||
all.discard(r)
|
||||
poller.unregister(r)
|
||||
r.close()
|
||||
if desc['type'] == 'stdout' and pendingexecs:
|
||||
node, cmdv = pendingexecs.popleft()
|
||||
run_cmdv(node, cmdv, all, pipedesc)
|
||||
run_cmdv(node, cmdv, all, poller, pipedesc)
|
||||
singlepoller.close()
|
||||
for node in sortutil.natural_sort(pernodeout):
|
||||
for line in pernodeout[node]:
|
||||
line = client.stringify(line)
|
||||
@@ -167,19 +174,21 @@ def run():
|
||||
sys.stdout.write('{0}: {1}'.format(node, line))
|
||||
sys.stdout.flush()
|
||||
if all:
|
||||
rdy, _, _ = select.select(all, [], [], 10)
|
||||
rdy = poller.poll(10)
|
||||
sys.exit(exitcode)
|
||||
|
||||
|
||||
def run_cmdv(node, cmdv, all, pipedesc):
|
||||
def run_cmdv(node, cmdv, all, poller, pipedesc):
|
||||
nopen = subprocess.Popen(
|
||||
cmdv, stdin=devnull, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
pipedesc[nopen.stdout] = {'node': node, 'popen': nopen,
|
||||
'type': 'stdout'}
|
||||
pipedesc[nopen.stderr] = {'node': node, 'popen': nopen,
|
||||
'type': 'stderr'}
|
||||
pipedesc[nopen.stdout.fileno()] = {'node': node, 'popen': nopen,
|
||||
'type': 'stdout', 'file': nopen.stdout}
|
||||
pipedesc[nopen.stderr.fileno()] = {'node': node, 'popen': nopen,
|
||||
'type': 'stderr', 'file': nopen.stderr}
|
||||
all.add(nopen.stdout)
|
||||
all.add(nopen.stderr)
|
||||
poller.register(nopen.stdout, select.EPOLLIN)
|
||||
poller.register(nopen.stderr, select.EPOLLIN)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -19,6 +19,7 @@ import argparse
|
||||
import base64
|
||||
import csv
|
||||
import io
|
||||
import os
|
||||
import numpy as np
|
||||
import sys
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ def stringify(instr):
|
||||
# Normalize unicode and bytes to 'str', correcting for
|
||||
# current python version
|
||||
if isinstance(instr, bytes) and not isinstance(instr, str):
|
||||
return instr.decode('utf-8')
|
||||
return instr.decode('utf-8', errors='replace')
|
||||
elif not isinstance(instr, bytes) and not isinstance(instr, str):
|
||||
return instr.encode('utf-8')
|
||||
return instr
|
||||
@@ -464,8 +464,8 @@ def printattributes(session, requestargs, showtype, nodetype, noderange, options
|
||||
|
||||
def _sort_attrib(k):
|
||||
if isinstance(k[1], dict) and k[1].get('sortid', None) is not None:
|
||||
return k[1]['sortid']
|
||||
return k[0]
|
||||
return sortutil.naturalize_string('{}'.format(k[1]['sortid']))
|
||||
return sortutil.naturalize_string(k[0])
|
||||
|
||||
def print_attrib_path(path, session, requestargs, options, rename=None, attrprefix=None):
|
||||
exitcode = 0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
%define name confluent_client
|
||||
%define version #VERSION#
|
||||
%define fversion %{lua:
|
||||
sv, _ = string.gsub("#VERSION#", "[~+]", "-")
|
||||
sv, _ = string.gsub("#VERSION#", "[~]", "-")
|
||||
print(sv)
|
||||
}
|
||||
%define release 1
|
||||
|
||||
@@ -19,7 +19,9 @@ interval of 1 second is used.
|
||||
## OPTIONS
|
||||
|
||||
* `-c`, `--csv`:
|
||||
Organize output into CSV format, one sensor per column.
|
||||
Organize output into CSV format, one sensor per column. Note that while normally nodesensors reports
|
||||
sensors in order as returned by server, CSV output enforces consistency by sorting after receiving
|
||||
the results, which may have a different ordering than non-CSV usage of nodesensors.
|
||||
|
||||
* `-i`, `--interval`=**SECONDS**:
|
||||
Repeat data gathering waiting, waiting the specified time between samples. Unless `-n` is
|
||||
|
||||
@@ -3,6 +3,7 @@ try:
|
||||
import http.client as client
|
||||
except ImportError:
|
||||
import httplib as client
|
||||
import base64
|
||||
import ctypes
|
||||
import ctypes.util
|
||||
import glob
|
||||
@@ -15,7 +16,13 @@ import sys
|
||||
import struct
|
||||
import time
|
||||
import re
|
||||
import json
|
||||
import hashlib
|
||||
try:
|
||||
import json
|
||||
import hmac
|
||||
except ImportError:
|
||||
json = None
|
||||
hmac = None
|
||||
|
||||
class InvalidApiKey(Exception):
|
||||
pass
|
||||
@@ -73,7 +80,7 @@ def get_my_addresses():
|
||||
return addrs
|
||||
|
||||
|
||||
def scan_confluents():
|
||||
def scan_confluents(confuuid=None):
|
||||
srvs = {}
|
||||
s6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
||||
s6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
|
||||
@@ -85,12 +92,13 @@ def scan_confluents():
|
||||
s4.bind(('0.0.0.0', 1900))
|
||||
doneidxs = set([])
|
||||
msg = 'M-SEARCH * HTTP/1.1\r\nST: urn:xcat.org:service:confluent:'
|
||||
with open('/etc/confluent/confluent.deploycfg') as dcfg:
|
||||
for line in dcfg.read().split('\n'):
|
||||
if line.startswith('confluent_uuid:'):
|
||||
confluentuuid = line.split(': ')[1]
|
||||
msg += '/confluentuuid=' + confluentuuid
|
||||
break
|
||||
if not confuuid:
|
||||
with open('/etc/confluent/confluent.deploycfg') as dcfg:
|
||||
for line in dcfg.read().split('\n'):
|
||||
if line.startswith('confluent_uuid:'):
|
||||
confluentuuid = line.split(': ')[1]
|
||||
msg += '/confluentuuid=' + confluentuuid
|
||||
break
|
||||
try:
|
||||
with open('/sys/devices/virtual/dmi/id/product_uuid') as uuidin:
|
||||
msg += '/uuid=' + uuidin.read().strip()
|
||||
@@ -127,6 +135,7 @@ def scan_confluents():
|
||||
srvlist = []
|
||||
if r:
|
||||
r = r[0]
|
||||
nodename = None
|
||||
while r:
|
||||
for s in r:
|
||||
(rsp, peer) = s.recvfrom(9000)
|
||||
@@ -134,6 +143,7 @@ def scan_confluents():
|
||||
current = None
|
||||
for line in rsp:
|
||||
if line.startswith(b'NODENAME: '):
|
||||
nodename = line.replace(b'NODENAME: ', b'').strip().decode('utf8')
|
||||
current = {}
|
||||
elif line.startswith(b'DEFAULTNET: 1'):
|
||||
current['isdefault'] = True
|
||||
@@ -149,16 +159,32 @@ def scan_confluents():
|
||||
r = select.select((s4, s6), (), (), 2)
|
||||
if r:
|
||||
r = r[0]
|
||||
if not os.path.exists('/etc/confluent/confluent.info'):
|
||||
with open('/etc/confluent/confluent.info', 'w+') as cinfo:
|
||||
if nodename:
|
||||
cinfo.write('NODENAME: {0}\n'.format(nodename))
|
||||
for srv in srvlist:
|
||||
cinfo.write('MANAGER: {0}\n'.format(srv))
|
||||
return srvlist, srvs
|
||||
|
||||
|
||||
def get_net_apikey(nodename, mgr):
|
||||
def get_net_apikey(nodename, mgr, hmackey=None, confuuid=None):
|
||||
alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./'
|
||||
newpass = ''.join([alpha[x >> 2] for x in bytearray(os.urandom(32))])
|
||||
salt = '$5$' + ''.join([alpha[x >> 2] for x in bytearray(os.urandom(8))])
|
||||
newpass = newpass.encode('utf8')
|
||||
salt = salt.encode('utf8')
|
||||
crypted = c_crypt(newpass, salt)
|
||||
if hmackey:
|
||||
hmacvalue = hmac.new(hmackey.encode('utf8'), crypted, hashlib.sha256).digest()
|
||||
hmacvalue = base64.b64encode(hmacvalue).decode('utf8')
|
||||
client = HTTPSClient(host=mgr, phmac=hmacvalue, nodename=nodename, confuuid=confuuid)
|
||||
try:
|
||||
status, rsp = client.grab_url_with_status('/confluent-api/self/registerapikey', data=crypted, returnrsp=True)
|
||||
if status == 200:
|
||||
return newpass.decode('utf8')
|
||||
except Exception:
|
||||
pass
|
||||
for addrinfo in socket.getaddrinfo(mgr, 13001, 0, socket.SOCK_STREAM):
|
||||
try:
|
||||
clisock = socket.socket(addrinfo[0], addrinfo[1])
|
||||
@@ -196,7 +222,7 @@ def get_net_apikey(nodename, mgr):
|
||||
return ''
|
||||
|
||||
|
||||
def get_apikey(nodename, hosts, errout=None):
|
||||
def get_apikey(nodename, hosts, errout=None, hmackey=None, confuuid=None):
|
||||
apikey = ""
|
||||
if os.path.exists('/etc/confluent/confluent.apikey'):
|
||||
apikey = open('/etc/confluent/confluent.apikey').read().strip()
|
||||
@@ -205,16 +231,16 @@ def get_apikey(nodename, hosts, errout=None):
|
||||
while not apikey:
|
||||
for host in hosts:
|
||||
try:
|
||||
apikey = get_net_apikey(nodename, host)
|
||||
apikey = get_net_apikey(nodename, host, hmackey=hmackey, confuuid=confuuid)
|
||||
except OSError:
|
||||
apikey = None
|
||||
if apikey:
|
||||
break
|
||||
else:
|
||||
srvlist, _ = scan_confluents()
|
||||
srvlist, _ = scan_confluents(confuuid=confuuid)
|
||||
for host in srvlist:
|
||||
try:
|
||||
apikey = get_net_apikey(nodename, host)
|
||||
apikey = get_net_apikey(nodename, host, hmackey=hmackey, confuuid=confuuid)
|
||||
except OSError:
|
||||
apikey = None
|
||||
if apikey:
|
||||
@@ -232,35 +258,43 @@ def get_apikey(nodename, hosts, errout=None):
|
||||
return apikey
|
||||
|
||||
class HTTPSClient(client.HTTPConnection, object):
|
||||
def __init__(self, usejson=False, port=443, host=None, errout=None, phmac=None, checkonly=False):
|
||||
def __init__(self, usejson=False, port=443, host=None, errout=None, phmac=None, checkonly=False, hmackey=None, nodename=None, confuuid=None):
|
||||
self.ignorehosts = set([])
|
||||
self.phmac = phmac
|
||||
self.hmackey = hmackey
|
||||
self.confuuid = confuuid
|
||||
self.errout = None
|
||||
self.stdheaders = {}
|
||||
if nodename:
|
||||
self.stdheaders['CONFLUENT_NODENAME'] = nodename
|
||||
if errout:
|
||||
self.errout = open(errout, 'w')
|
||||
self.errout.flush()
|
||||
self.stdheaders = {}
|
||||
mgtiface = None
|
||||
if usejson:
|
||||
self.stdheaders['ACCEPT'] = 'application/json'
|
||||
if host:
|
||||
self.hosts = [host]
|
||||
with open('/etc/confluent/confluent.info') as cinfo:
|
||||
info = cinfo.read().split('\n')
|
||||
for line in info:
|
||||
if line.startswith('NODENAME:'):
|
||||
node = line.split(' ')[1]
|
||||
self.stdheaders['CONFLUENT_NODENAME'] = node
|
||||
if not nodename:
|
||||
with open('/etc/confluent/confluent.info') as cinfo:
|
||||
info = cinfo.read().split('\n')
|
||||
for line in info:
|
||||
if line.startswith('NODENAME:'):
|
||||
nodename = line.split(' ')[1]
|
||||
self.stdheaders['CONFLUENT_NODENAME'] = nodename
|
||||
else:
|
||||
self.hosts = []
|
||||
info = open('/etc/confluent/confluent.info').read().split('\n')
|
||||
try:
|
||||
info = open('/etc/confluent/confluent.info').read().split('\n')
|
||||
except Exception:
|
||||
info = []
|
||||
havedefault = '0'
|
||||
plainhost = ''
|
||||
for line in info:
|
||||
host = ''
|
||||
if line.startswith('NODENAME:'):
|
||||
node = line.split(' ')[1]
|
||||
self.stdheaders['CONFLUENT_NODENAME'] = node
|
||||
nodename = line.split(' ')[1]
|
||||
self.stdheaders['CONFLUENT_NODENAME'] = nodename
|
||||
if line.startswith('MANAGER:') and not host:
|
||||
host = line.split(' ')[1]
|
||||
self.hosts.append(host)
|
||||
@@ -295,15 +329,14 @@ class HTTPSClient(client.HTTPConnection, object):
|
||||
if plainhost and not self.hosts:
|
||||
self.hosts.append(plainhost)
|
||||
if self.phmac:
|
||||
with open(phmac, 'r') as hmacin:
|
||||
self.stdheaders['CONFLUENT_CRYPTHMAC'] = hmacin.read()
|
||||
self.stdheaders['CONFLUENT_CRYPTHMAC'] = self.phmac
|
||||
elif not checkonly:
|
||||
self.stdheaders['CONFLUENT_APIKEY'] = get_apikey(node, self.hosts, errout=self.errout)
|
||||
self.stdheaders['CONFLUENT_APIKEY'] = get_apikey(nodename, self.hosts, errout=self.errout, hmackey=hmackey, confuuid=self.confuuid)
|
||||
if mgtiface:
|
||||
self.stdheaders['CONFLUENT_MGTIFACE'] = mgtiface
|
||||
self.port = port
|
||||
self.host = None
|
||||
self.node = node
|
||||
self.node = nodename
|
||||
host = self.check_connections()
|
||||
client.HTTPConnection.__init__(self, host, port)
|
||||
self.connect()
|
||||
@@ -343,7 +376,7 @@ class HTTPSClient(client.HTTPConnection, object):
|
||||
continue
|
||||
break
|
||||
if not foundsrv:
|
||||
srvlist, srvs = scan_confluents()
|
||||
srvlist, srvs = scan_confluents(self.confuuid)
|
||||
hosts = []
|
||||
for srv in srvlist:
|
||||
if srvs[srv].get('isdefault', False):
|
||||
@@ -417,7 +450,7 @@ class HTTPSClient(client.HTTPConnection, object):
|
||||
with open('/etc/confluent/confluent.apikey', 'w+') as akfile:
|
||||
akfile.write('')
|
||||
self.stdheaders['CONFLUENT_APIKEY'] = get_apikey(
|
||||
self.node, [self.host], errout=self.errout)
|
||||
self.node, [self.host], errout=self.errout, hmackey=self.hmackey, confuuid=self.confuuid)
|
||||
if rsp.status == 503: # confluent is down, but the server running confluent is otherwise up
|
||||
authed = False
|
||||
self.ignorehosts.add(self.host)
|
||||
@@ -464,7 +497,7 @@ def get_available_nics():
|
||||
parts = re.split(r'\s{2,}', line.strip())
|
||||
if len(parts) >= 5:
|
||||
nic_name = parts[0]
|
||||
nic_status = parts[4] # "Link Status" este al 5-lea câmp
|
||||
nic_status = parts[4] # "Link Status" is the 5th field
|
||||
available_nics[nic_name] = nic_status
|
||||
|
||||
return available_nics
|
||||
@@ -509,7 +542,7 @@ if __name__ == '__main__':
|
||||
try:
|
||||
fix_vswitch()
|
||||
except Exception as e:
|
||||
print(f"fix_vswitch() error: {e}")
|
||||
print("fix_vswitch() error: {}".format(e))
|
||||
sys.argv.remove('-f')
|
||||
sys.exit(0)
|
||||
usejson = False
|
||||
@@ -546,8 +579,24 @@ if __name__ == '__main__':
|
||||
phmac = sys.argv.index('-p')
|
||||
sys.argv.pop(phmac)
|
||||
phmac = sys.argv.pop(phmac)
|
||||
with open(phmac, 'r') as hmacin:
|
||||
phmac = hmacin.read()
|
||||
except ValueError:
|
||||
phmac = None
|
||||
try:
|
||||
identfile = sys.argv.index('-i')
|
||||
sys.argv.pop(identfile)
|
||||
identfile = sys.argv.pop(identfile)
|
||||
with open(identfile) as idin:
|
||||
data = idin.read()
|
||||
identinfo = json.loads(data)
|
||||
nodename = identinfo.get('nodename', None)
|
||||
hmackey = identinfo.get('apitoken', None)
|
||||
confuuid = identinfo.get('confluent_uuid', None)
|
||||
except ValueError:
|
||||
hmackey = None
|
||||
nodename = None
|
||||
confuuid = None
|
||||
try:
|
||||
checkonly = False
|
||||
idxit = sys.argv.index('-c')
|
||||
@@ -559,7 +608,7 @@ if __name__ == '__main__':
|
||||
data = open(sys.argv[-1]).read()
|
||||
if outbin:
|
||||
with open(outbin, 'ab+') as outf:
|
||||
reader = HTTPSClient(usejson=usejson, errout=errout).grab_url(
|
||||
reader = HTTPSClient(usejson=usejson, errout=errout, hmackey=hmackey, nodename=nodename, confuuid=confuuid).grab_url(
|
||||
sys.argv[1], data, returnrsp=True)
|
||||
chunk = reader.read(16384)
|
||||
while chunk:
|
||||
@@ -567,7 +616,7 @@ if __name__ == '__main__':
|
||||
chunk = reader.read(16384)
|
||||
sys.exit(0)
|
||||
|
||||
mclient = HTTPSClient(usejson, errout=errout, phmac=phmac, checkonly=checkonly)
|
||||
mclient = HTTPSClient(usejson, errout=errout, phmac=phmac, checkonly=checkonly, hmackey=hmackey, nodename=nodename, confuuid=confuuid)
|
||||
if waitfor:
|
||||
status = 201
|
||||
while status != waitfor:
|
||||
|
||||
@@ -164,6 +164,9 @@ class NetplanManager(object):
|
||||
if curraddr not in currips:
|
||||
needcfgwrite = True
|
||||
currips.append(curraddr)
|
||||
if stgs.get('mtu', None):
|
||||
devdict = self.getcfgarrpath([devname])
|
||||
devdict['mtu'] = int(stgs['mtu'])
|
||||
gws = []
|
||||
gws.append(stgs.get('ipv4_gateway', None))
|
||||
gws.append(stgs.get('ipv6_gateway', None))
|
||||
@@ -381,6 +384,8 @@ class NetworkManager(object):
|
||||
cmdargs['ipv4.gateway'] = stgs['ipv4_gateway']
|
||||
if stgs.get('ipv6_gateway', None):
|
||||
cmdargs['ipv6.gateway'] = stgs['ipv6_gateway']
|
||||
if stgs.get('mtu', None):
|
||||
cmdargs['802-3-ethernet.mtu'] = stgs['mtu']
|
||||
dnsips = self.deploycfg.get('nameservers', [])
|
||||
if not dnsips:
|
||||
dnsips = []
|
||||
@@ -406,7 +411,7 @@ class NetworkManager(object):
|
||||
cargs = []
|
||||
for arg in cmdargs:
|
||||
cargs.append(arg)
|
||||
cargs.append(cmdargs[arg])
|
||||
cargs.append('{}'.format(cmdargs[arg]))
|
||||
if stgs['team_mode'] in self.bondtypes:
|
||||
stgs['team_mode'] = self.bondtypes[stgs['team_mode']]
|
||||
subprocess.check_call(['nmcli', 'c', 'add', 'type', 'bond', 'con-name', cname, 'connection.interface-name', cname, 'bond.options', 'miimon=100,mode={}'.format(stgs['team_mode'])] + cargs)
|
||||
@@ -441,7 +446,7 @@ class NetworkManager(object):
|
||||
cargs = []
|
||||
for arg in cmdargs:
|
||||
cargs.append(arg)
|
||||
cargs.append(cmdargs[arg])
|
||||
cargs.append('{}'.format(cmdargs[arg]))
|
||||
if u:
|
||||
subprocess.check_call(['nmcli', 'c', 'm', u, 'connection.interface-name', iname] + cargs)
|
||||
subprocess.check_call(['nmcli', 'c', 'u', u])
|
||||
@@ -455,6 +460,9 @@ class NetworkManager(object):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
checktarg = None
|
||||
if '-c' in sys.argv:
|
||||
checktarg = sys.argv[sys.argv.index('-c') + 1]
|
||||
havefirewall = subprocess.call(['systemctl', 'status', 'firewalld'])
|
||||
havefirewall = havefirewall == 0
|
||||
if havefirewall:
|
||||
@@ -540,7 +548,7 @@ if __name__ == '__main__':
|
||||
rm_tmp_llas(tmpllas)
|
||||
if os.path.exists('/usr/sbin/netplan'):
|
||||
nm = NetplanManager(dc)
|
||||
if os.path.exists('/usr/bin/nmcli'):
|
||||
elif os.path.exists('/usr/bin/nmcli'):
|
||||
nm = NetworkManager(devtypes, dc)
|
||||
elif os.path.exists('/usr/sbin/wicked'):
|
||||
nm = WickedManager()
|
||||
@@ -562,4 +570,27 @@ if __name__ == '__main__':
|
||||
if havefirewall:
|
||||
subprocess.check_call(['systemctl', 'start', 'firewalld'])
|
||||
await_tentative()
|
||||
maxwait = 10
|
||||
while maxwait:
|
||||
try:
|
||||
tclient = apiclient.HTTPSClient(checkonly=True)
|
||||
tclient.check_connections()
|
||||
break
|
||||
except Exception:
|
||||
maxwait -= 1
|
||||
time.sleep(1)
|
||||
maxwait = 10
|
||||
if checktarg:
|
||||
while maxwait:
|
||||
try:
|
||||
addrinf = socket.getaddrinfo(checktarg, 443)[0]
|
||||
psock = socket.socket(addrinf[0], socket.SOCK_STREAM)
|
||||
psock.settimeout(10)
|
||||
psock.connect(addrinf[4])
|
||||
psock.close()
|
||||
break
|
||||
except Exception:
|
||||
maxwait -= 1
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ for pubkey in /etc/ssh/ssh_host*key.pub; do
|
||||
continue
|
||||
fi
|
||||
certfile=${pubkey/.pub/-cert.pub}
|
||||
rm $certfile
|
||||
echo -n > $certfile
|
||||
confluentpython $confapiclient /confluent-api/self/sshcert $pubkey -o $certfile
|
||||
done
|
||||
if [ -d /etc/ssh/sshd_config.d/ -a ! -e /etc/ssh/sshd_config.d/90-confluent.conf ]; then
|
||||
@@ -17,6 +17,13 @@ if [ -d /etc/ssh/sshd_config.d/ -a ! -e /etc/ssh/sshd_config.d/90-confluent.conf
|
||||
echo HostbasedAuthentication yes >> /etc/ssh/sshd_config.d/90-confluent.conf
|
||||
echo HostbasedUsesNameFromPacketOnly yes >> /etc/ssh/sshd_config.d/90-confluent.conf
|
||||
echo IgnoreRhosts no >> /etc/ssh/sshd_config.d/90-confluent.conf
|
||||
elif [ ! -d /etc/ssh/sshd_config.d/ ] && ! grep HostCertificate /etc/ssh/sshd_config > /dev/null; then
|
||||
for cert in /etc/ssh/ssh*-cert.pub; do
|
||||
echo HostCertificate $cert >> /etc/ssh/sshd_config
|
||||
done
|
||||
echo HostbasedAuthentication yes >> /etc/ssh/sshd_config
|
||||
echo HostbasedUsesNameFromPacketOnly yes >> /etc/ssh/sshd_config
|
||||
echo IgnoreRhosts no >> /etc/ssh/sshd_config
|
||||
fi
|
||||
|
||||
TMPDIR=$(mktemp -d)
|
||||
@@ -25,13 +32,20 @@ confluentpython $confapiclient /confluent-public/site/initramfs.tgz -o initramfs
|
||||
tar xf initramfs.tgz
|
||||
for ca in ssh/*.ca; do
|
||||
LINE=$(cat $ca)
|
||||
cp -af /etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts.new
|
||||
grep -v "$LINE" /etc/ssh/ssh_known_hosts > /etc/ssh/ssh_known_hosts.new
|
||||
if [ -z "$LINE" ]; then continue; fi
|
||||
if [ -f /etc/ssh/ssh_known_hosts ]; then
|
||||
cp -af /etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts.new
|
||||
grep -v "$LINE" /etc/ssh/ssh_known_hosts > /etc/ssh/ssh_known_hosts.new
|
||||
fi
|
||||
echo '@cert-authority *' $LINE >> /etc/ssh/ssh_known_hosts.new
|
||||
mv /etc/ssh/ssh_known_hosts.new /etc/ssh/ssh_known_hosts
|
||||
done
|
||||
mkdir -p /root/.ssh/
|
||||
chmod 700 /root/.ssh/
|
||||
touch /root/.ssh/authorized_keys
|
||||
for pubkey in ssh/*.*pubkey; do
|
||||
LINE=$(cat $pubkey)
|
||||
if [ -z "$LINE" ]; then continue; fi
|
||||
cp -af /root/.ssh/authorized_keys /root/.ssh/authorized_keys.new
|
||||
grep -v "$LINE" /root/.ssh/authorized_keys > /root/.ssh/authorized_keys.new
|
||||
echo "$LINE" >> /root/.ssh/authorized_keys.new
|
||||
@@ -41,3 +55,4 @@ confluentpython $confapiclient /confluent-api/self/nodelist | sed -e 's/^- //' >
|
||||
cat /etc/ssh/shosts.equiv > /root/.shosts
|
||||
cd -
|
||||
rm -rf $TMPDIR
|
||||
systemctl try-restart sshd
|
||||
|
||||
@@ -26,11 +26,19 @@ mkdir -p opt/confluent/bin
|
||||
mkdir -p stateless-bin
|
||||
cp -a el8bin/* .
|
||||
ln -s el8 el9
|
||||
for os in rhvh4 el7 genesis el8 suse15 ubuntu20.04 ubuntu22.04 ubuntu24.04 coreos el9; do
|
||||
ln -s el8 el10
|
||||
mv el10/initramfs/usr el10/initramfs/var
|
||||
cp -a debian debian13
|
||||
mkdir -p debian13/initramfs/usr
|
||||
mv debian13/initramfs/lib debian13/initramfs/usr/
|
||||
mv el10/initramfs/usr el10/initramfs/var
|
||||
for os in rhvh4 el7 genesis el8 suse15 debian debian13 ubuntu20.04 ubuntu22.04 ubuntu24.04 coreos el9 el10; do
|
||||
mkdir ${os}out
|
||||
cd ${os}out
|
||||
if [ -d ../${os}bin ]; then
|
||||
cp -a ../${os}bin/opt .
|
||||
elif [ $os = el10 ]; then
|
||||
cp -a ../el9bin/opt .
|
||||
else
|
||||
cp -a ../opt .
|
||||
fi
|
||||
@@ -40,11 +48,13 @@ for os in rhvh4 el7 genesis el8 suse15 ubuntu20.04 ubuntu22.04 ubuntu24.04 coreo
|
||||
mv ../addons.cpio .
|
||||
cd ..
|
||||
done
|
||||
for os in el7 el8 suse15 el9 ubuntu20.04; do
|
||||
for os in el7 el8 suse15 el9 el10 ubuntu20.04; do
|
||||
mkdir ${os}disklessout
|
||||
cd ${os}disklessout
|
||||
if [ -d ../${os}bin ]; then
|
||||
cp -a ../${os}bin/opt .
|
||||
elif [ $os = el10 ]; then
|
||||
cp -a ../el9bin/opt .
|
||||
else
|
||||
cp -a ../opt .
|
||||
fi
|
||||
@@ -76,7 +86,7 @@ cp -a esxi7 esxi8
|
||||
%install
|
||||
mkdir -p %{buildroot}/opt/confluent/share/licenses/confluent_osdeploy/
|
||||
#cp LICENSE %{buildroot}/opt/confluent/share/licenses/confluent_osdeploy/
|
||||
for os in rhvh4 el7 el8 el9 genesis suse15 ubuntu20.04 ubuntu22.04 ubuntu24.04 esxi6 esxi7 esxi8 coreos; do
|
||||
for os in rhvh4 el7 el8 el9 el10 debian debian13 genesis suse15 ubuntu20.04 ubuntu22.04 ubuntu24.04 esxi6 esxi7 esxi8 coreos; do
|
||||
mkdir -p %{buildroot}/opt/confluent/lib/osdeploy/$os/initramfs/aarch64/
|
||||
cp ${os}out/addons.* %{buildroot}/opt/confluent/lib/osdeploy/$os/initramfs/aarch64/
|
||||
if [ -d ${os}disklessout ]; then
|
||||
|
||||
@@ -29,8 +29,11 @@ This contains support utilities for enabling deployment of x86_64 architecture s
|
||||
#cd ..
|
||||
ln -s el8 el9
|
||||
cp -a el8 el10
|
||||
cp -a debian debian13
|
||||
mkdir -p debian13/initramfs/usr
|
||||
mv debian13/initramfs/lib debian13/initramfs/usr/
|
||||
mv el10/initramfs/usr el10/initramfs/var
|
||||
for os in rhvh4 el7 genesis el8 suse15 debian ubuntu18.04 ubuntu20.04 ubuntu22.04 ubuntu24.04 coreos el9 el10; do
|
||||
for os in rhvh4 el7 genesis el8 suse15 debian debian13 ubuntu18.04 ubuntu20.04 ubuntu22.04 ubuntu24.04 coreos el9 el10; do
|
||||
mkdir ${os}out
|
||||
cd ${os}out
|
||||
if [ -d ../${os}bin ]; then
|
||||
@@ -46,11 +49,13 @@ for os in rhvh4 el7 genesis el8 suse15 debian ubuntu18.04 ubuntu20.04 ubuntu22.0
|
||||
mv ../addons.cpio .
|
||||
cd ..
|
||||
done
|
||||
for os in el7 el8 suse15 el9 ubuntu20.04 ubuntu22.04 ubuntu24.04; do
|
||||
for os in el7 el8 suse15 el9 el10 ubuntu20.04 ubuntu22.04 ubuntu24.04; do
|
||||
mkdir ${os}disklessout
|
||||
cd ${os}disklessout
|
||||
if [ -d ../${os}bin ]; then
|
||||
cp -a ../${os}bin/opt .
|
||||
elif [ $os = el10 ]; then
|
||||
cp -a ../el9bin/opt .
|
||||
else
|
||||
cp -a ../el8bin/opt .
|
||||
fi
|
||||
@@ -84,7 +89,7 @@ cp -a esxi7 esxi9
|
||||
%install
|
||||
mkdir -p %{buildroot}/opt/confluent/share/licenses/confluent_osdeploy/
|
||||
cp LICENSE %{buildroot}/opt/confluent/share/licenses/confluent_osdeploy/
|
||||
for os in rhvh4 el7 el8 el9 el10 genesis suse15 ubuntu20.04 debian ubuntu18.04 ubuntu22.04 ubuntu24.04 esxi6 esxi7 esxi8 esxi9 coreos; do
|
||||
for os in rhvh4 el7 el8 el9 el10 genesis suse15 ubuntu20.04 debian debian13 ubuntu18.04 ubuntu22.04 ubuntu24.04 esxi6 esxi7 esxi8 esxi9 coreos; do
|
||||
mkdir -p %{buildroot}/opt/confluent/lib/osdeploy/$os/initramfs
|
||||
mkdir -p %{buildroot}/opt/confluent/lib/osdeploy/$os/profiles
|
||||
cp ${os}out/addons.* %{buildroot}/opt/confluent/lib/osdeploy/$os/initramfs
|
||||
|
||||
@@ -164,6 +164,9 @@ class NetplanManager(object):
|
||||
if curraddr not in currips:
|
||||
needcfgwrite = True
|
||||
currips.append(curraddr)
|
||||
if stgs.get('mtu', None):
|
||||
devdict = self.getcfgarrpath([devname])
|
||||
devdict['mtu'] = int(stgs['mtu'])
|
||||
gws = []
|
||||
gws.append(stgs.get('ipv4_gateway', None))
|
||||
gws.append(stgs.get('ipv6_gateway', None))
|
||||
@@ -381,6 +384,8 @@ class NetworkManager(object):
|
||||
cmdargs['ipv4.gateway'] = stgs['ipv4_gateway']
|
||||
if stgs.get('ipv6_gateway', None):
|
||||
cmdargs['ipv6.gateway'] = stgs['ipv6_gateway']
|
||||
if stgs.get('mtu', None):
|
||||
cmdargs['802-3-ethernet.mtu'] = stgs['mtu']
|
||||
dnsips = self.deploycfg.get('nameservers', [])
|
||||
if not dnsips:
|
||||
dnsips = []
|
||||
@@ -406,10 +411,10 @@ class NetworkManager(object):
|
||||
cargs = []
|
||||
for arg in cmdargs:
|
||||
cargs.append(arg)
|
||||
cargs.append(cmdargs[arg])
|
||||
cargs.append('{}'.format(cmdargs[arg]))
|
||||
if stgs['team_mode'] in self.bondtypes:
|
||||
stgs['team_mode'] = self.bondtypes[stgs['team_mode']]
|
||||
subprocess.check_call(['nmcli', 'c', 'add', 'type', 'bond', 'con-name', cname, 'connection.interface-name', cname, 'bond.options', 'mode={}'.format(stgs['team_mode'])] + cargs)
|
||||
subprocess.check_call(['nmcli', 'c', 'add', 'type', 'bond', 'con-name', cname, 'connection.interface-name', cname, 'bond.options', 'miimon=100,mode={}'.format(stgs['team_mode'])] + cargs)
|
||||
for iface in cfg['interfaces']:
|
||||
self.add_team_member(cname, iface)
|
||||
subprocess.check_call(['nmcli', 'c', 'u', cname])
|
||||
@@ -441,7 +446,7 @@ class NetworkManager(object):
|
||||
cargs = []
|
||||
for arg in cmdargs:
|
||||
cargs.append(arg)
|
||||
cargs.append(cmdargs[arg])
|
||||
cargs.append('{}'.format(cmdargs[arg]))
|
||||
if u:
|
||||
subprocess.check_call(['nmcli', 'c', 'm', u, 'connection.interface-name', iname] + cargs)
|
||||
subprocess.check_call(['nmcli', 'c', 'u', u])
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -2,12 +2,18 @@
|
||||
# This script would run in post.d
|
||||
#
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
echo "deb [arch=amd64] http://download.proxmox.com/debian/pve bookworm pve-no-subscription" > /etc/apt/sources.list.d/pve-install-repo.list
|
||||
wget https://enterprise.proxmox.com/debian/proxmox-release-bookworm.gpg -O /etc/apt/trusted.gpg.d/proxmox-release-bookworm.gpg
|
||||
sum=$(sha512sum /etc/apt/trusted.gpg.d/proxmox-release-bookworm.gpg)
|
||||
if [ "$sum" -ne "7da6fe34168adc6e479327ba517796d4702fa2f8b4f0a9833f5ea6e6b48f6507a6da403a274fe201595edc86a84463d50383d07f64bdde2e3658108db7d6dc87" ]; then
|
||||
codename=$(grep ^VERSION_CODENAME /etc/os-release | cut -d= -f2)
|
||||
echo "deb [arch=amd64] http://download.proxmox.com/debian/pve $codename pve-no-subscription" > /etc/apt/sources.list.d/pve-install-repo.list
|
||||
wget https://enterprise.proxmox.com/debian/proxmox-release-$codename.gpg -O /etc/apt/trusted.gpg.d/proxmox-release-$codename.gpg
|
||||
sum=$(sha512sum /etc/apt/trusted.gpg.d/proxmox-release-$codename.gpg)
|
||||
if [ $codename == "bookworm" ]; then
|
||||
expectedsum=7da6fe34168adc6e479327ba517796d4702fa2f8b4f0a9833f5ea6e6b48f6507a6da403a274fe201595edc86a84463d50383d07f64bdde2e3658108db7d6dc87
|
||||
elif [ $codename == "trixie" ]; then
|
||||
expectedsum=8678f2327c49276615288d7ca11e7d296bc8a2b96946fe565a9c81e533f9b15a5dbbad210a0ad5cd46d361ff1d3c4bac55844bc296beefa4f88b86e44e69fa51
|
||||
fi
|
||||
if [ "$sum" -ne "$expectedsum" ]; then
|
||||
echo "Mismatch in fingerprint!"
|
||||
rm /etc/apt/trusted.gpg.d/proxmox-release-bookworm.gpg
|
||||
rm /etc/apt/trusted.gpg.d/proxmox-release-$codename.gpg
|
||||
exit 1
|
||||
fi
|
||||
apt-get update && apt-get -y full-upgrade < /dev/null
|
||||
|
||||
@@ -7,7 +7,6 @@ for pubkey in /etc/ssh/ssh_host*key.pub; do
|
||||
continue
|
||||
fi
|
||||
certfile=${pubkey/.pub/-cert.pub}
|
||||
rm $certfile
|
||||
confluentpython $confapiclient /confluent-api/self/sshcert $pubkey -o $certfile
|
||||
done
|
||||
if [ -d /etc/ssh/sshd_config.d/ -a ! -e /etc/ssh/sshd_config.d/90-confluent.conf ]; then
|
||||
@@ -25,6 +24,7 @@ confluentpython $confapiclient /confluent-public/site/initramfs.tgz -o initramfs
|
||||
tar xf initramfs.tgz
|
||||
for ca in ssh/*.ca; do
|
||||
LINE=$(cat $ca)
|
||||
if [ -z "$LINE" ]; then continue; fi
|
||||
cp -af /etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts.new
|
||||
grep -v "$LINE" /etc/ssh/ssh_known_hosts > /etc/ssh/ssh_known_hosts.new
|
||||
echo '@cert-authority *' $LINE >> /etc/ssh/ssh_known_hosts.new
|
||||
@@ -32,6 +32,7 @@ for ca in ssh/*.ca; do
|
||||
done
|
||||
for pubkey in ssh/*.*pubkey; do
|
||||
LINE=$(cat $pubkey)
|
||||
if [ -z "$LINE" ]; then continue; fi
|
||||
cp -af /root/.ssh/authorized_keys /root/.ssh/authorized_keys.new
|
||||
grep -v "$LINE" /root/.ssh/authorized_keys > /root/.ssh/authorized_keys.new
|
||||
echo "$LINE" >> /root/.ssh/authorized_keys.new
|
||||
@@ -41,3 +42,4 @@ confluentpython $confapiclient /confluent-api/self/nodelist | sed -e 's/^- //' >
|
||||
cat /etc/ssh/shosts.equiv > /root/.shosts
|
||||
cd -
|
||||
rm -rf $TMPDIR
|
||||
systemctl try-restart sshd
|
||||
|
||||
+327
@@ -0,0 +1,327 @@
|
||||
get_remote_apikey() {
|
||||
while [ -z "$confluent_apikey" ]; do
|
||||
/opt/confluent/bin/clortho $nodename $confluent_mgr > /etc/confluent/confluent.apikey
|
||||
if grep ^SEALED: /etc/confluent/confluent.apikey > /dev/null; then
|
||||
# we don't support remote sealed api keys anymore
|
||||
echo > /etc/confluent/confluent.apikey
|
||||
fi
|
||||
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
|
||||
if [ -z "$confluent_apikey" ]; then
|
||||
echo "Unable to acquire node api key, set deployment.apiarmed=once on node '$nodename', retrying..."
|
||||
if [ ! -z "$autoconsdev" ]; then echo "Unable to acquire node api key, set deployment.apiarmed=once on node '$nodename', retrying..." > $autoconsdev; fi
|
||||
sleep 10
|
||||
elif [ -c /dev/tpmrm0 ]; then
|
||||
tmpdir=$(mktemp -d)
|
||||
cd $tmpdir
|
||||
tpm2_startauthsession --session=session.ctx
|
||||
tpm2_policypcr -Q --session=session.ctx --pcr-list="sha256:15" --policy=pcr15.sha256.policy
|
||||
tpm2_createprimary -G ecc -Q --key-context=prim.ctx
|
||||
(echo -n "CONFLUENT_APIKEY:";cat /etc/confluent/confluent.apikey) | tpm2_create -Q --policy=pcr15.sha256.policy --public=data.pub --private=data.priv -i - -C prim.ctx
|
||||
tpm2_load -Q --parent-context=prim.ctx --public=data.pub --private=data.priv --name=confluent.apikey --key-context=data.ctx
|
||||
tpm2_evictcontrol -Q -c data.ctx
|
||||
tpm2_flushcontext session.ctx
|
||||
cd - > /dev/null
|
||||
rm -rf $tmpdir
|
||||
fi
|
||||
done
|
||||
}
|
||||
root=1
|
||||
rootok=1
|
||||
netroot=confluent
|
||||
echo -ne '\033[H\033[2J\033[3J'
|
||||
mkdir -p /etc/ssh
|
||||
mkdir -p /var/tmp/
|
||||
mkdir -p /var/empty/sshd
|
||||
mkdir -p /usr/share/empty.sshd
|
||||
mkdir -p /etc/confluent
|
||||
sed -i '/^root:x/d' /etc/passwd
|
||||
echo root:x:0:0::/:/bin/bash >> /etc/passwd
|
||||
echo sshd:x:30:30:SSH User:/var/empty/sshd:/sbin/nologin >> /etc/passwd
|
||||
|
||||
if ! grep console= /proc/cmdline >& /dev/null; then
|
||||
autocons=$(/opt/confluent/bin/autocons)
|
||||
autoconsdev=${autocons%,*}
|
||||
autocons=${autocons##*/}
|
||||
echo "Automatic console configured for $autocons"
|
||||
fi
|
||||
echo "Initializing confluent diskless environment"
|
||||
echo -n "udevd: "
|
||||
/usr/lib/systemd/systemd-udevd --daemon
|
||||
echo -n "Loading drivers..."
|
||||
udevadm trigger
|
||||
udevadm trigger --type=devices --action=add
|
||||
udevadm settle
|
||||
modprobe ib_ipoib
|
||||
modprobe ib_umad
|
||||
modprobe hfi1
|
||||
modprobe mlx5_ib
|
||||
echo "done"
|
||||
cat > /etc/ssh/sshd_config << EOF
|
||||
Port 2222
|
||||
Subsystem sftp /usr/libexec/openssh/sftp-server
|
||||
PermitRootLogin yes
|
||||
AuthorizedKeysFile .ssh/authorized_keys
|
||||
EOF
|
||||
mkdir /root/.ssh
|
||||
mkdir /.ssh
|
||||
cat /ssh/*pubkey > /root/.ssh/authorized_keys 2>/dev/null
|
||||
cp /root/.ssh/authorized_keys /.ssh/
|
||||
cat /tls/*.pem > /etc/confluent/ca.pem
|
||||
mkdir -p /etc/pki/tls/certs
|
||||
cat /tls/*.pem > /etc/pki/tls/certs/ca-bundle.crt
|
||||
TRIES=0
|
||||
oldumask=$(umask)
|
||||
umask 0077
|
||||
tpmdir=$(mktemp -d)
|
||||
cd $tpmdir
|
||||
lasthdl=""
|
||||
if [ -c /dev/tpmrm0 ]; then
|
||||
for hdl in $(tpm2_getcap handles-persistent|awk '{print $2}'); do
|
||||
tpm2_startauthsession --policy-session --session=session.ctx
|
||||
tpm2_policypcr -Q --session=session.ctx --pcr-list="sha256:15" --policy=pcr15.sha256.policy
|
||||
unsealeddata=$(tpm2_unseal --auth=session:session.ctx -Q -c $hdl 2>/dev/null)
|
||||
tpm2_flushcontext session.ctx
|
||||
if [[ $unsealeddata == "CONFLUENT_APIKEY:"* ]]; then
|
||||
confluent_apikey=${unsealeddata#CONFLUENT_APIKEY:}
|
||||
echo $confluent_apikey > /etc/confluent/confluent.apikey
|
||||
if [ -n "$lasthdl" ]; then
|
||||
tpm2_evictcontrol -c $lasthdl
|
||||
fi
|
||||
lasthdl=$hdl
|
||||
fi
|
||||
done
|
||||
fi
|
||||
cd - > /dev/null
|
||||
rm -rf $tpmdir
|
||||
touch /etc/confluent/confluent.info
|
||||
cd /sys/class/net
|
||||
echo -n "Scanning for network configuration..."
|
||||
while ! grep ^EXTMGRINFO: /etc/confluent/confluent.info | awk -F'|' '{print $3}' | grep 1 >& /dev/null && [ "$TRIES" -lt 30 ]; do
|
||||
TRIES=$((TRIES + 1))
|
||||
for i in *; do
|
||||
ip link set $i up
|
||||
done
|
||||
/opt/confluent/bin/copernicus -t > /etc/confluent/confluent.info
|
||||
echo -n .
|
||||
done
|
||||
TRIES=0
|
||||
while ! grep ^NODENAME: /etc/confluent/confluent.info >& /dev/null && [ "$TRIES" -lt 300 ]; do
|
||||
sleep 0.5
|
||||
echo -n .
|
||||
/opt/confluent/bin/copernicus -t > /etc/confluent/confluent.info
|
||||
TRIES=$((TRIES + 1))
|
||||
done
|
||||
cd /
|
||||
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
|
||||
hostname $nodename
|
||||
confluent_mgr=$(grep '^EXTMGRINFO:.*1$' /etc/confluent/confluent.info | head -n 1 | awk -F': ' '{print $2}' | awk -F'|' '{print $1}')
|
||||
if [ -z "$confluent_mgr" ]; then
|
||||
confluent_mgr=$(grep ^MANAGER: /etc/confluent/confluent.info|head -n 1 | awk '{print $2}')
|
||||
fi
|
||||
if [[ $confluent_mgr == *%* ]]; then
|
||||
echo $confluent_mgr | awk -F% '{print $2}' > /tmp/confluent.ifidx
|
||||
ifidx=$(cat /tmp/confluent.ifidx)
|
||||
ifname=$(ip link |grep ^$ifidx:|awk '{print $2}')
|
||||
ifname=${ifname%:}
|
||||
fi
|
||||
|
||||
ready=0
|
||||
while [ $ready = "0" ]; do
|
||||
get_remote_apikey
|
||||
if [[ $confluent_mgr == *:* ]] && [[ $confluent_mgr != "["* ]]; then
|
||||
confluent_mgr="[$confluent_mgr]"
|
||||
fi
|
||||
tmperr=$(mktemp)
|
||||
curl -sSf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/deploycfg2 > /etc/confluent/confluent.deploycfg 2> $tmperr
|
||||
if grep 401 $tmperr > /dev/null; then
|
||||
confluent_apikey=""
|
||||
if [ -n "$lasthdl" ]; then
|
||||
tpm2_evictcontrol -c $lasthdl
|
||||
fi
|
||||
confluent_mgr=${confluent_mgr#[}
|
||||
confluent_mgr=${confluent_mgr%]}
|
||||
elif grep 'SSL' $tmperr > /dev/null; then
|
||||
confluent_mgr=${confluent_mgr#[}
|
||||
confluent_mgr=${confluent_mgr%]}
|
||||
echo 'Failure establishing TLS conneection to '$confluent_mgr' (try `osdeploy initialize -t` on the deployment server)'
|
||||
if [ ! -z "$autoconsdev" ]; then echo 'Failure establishing TLS conneection to '$confluent_mgr' (try `osdeploy initialize -t` on the deployment server)' > $autoconsdev; fi
|
||||
sleep 10
|
||||
else
|
||||
ready=1
|
||||
fi
|
||||
rm $tmperr
|
||||
done
|
||||
if [ ! -z "$autocons" ] && grep "textconsole: true" /etc/confluent/confluent.deploycfg > /dev/null; then /opt/confluent/bin/autocons -c > /dev/null; fi
|
||||
if [ -c /dev/tpmrm0 ]; then
|
||||
tpm2_pcrextend 15:sha256=2fbe96c50dde38ce9cd2764ddb79c216cfbcd3499568b1125450e60c45dd19f2
|
||||
fi
|
||||
umask $oldumask
|
||||
mkdir -p /run/NetworkManager/system-connections
|
||||
cat > /run/NetworkManager/system-connections/$ifname.nmconnection << EOC
|
||||
[connection]
|
||||
EOC
|
||||
echo id=${ifname} >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
echo uuid=$(uuidgen) >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
linktype=$(ip link show dev ${ifname}|grep link/|awk '{print $1}')
|
||||
if [ "$linktype" = link/infiniband ]; then
|
||||
linktype="infiniband"
|
||||
else
|
||||
linktype="ethernet"
|
||||
fi
|
||||
echo type=$linktype >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
cat >> /run/NetworkManager/system-connections/$ifname.nmconnection << EOC
|
||||
autoconnect-retries=1
|
||||
EOC
|
||||
echo interface-name=$ifname >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
cat >> /run/NetworkManager/system-connections/$ifname.nmconnection << EOC
|
||||
multi-connect=1
|
||||
permissions=
|
||||
wait-device-timeout=60000
|
||||
|
||||
EOC
|
||||
if [ "$linktype" = infiniband ]; then
|
||||
cat >> /run/NetworkManager/system-connections/$ifname.nmconnection << EOC
|
||||
[infiniband]
|
||||
transport-mode=datagram
|
||||
|
||||
EOC
|
||||
fi
|
||||
autoconfigmethod=$(grep ^ipv4_method: /etc/confluent/confluent.deploycfg |awk '{print $2}')
|
||||
auto6configmethod=$(grep ^ipv6_method: /etc/confluent/confluent.deploycfg |awk '{print $2}')
|
||||
if [ "$autoconfigmethod" = "dhcp" ]; then
|
||||
echo -n "Attempting to use dhcp to bring up $ifname..."
|
||||
dhcpcd $ifname
|
||||
echo "Complete:"
|
||||
ip addr show dev $ifname
|
||||
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg| awk '{print $2}')
|
||||
elif [ "$autoconfigmethod" = "static" ]; then
|
||||
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg| awk '{print $2}')
|
||||
v4addr=$(grep ^ipv4_address: /etc/confluent/confluent.deploycfg)
|
||||
v4addr=${v4addr#ipv4_address: }
|
||||
v4gw=$(grep ^ipv4_gateway: /etc/confluent/confluent.deploycfg)
|
||||
v4gw=${v4gw#ipv4_gateway: }
|
||||
if [ "$v4gw" = "null" ]; then
|
||||
v4gw=""
|
||||
fi
|
||||
v4nm=$(grep ^prefix: /etc/confluent/confluent.deploycfg)
|
||||
v4nm=${v4nm#prefix: }
|
||||
echo "Setting up $ifname as static at $v4addr/$v4nm"
|
||||
ip addr add dev $ifname $v4addr/$v4nm
|
||||
if [ ! -z "$v4gw" ]; then
|
||||
ip route add default via $v4gw
|
||||
fi
|
||||
echo '[ipv4]' >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
echo address1=$v4addr/$v4nm >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
if [ ! -z "$v4gw" ]; then
|
||||
echo gateway=$v4gw >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
fi
|
||||
nameserversec=0
|
||||
nameservers=""
|
||||
while read -r entry; do
|
||||
if [ $nameserversec = 1 ]; then
|
||||
if [[ $entry == "-"*.* ]]; then
|
||||
nameservers="$nameservers"${entry#- }";"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
nameserversec=0
|
||||
if [ "${entry%:*}" = "nameservers" ]; then
|
||||
nameserversec=1
|
||||
continue
|
||||
fi
|
||||
done < /etc/confluent/confluent.deploycfg
|
||||
echo dns=$nameservers >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
dnsdomain=$(grep ^dnsdomain: /etc/confluent/confluent.deploycfg)
|
||||
dnsdomain=${dnsdomain#dnsdomain: }
|
||||
echo dns-search=$dnsdomain >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
cat >> /run/NetworkManager/system-connections/$ifname.nmconnection << EOC
|
||||
may-fail=false
|
||||
method=manual
|
||||
|
||||
[ipv6]
|
||||
addr-gen-mode=eui64
|
||||
method=auto
|
||||
|
||||
EOC
|
||||
elif [ "$auto6configmethod" = "static" ]; then
|
||||
confluent_mgr=$(grep ^deploy_server_v6: /etc/confluent/confluent.deploycfg| awk '{print $2}')
|
||||
v6addr=$(grep ^ipv6_address: /etc/confluent/confluent.deploycfg)
|
||||
v6addr=${v6addr#ipv6_address: }
|
||||
v6gw=$(grep ^ipv6_gateway: /etc/confluent/confluent.deploycfg)
|
||||
v6gw=${v6gw#ipv6_gateway: }
|
||||
if [ "$v6gw" = "null" ]; then
|
||||
v6gw=""
|
||||
fi
|
||||
v6nm=$(grep ^ipv6_prefix: /etc/confluent/confluent.deploycfg)
|
||||
v6nm=${v6nm#ipv6_prefix: }
|
||||
echo "Setting up $ifname as static at $v6addr/$v6nm"
|
||||
ip addr add dev $ifname $v6addr/$v6nm
|
||||
|
||||
cat >> /run/NetworkManager/system-connections/$ifname.nmconnection << EOC
|
||||
[ipv4]
|
||||
dhcp-timeout=90
|
||||
dhcp-vendor-class-identifier=anaconda-Linux
|
||||
method=disabled
|
||||
|
||||
[ipv6]
|
||||
addr-gen-mode=eui64
|
||||
method=manual
|
||||
may-fail=false
|
||||
EOC
|
||||
echo address1=$v6addr/$v6nm >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
if [ ! -z "$v6gw" ]; then
|
||||
ip route add default via $v6gw
|
||||
echo gateway=$v6gw >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
fi
|
||||
nameserversec=0
|
||||
nameservers=""
|
||||
while read -r entry; do
|
||||
if [ $nameserversec = 1 ]; then
|
||||
if [[ $entry == "-"*:* ]]; then
|
||||
nameservers="$nameservers"${entry#- }";"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
nameserversec=0
|
||||
if [ "${entry%:*}" = "nameservers" ]; then
|
||||
nameserversec=1
|
||||
continue
|
||||
fi
|
||||
done < /etc/confluent/confluent.deploycfg
|
||||
echo dns=$nameservers >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
dnsdomain=$(grep ^dnsdomain: /etc/confluent/confluent.deploycfg)
|
||||
dnsdomain=${dnsdomain#dnsdomain: }
|
||||
echo dns-search=$dnsdomain >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
fi
|
||||
echo '[proxy]' >> /run/NetworkManager/system-connections/$ifname.nmconnection
|
||||
chmod 600 /run/NetworkManager/system-connections/*.nmconnection
|
||||
confluent_websrv=$confluent_mgr
|
||||
if [[ $confluent_websrv == *:* ]] && [[ $confluent_websrv != "["* ]]; then
|
||||
confluent_websrv="[$confluent_websrv]"
|
||||
fi
|
||||
echo -n "Initializing ssh..."
|
||||
ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -C '' -N ''
|
||||
for pubkey in /etc/ssh/ssh_host*key.pub; do
|
||||
certfile=${pubkey/.pub/-cert.pub}
|
||||
privfile=${pubkey%.pub}
|
||||
curl -sf -X POST -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" -d @$pubkey https://$confluent_websrv/confluent-api/self/sshcert > $certfile
|
||||
if [ -s $certfile ]; then
|
||||
echo HostCertificate $certfile >> /etc/ssh/sshd_config
|
||||
fi
|
||||
echo HostKey $privfile >> /etc/ssh/sshd_config
|
||||
done
|
||||
/usr/sbin/sshd
|
||||
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg| awk '{print $2}')
|
||||
confluent_proto=$(grep ^protocol: /etc/confluent/confluent.deploycfg| awk '{print $2}')
|
||||
confluent_urls=""
|
||||
for addr in $(grep ^MANAGER: /etc/confluent/confluent.info|awk '{print $2}'|sed -e s/%/%25/); do
|
||||
if [[ $addr == *:* ]]; then
|
||||
confluent_urls="$confluent_urls $confluent_proto://[$addr]/confluent-public/os/$confluent_profile/rootimg.sfs"
|
||||
else
|
||||
confluent_urls="$confluent_urls $confluent_proto://$addr/confluent-public/os/$confluent_profile/rootimg.sfs"
|
||||
fi
|
||||
done
|
||||
mkdir -p /etc/confluent
|
||||
curl -sf https://$confluent_websrv/confluent-public/os/$confluent_profile/scripts/functions > /etc/confluent/functions
|
||||
. /etc/confluent/functions
|
||||
source_remote imageboot.sh
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../../../el9-diskless/profiles/default/scripts/add_local_repositories
|
||||
@@ -0,0 +1 @@
|
||||
../../../../el9-diskless/profiles/default/scripts/firstboot.custom
|
||||
@@ -0,0 +1 @@
|
||||
../../../../el9-diskless/profiles/default/scripts/firstboot.service
|
||||
@@ -0,0 +1 @@
|
||||
../../../../el9-diskless/profiles/default/scripts/firstboot.sh
|
||||
@@ -0,0 +1 @@
|
||||
../../../../el9-diskless/profiles/default/scripts/functions
|
||||
@@ -0,0 +1 @@
|
||||
../../../../el9-diskless/profiles/default/scripts/getinstalldisk
|
||||
@@ -0,0 +1 @@
|
||||
../../../../el9-diskless/profiles/default/scripts/image2disk.py
|
||||
@@ -0,0 +1,138 @@
|
||||
. /lib/dracut-lib.sh
|
||||
confluent_whost=$confluent_mgr
|
||||
if [[ "$confluent_whost" == *:* ]] && [[ "$confluent_whost" != "["* ]]; then
|
||||
confluent_whost="[$confluent_mgr]"
|
||||
fi
|
||||
mkdir -p /mnt/remoteimg /mnt/remote /mnt/overlay
|
||||
if [ "untethered" = "$(getarg confluent_imagemethod)" ]; then
|
||||
mount -t tmpfs untethered /mnt/remoteimg
|
||||
curl https://$confluent_whost/confluent-public/os/$confluent_profile/rootimg.sfs -o /mnt/remoteimg/rootimg.sfs
|
||||
else
|
||||
confluent_urls="$confluent_urls https://$confluent_whost/confluent-public/os/$confluent_profile/rootimg.sfs"
|
||||
/opt/confluent/bin/urlmount $confluent_urls /mnt/remoteimg
|
||||
fi
|
||||
/opt/confluent/bin/confluent_imginfo /mnt/remoteimg/rootimg.sfs > /tmp/rootimg.info
|
||||
loopdev=$(losetup -f)
|
||||
export mountsrc=$loopdev
|
||||
losetup -r $loopdev /mnt/remoteimg/rootimg.sfs
|
||||
if grep '^Format: confluent_crypted' /tmp/rootimg.info > /dev/null; then
|
||||
while ! curl -sf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$confluent_whost/confluent-api/self/profileprivate/pending/rootimg.key > /tmp/rootimg.key; do
|
||||
echo "Unable to retrieve private key from $confluent_mgr (verify that confluent can access /var/lib/confluent/private/os/$confluent_profile/pending/rootimg.key)"
|
||||
sleep 1
|
||||
done
|
||||
cipher=$(head -n 1 /tmp/rootimg.key)
|
||||
key=$(tail -n 1 /tmp/rootimg.key)
|
||||
len=$(wc -c /mnt/remoteimg/rootimg.sfs | awk '{print $1}')
|
||||
len=$(((len-4096)/512))
|
||||
dmsetup create cryptimg --table "0 $len crypt $cipher $key 0 $loopdev 8"
|
||||
/opt/confluent/bin/confluent_imginfo /dev/mapper/cryptimg > /tmp/rootimg.info
|
||||
mountsrc=/dev/mapper/cryptimg
|
||||
fi
|
||||
|
||||
if grep '^Format: squashfs' /tmp/rootimg.info > /dev/null; then
|
||||
mount -o ro $mountsrc /mnt/remote
|
||||
elif grep '^Format: confluent_multisquash' /tmp/rootimg.info; then
|
||||
tail -n +3 /tmp/rootimg.info | awk '{gsub("/", "_"); print "echo 0 " $4 " linear '$mountsrc' " $3 " | dmsetup create mproot" $7}' > /tmp/setupmount.sh
|
||||
. /tmp/setupmount.sh
|
||||
cat /tmp/setupmount.sh |awk '{printf "mount /dev/mapper/"$NF" "; sub("mproot", ""); gsub("_", "/"); print "/mnt/remote"$NF}' > /tmp/mountparts.sh
|
||||
. /tmp/mountparts.sh
|
||||
fi
|
||||
|
||||
|
||||
#mount -t tmpfs overlay /mnt/overlay
|
||||
modprobe zram
|
||||
memtot=$(grep ^MemTotal: /proc/meminfo|awk '{print $2}')
|
||||
memtot=$((memtot/2))$(grep ^MemTotal: /proc/meminfo | awk '{print $3'})
|
||||
echo $memtot > /sys/block/zram0/disksize
|
||||
mkfs.xfs /dev/zram0 > /dev/null
|
||||
mount -o discard /dev/zram0 /mnt/overlay
|
||||
if [ ! -f /tmp/mountparts.sh ]; then
|
||||
mkdir -p /mnt/overlay/upper /mnt/overlay/work
|
||||
mount -t overlay -o upperdir=/mnt/overlay/upper,workdir=/mnt/overlay/work,lowerdir=/mnt/remote disklessroot /sysroot
|
||||
else
|
||||
for srcmount in $(cat /tmp/mountparts.sh | awk '{print $3}'); do
|
||||
mkdir -p /mnt/overlay${srcmount}/upper /mnt/overlay${srcmount}/work
|
||||
mount -t overlay -o upperdir=/mnt/overlay${srcmount}/upper,workdir=/mnt/overlay${srcmount}/work,lowerdir=${srcmount} disklesspart /sysroot${srcmount#/mnt/remote}
|
||||
done
|
||||
fi
|
||||
mkdir -p /sysroot/etc/ssh
|
||||
mkdir -p /sysroot/etc/confluent
|
||||
mkdir -p /sysroot/root/.ssh
|
||||
cp /root/.ssh/* /sysroot/root/.ssh
|
||||
chmod 700 /sysroot/root/.ssh
|
||||
cp /etc/confluent/* /sysroot/etc/confluent/
|
||||
cp /etc/ssh/*key* /sysroot/etc/ssh/
|
||||
for pubkey in /etc/ssh/ssh_host*key.pub; do
|
||||
certfile=${pubkey/.pub/-cert.pub}
|
||||
privfile=${pubkey%.pub}
|
||||
if [ -s $certfile ]; then
|
||||
echo HostCertificate $certfile >> /sysroot/etc/ssh/sshd_config
|
||||
fi
|
||||
echo HostKey $privfile >> /sysroot/etc/ssh/sshd_config
|
||||
done
|
||||
|
||||
mkdir -p /sysroot/dev /sysroot/sys /sysroot/proc /sysroot/run
|
||||
if [ ! -z "$autocons" ]; then
|
||||
autocons=${autocons%,*}
|
||||
mkdir -p /run/systemd/generator/getty.target.wants
|
||||
ln -s /usr/lib/systemd/system/serial-getty@.service /run/systemd/generator/getty.target.wants/serial-getty@${autocons}.service
|
||||
fi
|
||||
while [ ! -e /sysroot/sbin/init ]; do
|
||||
echo "Failed to access root filesystem or it is missing /sbin/init"
|
||||
echo "System should be accessible through ssh at port 2222 with the appropriate key"
|
||||
while [ ! -e /sysroot/sbin/init ]; do
|
||||
sleep 1
|
||||
done
|
||||
done
|
||||
rootpassword=$(grep ^rootpassword: /etc/confluent/confluent.deploycfg)
|
||||
rootpassword=${rootpassword#rootpassword: }
|
||||
if [ "$rootpassword" = "null" ]; then
|
||||
rootpassword=""
|
||||
fi
|
||||
|
||||
if [ ! -z "$rootpassword" ]; then
|
||||
sed -i "s@root:[^:]*:@root:$rootpassword:@" /sysroot/etc/shadow
|
||||
fi
|
||||
for i in /ssh/*.ca; do
|
||||
echo '@cert-authority *' $(cat $i) >> /sysroot/etc/ssh/ssh_known_hosts
|
||||
done
|
||||
echo HostbasedAuthentication yes >> /sysroot/etc/ssh/sshd_config
|
||||
echo HostbasedUsesNameFromPacketOnly yes >> /sysroot/etc/ssh/sshd_config
|
||||
echo IgnoreRhosts no >> /sysroot/etc/ssh/sshd_config
|
||||
sshconf=/sysroot/etc/ssh/ssh_config
|
||||
if [ -d /sysroot/etc/ssh/ssh_config.d/ ]; then
|
||||
sshconf=/sysroot/etc/ssh/ssh_config.d/01-confluent.conf
|
||||
fi
|
||||
echo 'Host *' >> $sshconf
|
||||
echo ' HostbasedAuthentication yes' >> $sshconf
|
||||
echo ' EnableSSHKeysign yes' >> $sshconf
|
||||
echo ' HostbasedKeyTypes *ed25519*' >> $sshconf
|
||||
curl -sf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$confluent_whost/confluent-api/self/nodelist > /sysroot/etc/ssh/shosts.equiv
|
||||
cp /sysroot/etc/ssh/shosts.equiv /sysroot/root/.shosts
|
||||
chmod 640 /sysroot/etc/ssh/*_key
|
||||
cp /tls/*.pem /sysroot/etc/pki/ca-trust/source/anchors/
|
||||
chroot /sysroot/ update-ca-trust
|
||||
curl -sf https://$confluent_whost/confluent-public/os/$confluent_profile/scripts/onboot.service > /sysroot/etc/systemd/system/onboot.service
|
||||
mkdir -p /sysroot/opt/confluent/bin
|
||||
curl -sf https://$confluent_whost/confluent-public/os/$confluent_profile/scripts/onboot.sh > /sysroot/opt/confluent/bin/onboot.sh
|
||||
chmod +x /sysroot/opt/confluent/bin/onboot.sh
|
||||
cp /opt/confluent/bin/apiclient /sysroot/opt/confluent/bin
|
||||
ln -s /etc/systemd/system/onboot.service /sysroot/etc/systemd/system/multi-user.target.wants/onboot.service
|
||||
cp /etc/confluent/functions /sysroot/etc/confluent/functions
|
||||
if grep installtodisk /proc/cmdline > /dev/null; then
|
||||
. /etc/confluent/functions
|
||||
run_remote installimage
|
||||
exec reboot -f
|
||||
fi
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
../../../../el9-diskless/profiles/default/scripts/installimage
|
||||
@@ -0,0 +1 @@
|
||||
../../../../el9-diskless/profiles/default/scripts/onboot.custom
|
||||
@@ -0,0 +1 @@
|
||||
../../../../el9-diskless/profiles/default/scripts/onboot.service
|
||||
@@ -0,0 +1 @@
|
||||
../../../../el9-diskless/profiles/default/scripts/onboot.sh
|
||||
@@ -0,0 +1 @@
|
||||
../../../../el9-diskless/profiles/default/scripts/post.sh
|
||||
@@ -0,0 +1 @@
|
||||
../../../../el9-diskless/profiles/default/scripts/syncfileclient
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
+5
-1
@@ -107,7 +107,11 @@ if [ ! -z "$confluentsrv" ]; then
|
||||
/usr/libexec/nm-initrd-generator ip=:dhcp6
|
||||
else
|
||||
confluenthttpsrv=$confluentsrv
|
||||
ifname=$(ip -br link|grep LOWER_UP|grep -v UNKNOWN|head -n 1|awk '{print $1}')
|
||||
ifname=""
|
||||
while [ -z "$ifname" ]; do
|
||||
ifname=$(ip -br link|grep LOWER_UP|grep -v ib|grep -v UNKNOWN|head -n 1|awk '{print $1}')
|
||||
sleep 0.5
|
||||
done
|
||||
echo -n "Attempting to use dhcp to bring up $ifname..."
|
||||
dhclient $ifname
|
||||
while ! ip -br addr show dev $ifname | grep \\. > /dev/null; do
|
||||
|
||||
@@ -25,7 +25,8 @@ if [ ! -f /etc/confluent/firstboot.ran ]; then
|
||||
touch /etc/confluent/firstboot.ran
|
||||
|
||||
cat /etc/confluent/tls/*.pem >> /etc/pki/tls/certs/ca-bundle.crt
|
||||
|
||||
confluentpython /root/confignet
|
||||
rm /root/confignet
|
||||
run_remote firstboot.custom
|
||||
# Firstboot scripts may be placed into firstboot.d, e.g. firstboot.d/01-firstaction.sh, firstboot.d/02-secondaction.sh
|
||||
run_remote_parts firstboot.d
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
@@ -56,7 +56,11 @@ def get_image_metadata(imgpath):
|
||||
for md in get_multipart_image_meta(img):
|
||||
yield md
|
||||
else:
|
||||
raise Exception('Installation from single part image not supported')
|
||||
yield {'mount': '/', 'filesystem': 'xfs', 'minsize': 4294967296, 'initsize': 954128662528, 'flags': 'rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota', 'device': '/dev/mapper/root', 'compressed_size': 27022069760}
|
||||
yield {'mount': '/boot', 'filesystem': 'xfs', 'minsize': 536870912, 'initsize': 1006632960, 'flags': 'rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota', 'device': '/dev/nvme1n1p2', 'compressed_size': 171462656}
|
||||
yield {'mount': '/boot/efi', 'filesystem': 'vfat', 'minsize': 33554432, 'initsize': 627900416, 'flags': 'rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=ascii,shortname=winnt,errors=remount-ro', 'device': '/dev/nvme1n1p1', 'compressed_size': 1576960}
|
||||
#raise Exception('Installation from single part image not supported')
|
||||
|
||||
|
||||
class PartedRunner():
|
||||
def __init__(self, disk):
|
||||
@@ -75,8 +79,17 @@ def fixup(rootdir, vols):
|
||||
for vol in vols:
|
||||
devbymount[vol['mount']] = vol['targetdisk']
|
||||
fstabfile = os.path.join(rootdir, 'etc/fstab')
|
||||
with open(fstabfile) as tfile:
|
||||
fstab = tfile.read().split('\n')
|
||||
if os.path.exists(fstabfile):
|
||||
with open(fstabfile) as tfile:
|
||||
fstab = tfile.read().split('\n')
|
||||
else:
|
||||
# fabricate a reference fstab
|
||||
fstab = [
|
||||
"#ORIGFSTAB#/dev/mapper/root# / xfs defaults 0 0",
|
||||
"#ORIGFSTAB#UUID=aaf9e0f9-aa4d-4d74-9e75-3537620cfe23# /boot xfs defaults 0 0",
|
||||
"#ORIGFSTAB#UUID=C21D-B881# /boot/efi vfat umask=0077,shortname=winnt 0 2",
|
||||
"#ORIGFSTAB#/dev/mapper/swap# none swap defaults 0 0",
|
||||
]
|
||||
while not fstab[0]:
|
||||
fstab = fstab[1:]
|
||||
if os.path.exists(os.path.join(rootdir, '.autorelabel')):
|
||||
@@ -126,8 +139,10 @@ def fixup(rootdir, vols):
|
||||
newcfg = ifcfg.split('/')[-1]
|
||||
newcfg = os.path.join(rootdir, 'etc/NetworkManager/system-connections/{0}'.format(newcfg))
|
||||
shutil.copy2(ifcfg, newcfg)
|
||||
shutil.rmtree(os.path.join(rootdir, 'etc/confluent/'))
|
||||
shutil.copytree('/etc/confluent', os.path.join(rootdir, 'etc/confluent'))
|
||||
rootconfluentdir = os.path.join(rootdir, 'etc/confluent/')
|
||||
if os.path.exists(rootconfluentdir):
|
||||
shutil.rmtree(rootconfluentdir)
|
||||
shutil.copytree('/etc/confluent', rootconfluentdir)
|
||||
if policy:
|
||||
sys.stdout.write('Applying SELinux labeling...')
|
||||
sys.stdout.flush()
|
||||
@@ -142,14 +157,41 @@ def fixup(rootdir, vols):
|
||||
grubsyscfg = os.path.join(rootdir, 'etc/sysconfig/grub')
|
||||
if not os.path.exists(grubsyscfg):
|
||||
grubsyscfg = os.path.join(rootdir, 'etc/default/grub')
|
||||
with open(grubsyscfg) as defgrubin:
|
||||
defgrub = defgrubin.read().split('\n')
|
||||
currcmdline = []
|
||||
with open('/proc/cmdline') as cmdlinein:
|
||||
cmdline = cmdlinein.read().strip()
|
||||
for arg in cmdline.split():
|
||||
if arg.startswith('console='):
|
||||
currcmdline.append(arg)
|
||||
elif arg == 'quiet':
|
||||
currcmdline.append(arg)
|
||||
currcmdlinestr = ' '.join(currcmdline)
|
||||
if os.path.exists(grubsyscfg):
|
||||
with open(grubsyscfg) as defgrubin:
|
||||
defgrub = defgrubin.read().split('\n')
|
||||
else:
|
||||
defgrub = [
|
||||
'GRUB_TIMEOUT=5',
|
||||
'GRUB_DISTRIBUTOR="$(sed ' + "'s, release .*$,,g'" + ' /etc/system-release)"',
|
||||
'GRUB_DEFAULT=saved',
|
||||
'GRUB_DISABLE_SUBMENU=true',
|
||||
'GRUB_TERMINAL=""',
|
||||
'GRUB_SERIAL_COMMAND=""',
|
||||
'GRUB_CMDLINE_LINUX="{} crashkernel=1G-4G:192M,4G-64G:256M,64G-:512M rd.lvm.lv=vg/root rd.lvm.lv=vg/swap"'.format(currcmdlinestr),
|
||||
'GRUB_DISABLE_RECOVERY="true"',
|
||||
'GRUB_ENABLE_BLSCFG=true',
|
||||
]
|
||||
if not os.path.exists(os.path.join(rootdir, "etc/kernel/cmdline")):
|
||||
with open(os.path.join(rootdir, "etc/kernel/cmdline"), "w") as cmdlineout:
|
||||
cmdlineout.write("{} root=/dev/mapper/localstorage-root rd.lvm.lv=localstorage/root".format(currcmdlinestr))
|
||||
with open(grubsyscfg, 'w') as defgrubout:
|
||||
for gline in defgrub:
|
||||
gline = gline.split()
|
||||
newline = []
|
||||
for ent in gline:
|
||||
if ent.startswith('resume=') or ent.startswith('rd.lvm.lv'):
|
||||
if ent.endswith('"'):
|
||||
newline.append('"')
|
||||
continue
|
||||
newline.append(ent)
|
||||
defgrubout.write(' '.join(newline) + '\n')
|
||||
@@ -159,6 +201,12 @@ def fixup(rootdir, vols):
|
||||
grubcfg = grubcfg[:-1]
|
||||
if len(grubcfg) == 1:
|
||||
grubcfg = grubcfg[0]
|
||||
elif not grubcfg:
|
||||
grubcfg = '/boot/grub2/grub.cfg'
|
||||
paths = glob.glob(os.path.join(rootdir, 'boot/efi/EFI/*'))
|
||||
for path in paths:
|
||||
with open(os.path.join(path, 'grub.cfg'), 'w') as stubgrubout:
|
||||
stubgrubout.write("search --no-floppy --root-dev-only --fs-uuid --set=dev " + bootuuid + "\nset prefix=($dev)/grub2\nexport $prefix\nconfigfile $prefix/grub.cfg\n")
|
||||
else:
|
||||
for gcfg in grubcfg:
|
||||
rgcfg = os.path.join(rootdir, gcfg[1:]) # gcfg has a leading / to get rid of
|
||||
@@ -214,10 +262,18 @@ def fixup(rootdir, vols):
|
||||
shimpath = subprocess.check_output(['find', os.path.join(rootdir, 'boot/efi'), '-name', 'shimx64.efi']).decode('utf8').strip()
|
||||
shimpath = shimpath.replace(rootdir, '/').replace('/boot/efi', '').replace('//', '/').replace('/', '\\')
|
||||
subprocess.check_call(['efibootmgr', '-c', '-d', targblock, '-l', shimpath, '--part', partnum])
|
||||
try:
|
||||
os.makedirs(os.path.join(rootdir, 'opt/confluent/bin'))
|
||||
except Exception:
|
||||
pass
|
||||
shutil.copy2('/opt/confluent/bin/apiclient', os.path.join(rootdir, 'opt/confluent/bin/apiclient'))
|
||||
#other network interfaces
|
||||
|
||||
|
||||
def had_swap():
|
||||
if not os.path.exists('/etc/fstab'):
|
||||
# diskless source, assume swap
|
||||
return True
|
||||
with open('/etc/fstab') as tabfile:
|
||||
tabs = tabfile.read().split('\n')
|
||||
for tab in tabs:
|
||||
@@ -362,6 +418,8 @@ def install_to_disk(imgpath):
|
||||
subprocess.check_call(['mount', vol['targetdisk'], '/run/imginst/targ'])
|
||||
source = vol['mount'].replace('/', '_')
|
||||
source = '/run/imginst/sources/' + source
|
||||
if not os.path.exists(source):
|
||||
source = '/run/imginst/sources/_' + vol['mount']
|
||||
blankfsstat = os.statvfs('/run/imginst/targ')
|
||||
blankused = (blankfsstat.f_blocks - blankfsstat.f_bfree) * blankfsstat.f_bsize
|
||||
sys.stdout.write('\nWriting {0}: '.format(vol['mount']))
|
||||
@@ -419,8 +477,14 @@ def install_to_disk(imgpath):
|
||||
|
||||
|
||||
|
||||
|
||||
subprocess.check_call(['umount', '/run/imginst/targ'])
|
||||
while True:
|
||||
try:
|
||||
subprocess.check_call(['umount', '/run/imginst/targ'])
|
||||
except subprocess.CalledProcessError:
|
||||
print("Failed to unmount /run/imginst/targ, retrying")
|
||||
time.sleep(1)
|
||||
else:
|
||||
break
|
||||
for vol in allvols:
|
||||
subprocess.check_call(['mount', vol['targetdisk'], '/run/imginst/targ/' + vol['mount']])
|
||||
fixup('/run/imginst/targ', allvols)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
# and existing mounts of image (to take advantage of caching)
|
||||
mount -o bind /sys /sysroot/sys
|
||||
mount -o bind /dev /sysroot/dev
|
||||
mount -o bind /dev/pts /sysroot/dev/pts
|
||||
mount -o bind /proc /sysroot/proc
|
||||
mount -o bind /run /sysroot/run
|
||||
|
||||
@@ -21,8 +22,14 @@ else
|
||||
done
|
||||
fi
|
||||
cd /sysroot/run
|
||||
cp /run/sshd.pid /tmp/dbgssh.pid
|
||||
chroot /sysroot/ bash -c "/usr/sbin/sshd"
|
||||
chroot /sysroot/ bash -c "source /etc/confluent/functions; run_remote_python getinstalldisk"
|
||||
chroot /sysroot/ bash -c "source /etc/confluent/functions; run_remote_parts pre.d"
|
||||
for nameserver in $(sed -n '/^nameservers:/,/^[^-]/p' /etc/confluent/confluent.deploycfg|grep ^- | cut -d ' ' -f 2|sed -e 's/ //'); do
|
||||
echo "nameserver $nameserver" >> /sysroot/etc/resolv.conf
|
||||
done
|
||||
#chroot /sysroot/ bash -c "source /etc/confluent/functions; run_remote_python confignet"
|
||||
if [ ! -f /sysroot/tmp/installdisk ]; then
|
||||
echo 'Unable to find a suitable installation target device, ssh to port 2222 to investigate'
|
||||
while [ ! -f /sysroot/tmp/installdisk ]; do
|
||||
@@ -39,7 +46,10 @@ chroot /sysroot bash -c "source /etc/confluent/functions; run_remote_python imag
|
||||
echo "Port 22" >> /etc/ssh/sshd_config
|
||||
echo 'Match LocalPort 22' >> /etc/ssh/sshd_config
|
||||
echo ' ChrootDirectory /sysroot/run/imginst/targ' >> /etc/ssh/sshd_config
|
||||
kill -HUP $(cat /run/sshd.pid)
|
||||
kill $(cat /sysroot/var/run/sshd.pid)
|
||||
kill -HUP $(cat /tmp/dbgssh.pid)
|
||||
cp /sysroot/etc/pki/ca-trust/source/anchors/* /sysroot/run/imginst/targ/etc/pki/ca-trust/source/anchors/
|
||||
chroot /sysroot/run/imginst/targ update-ca-trust
|
||||
|
||||
chroot /sysroot/run/imginst/targ bash -c "source /etc/confluent/functions; run_remote post.sh"
|
||||
chroot /sysroot bash -c "umount \$(tac /proc/mounts|awk '{print \$2}'|grep ^/run/imginst/targ)"
|
||||
|
||||
@@ -59,7 +59,7 @@ rpm --import /etc/pki/rpm-gpg/*
|
||||
|
||||
run_remote_python add_local_repositories
|
||||
run_remote_python syncfileclient
|
||||
run_remote_python confignet
|
||||
run_remote_python confignet -c $confluent_mgr
|
||||
|
||||
run_remote onboot.custom
|
||||
# onboot scripts may be placed into onboot.d, e.g. onboot.d/01-firstaction.sh, onboot.d/02-secondaction.sh
|
||||
|
||||
@@ -2,13 +2,17 @@
|
||||
|
||||
# This script is executed 'chrooted' into a cloned disk target before rebooting
|
||||
#
|
||||
|
||||
if [ -f /etc/dracut.conf.d/diskless.conf ]; then
|
||||
rm /etc/dracut.conf.d/diskless.conf
|
||||
fi
|
||||
for kver in /lib/modules/*; do kver=$(basename $kver); kernel-install add $kver /boot/vmlinuz-$kver; done
|
||||
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
|
||||
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
|
||||
confluent_mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
export nodename confluent_mgr confluent_profile
|
||||
. /etc/confluent/functions
|
||||
run_remote setupssh
|
||||
mkdir -p /var/log/confluent
|
||||
chmod 700 /var/log/confluent
|
||||
exec >> /var/log/confluent/confluent-post.log
|
||||
@@ -33,6 +37,8 @@ run_remote_parts post.d
|
||||
# Induce execution of remote configuration, e.g. ansible plays in ansible/post.d/
|
||||
run_remote_config post.d
|
||||
|
||||
cd /root/
|
||||
fetch_remote confignet
|
||||
curl -sf -X POST -d 'status: staged' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus
|
||||
|
||||
kill $logshowpid
|
||||
|
||||
@@ -3,10 +3,12 @@ echo -n "" >> /tmp/net.ifaces
|
||||
echo -n "" > /tmp/01-autocons.devnode
|
||||
BUNDLENAME=/etc/pki/tls/certs/ca-bundle.crt
|
||||
if [ ! -e "$BUNDLENAME" ]; then
|
||||
BUNDLENAME=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
|
||||
mkdir -p /etc/pki/tls/certs
|
||||
ln -s $BUNDLENAME /etc/pki/tls/certs/ca-bundle.crt
|
||||
fi
|
||||
if [ -e /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem ]; then
|
||||
BUNDLENAME=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
|
||||
ln -s $BUNDLENAME /etc/pki/tls/certs/ca-bundle.crt
|
||||
fi
|
||||
fi
|
||||
cat /tls/*.0 >> $BUNDLENAME
|
||||
if ! grep console= /proc/cmdline >& /dev/null; then
|
||||
autocons=$(/opt/confluent/bin/autocons)
|
||||
|
||||
@@ -3,6 +3,10 @@ sed -i 's/centos/CentOS/; s/rhel/Red Hat Enterprise Linux/; s/oraclelinux/Oracle
|
||||
if grep Fedora $2/profile.yaml > /dev/null; then
|
||||
sed -i 's/@^minimal-environment/#/' $2/packagelist
|
||||
fi
|
||||
if grep ^label: $2/profile.yaml | grep 10 > /dev/null; then
|
||||
echo 'echo openssh-keysign >> /tmp/addonpackages' > $2/scripts/pre.d/enablekeysign
|
||||
chmod 644 $2/scripts/pre.d/enablekeysign
|
||||
fi
|
||||
ln -s $1/images/pxeboot/vmlinuz $2/boot/kernel && \
|
||||
ln -s $1/images/pxeboot/initrd.img $2/boot/initramfs/distribution
|
||||
mkdir -p $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,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
+15
-2
@@ -42,7 +42,9 @@ if ! grep console= /proc/cmdline >& /dev/null; then
|
||||
autocons=$(/opt/confluent/bin/autocons)
|
||||
autoconsdev=${autocons%,*}
|
||||
autocons=${autocons##*/}
|
||||
echo "Automatic console configured for $autocons"
|
||||
if [ ! -z "$autocons" ]; then
|
||||
echo "Automatic console configured for $autocons"
|
||||
fi
|
||||
fi
|
||||
echo "Initializing confluent diskless environment"
|
||||
echo -n "udevd: "
|
||||
@@ -102,6 +104,14 @@ while ! grep ^EXTMGRINFO: /etc/confluent/confluent.info | awk -F'|' '{print $3}'
|
||||
ip link set $i up
|
||||
done
|
||||
/opt/confluent/bin/copernicus -t > /etc/confluent/confluent.info
|
||||
echo -n .
|
||||
done
|
||||
TRIES=0
|
||||
while ! grep ^NODENAME: /etc/confluent/confluent.info >& /dev/null && [ "$TRIES" -lt 300 ]; do
|
||||
sleep 0.5
|
||||
echo -n .
|
||||
/opt/confluent/bin/copernicus -t > /etc/confluent/confluent.info
|
||||
TRIES=$((TRIES + 1))
|
||||
done
|
||||
cd /
|
||||
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
|
||||
@@ -302,7 +312,10 @@ for pubkey in /etc/ssh/ssh_host*key.pub; do
|
||||
fi
|
||||
echo HostKey $privfile >> /etc/ssh/sshd_config
|
||||
done
|
||||
/usr/sbin/sshd
|
||||
if grep "debugssh" /proc/cmdline > /dev/null; then
|
||||
/usr/sbin/sshd
|
||||
fi
|
||||
echo "done"
|
||||
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg| awk '{print $2}')
|
||||
confluent_proto=$(grep ^protocol: /etc/confluent/confluent.deploycfg| awk '{print $2}')
|
||||
confluent_urls=""
|
||||
|
||||
@@ -31,8 +31,10 @@ done
|
||||
if [ ! -f /etc/confluent/firstboot.ran ]; then
|
||||
touch /etc/confluent/firstboot.ran
|
||||
|
||||
cat /etc/confluent/tls/*.pem >> /etc/pki/tls/certs/ca-bundle.crt
|
||||
|
||||
cat /etc/confluent/tls/*.pem >> /etc/pki/tls/certs/ca-bundle.crt
|
||||
confluentpython /root/confignet
|
||||
rm /root/confignet
|
||||
run_remote firstboot.custom
|
||||
# Firstboot scripts may be placed into firstboot.d, e.g. firstboot.d/01-firstaction.sh, firstboot.d/02-secondaction.sh
|
||||
run_remote_parts firstboot.d
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@ import struct
|
||||
import sys
|
||||
import subprocess
|
||||
import traceback
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
yaml = None
|
||||
|
||||
bootuuid = None
|
||||
vgname = 'localstorage'
|
||||
@@ -66,9 +70,9 @@ def get_image_metadata(imgpath):
|
||||
yield md
|
||||
else:
|
||||
# plausible filesystem structure to apply to a nominally "diskless" image
|
||||
yield {'mount': '/', 'filesystem': 'xfs', 'minsize': 39513563136, 'initsize': 954128662528, 'flags': 'rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota', 'device': '/dev/mapper/root', 'compressed_size': 27022069760}
|
||||
yield {'mount': '/boot', 'filesystem': 'xfs', 'minsize': 232316928, 'initsize': 1006632960, 'flags': 'rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota', 'device': '/dev/nvme1n1p2', 'compressed_size': 171462656}
|
||||
yield {'mount': '/boot/efi', 'filesystem': 'vfat', 'minsize': 7835648, 'initsize': 627900416, 'flags': 'rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=ascii,shortname=winnt,errors=remount-ro', 'device': '/dev/nvme1n1p1', 'compressed_size': 1576960}
|
||||
yield {'mount': '/', 'filesystem': 'xfs', 'minsize': 4294967296, 'initsize': 954128662528, 'flags': 'rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota', 'device': '/dev/mapper/root', 'compressed_size': 27022069760}
|
||||
yield {'mount': '/boot', 'filesystem': 'xfs', 'minsize': 536870912, 'initsize': 1006632960, 'flags': 'rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota', 'device': '/dev/nvme1n1p2', 'compressed_size': 171462656}
|
||||
yield {'mount': '/boot/efi', 'filesystem': 'vfat', 'minsize': 33554432, 'initsize': 627900416, 'flags': 'rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=ascii,shortname=winnt,errors=remount-ro', 'device': '/dev/nvme1n1p1', 'compressed_size': 1576960}
|
||||
#raise Exception('Installation from single part image not supported')
|
||||
|
||||
class PartedRunner():
|
||||
@@ -166,6 +170,15 @@ def fixup(rootdir, vols):
|
||||
grubsyscfg = os.path.join(rootdir, 'etc/sysconfig/grub')
|
||||
if not os.path.exists(grubsyscfg):
|
||||
grubsyscfg = os.path.join(rootdir, 'etc/default/grub')
|
||||
currcmdline = []
|
||||
with open('/proc/cmdline') as cmdlinein:
|
||||
cmdline = cmdlinein.read().strip()
|
||||
for arg in cmdline.split():
|
||||
if arg.startswith('console='):
|
||||
currcmdline.append(arg)
|
||||
elif arg == 'quiet':
|
||||
currcmdline.append(arg)
|
||||
currcmdlinestr = ' '.join(currcmdline)
|
||||
kcmdline = os.path.join(rootdir, 'etc/kernel/cmdline')
|
||||
if os.path.exists(kcmdline):
|
||||
with open(kcmdline) as kcmdlinein:
|
||||
@@ -177,8 +190,10 @@ def fixup(rootdir, vols):
|
||||
elif ent.startswith('root='):
|
||||
newkcmdlineent.append('root={}'.format(newrootdev))
|
||||
elif ent.startswith('rd.lvm.lv='):
|
||||
ent = convert_lv(ent)
|
||||
if ent:
|
||||
nent = convert_lv(ent)
|
||||
if nent:
|
||||
newkcmdlineent.append(ent)
|
||||
else:
|
||||
newkcmdlineent.append(ent)
|
||||
else:
|
||||
newkcmdlineent.append(ent)
|
||||
@@ -200,8 +215,10 @@ def fixup(rootdir, vols):
|
||||
elif cfgpart.startswith('resume='):
|
||||
newcfgparts.append('resume={}'.format(newswapdev))
|
||||
elif cfgpart.startswith('rd.lvm.lv='):
|
||||
cfgpart = convert_lv(cfgpart)
|
||||
if cfgpart:
|
||||
ncfgpart = convert_lv(cfgpart)
|
||||
if ncfgpart:
|
||||
newcfgparts.append(ncfgpart)
|
||||
else:
|
||||
newcfgparts.append(cfgpart)
|
||||
else:
|
||||
newcfgparts.append(cfgpart)
|
||||
@@ -217,13 +234,13 @@ def fixup(rootdir, vols):
|
||||
'GRUB_DISABLE_SUBMENU=true',
|
||||
'GRUB_TERMINAL=""',
|
||||
'GRUB_SERIAL_COMMAND=""',
|
||||
'GRUB_CMDLINE_LINUX="crashkernel=1G-4G:192M,4G-64G:256M,64G-:512M rd.lvm.lv=vg/root rd.lvm.lv=vg/swap"',
|
||||
'GRUB_CMDLINE_LINUX="{}crashkernel=1G-4G:192M,4G-64G:256M,64G-:512M rd.lvm.lv=vg/root rd.lvm.lv=vg/swap"'.format(currcmdlinestr),
|
||||
'GRUB_DISABLE_RECOVERY="true"',
|
||||
'GRUB_ENABLE_BLSCFG=true',
|
||||
]
|
||||
if not os.path.exists(os.path.join(rootdir, "etc/kernel/cmdline")):
|
||||
with open(os.path.join(rootdir, "etc/kernel/cmdline"), "w") as cmdlineout:
|
||||
cmdlineout.write("root=/dev/mapper/localstorage-root rd.lvm.lv=localstorage/root")
|
||||
cmdlineout.write("{} root=/dev/mapper/localstorage-root rd.lvm.lv=localstorage/root".format(currcmdlinestr))
|
||||
with open(grubsyscfg, 'w') as defgrubout:
|
||||
for gline in defgrub:
|
||||
gline = gline.split()
|
||||
@@ -234,11 +251,11 @@ def fixup(rootdir, vols):
|
||||
elif ent.startswith('root='):
|
||||
newline.append('root={}'.format(newrootdev))
|
||||
elif ent.startswith('rd.lvm.lv='):
|
||||
ent = convert_lv(ent)
|
||||
if ent:
|
||||
nent = convert_lv(ent)
|
||||
if nent:
|
||||
newline.append(nent)
|
||||
else:
|
||||
newline.append(ent)
|
||||
elif '""' in ent:
|
||||
newline.append('""')
|
||||
else:
|
||||
newline.append(ent)
|
||||
defgrubout.write(' '.join(newline) + '\n')
|
||||
@@ -301,8 +318,8 @@ def fixup(rootdir, vols):
|
||||
for vol in vols:
|
||||
if vol['mount'] == '/boot/efi':
|
||||
targdev = vol['targetdisk']
|
||||
partnum = re.search('(\d+)$', targdev).group(1)
|
||||
targblock = re.search('(.*)\d+$', targdev).group(1)
|
||||
partnum = re.search(r'(\d+)$', targdev).group(1)
|
||||
targblock = re.search(r'(.*)\d+$', targdev).group(1)
|
||||
if targblock:
|
||||
if targblock.endswith('p') and 'nvme' in targblock:
|
||||
targblock = targblock[:-1]
|
||||
@@ -334,13 +351,16 @@ def had_swap():
|
||||
|
||||
newrootdev = None
|
||||
newswapdev = None
|
||||
vgmap = None
|
||||
def install_to_disk(imgpath):
|
||||
global vgmap
|
||||
global bootuuid
|
||||
global newrootdev
|
||||
global newswapdev
|
||||
global vgname
|
||||
global oldvgname
|
||||
lvmvols = {}
|
||||
vgmap = {}
|
||||
deftotsize = 0
|
||||
mintotsize = 0
|
||||
deflvmsize = 0
|
||||
@@ -365,24 +385,30 @@ def install_to_disk(imgpath):
|
||||
mintotsize = swapsize
|
||||
for fs in get_image_metadata(imgpath):
|
||||
allvols.append(fs)
|
||||
deftotsize += fs['initsize']
|
||||
mintotsize += fs['minsize']
|
||||
if fs['initsize'] > biggestsize:
|
||||
biggestfs = fs
|
||||
biggestsize = fs['initsize']
|
||||
|
||||
if fs['device'].startswith('/dev/mapper'):
|
||||
oldvgname = fs['device'].rsplit('/', 1)[-1]
|
||||
odevname = fs['device'].rsplit('/', 1)[-1]
|
||||
# if node has - then /dev/mapper will double up the hypen
|
||||
if '_' in oldvgname and '-' in oldvgname.split('_')[-1]:
|
||||
oldvgname = oldvgname.rsplit('-', 1)[0].replace('--', '-')
|
||||
if '_' in odevname and '-' in odevname.split('_', 1)[-1]:
|
||||
oldvgname = odevname.rsplit('-', 1)[0].replace('--', '-')
|
||||
osname = oldvgname.split('_')[0]
|
||||
nodename = socket.gethostname().split('.')[0]
|
||||
vgname = '{}_{}'.format(osname, nodename)
|
||||
lvmvols[fs['device'].replace('/dev/mapper/', '')] = fs
|
||||
elif '-' in odevname: # unique one
|
||||
vgmap[odevname] = odevname.split('-')[0]
|
||||
lvmvols[odevname] = fs
|
||||
|
||||
continue
|
||||
lvmvols[odevname] = fs
|
||||
deflvmsize += fs['initsize']
|
||||
minlvmsize += fs['minsize']
|
||||
else:
|
||||
plainvols[int(re.search('(\d+)$', fs['device'])[0])] = fs
|
||||
plainvols[int(re.search(r'(\d+)$', fs['device'])[0])] = fs
|
||||
if fs['initsize'] > biggestsize:
|
||||
biggestfs = fs
|
||||
biggestsize = fs['initsize']
|
||||
deftotsize += fs['initsize']
|
||||
mintotsize += fs['minsize']
|
||||
with open('/tmp/installdisk') as diskin:
|
||||
instdisk = diskin.read()
|
||||
instdisk = '/dev/' + instdisk
|
||||
@@ -440,6 +466,28 @@ def install_to_disk(imgpath):
|
||||
lvmpart = get_partname(instdisk, volidx + 1)
|
||||
subprocess.check_call(['pvcreate', '-ff', '-y', lvmpart])
|
||||
subprocess.check_call(['vgcreate', vgname, lvmpart])
|
||||
vgroupmap = {}
|
||||
if yaml and vgmap:
|
||||
with open('/tmp/volumegroupmap.yml') as mapin:
|
||||
vgroupmap = yaml.safe_load(mapin)
|
||||
donedisks = {}
|
||||
for morevolname in vgmap:
|
||||
morevg = vgmap[morevolname]
|
||||
if morevg not in vgroupmap:
|
||||
raise Exception("No mapping defined to create volume group {}".format(morevg))
|
||||
targdisk = vgroupmap[morevg]
|
||||
if targdisk not in donedisks:
|
||||
moreparted = PartedRunner(targdisk)
|
||||
moreparted.run('mklabel gpt')
|
||||
moreparted.run('mkpart lvm 0% 100%')
|
||||
morelvmpart = get_partname(targdisk, 1)
|
||||
subprocess.check_call(['pvcreate', '-ff', '-y', morelvmpart])
|
||||
subprocess.check_call(['vgcreate', morevg, morelvmpart])
|
||||
donedisks[targdisk] = 1
|
||||
morelvname = morevolname.split('-', 1)[1]
|
||||
subprocess.check_call(['lvcreate', '-L', '{}b'.format(lvmvols[morevolname]['initsize']), '-y', '-n', morelvname, morevg])
|
||||
lvmvols[morevolname]['targetdisk'] = '/dev/{}/{}'.format(morevg, morelvname)
|
||||
|
||||
vginfo = subprocess.check_output(['vgdisplay', vgname, '--units', 'b']).decode('utf8')
|
||||
vginfo = vginfo.split('\n')
|
||||
pesize = 0
|
||||
@@ -452,6 +500,9 @@ def install_to_disk(imgpath):
|
||||
pes = int(infline[4])
|
||||
takeaway = swapsize // pesize
|
||||
for volidx in lvmvols:
|
||||
if volidx in vgmap:
|
||||
# was handled previously
|
||||
continue
|
||||
vol = lvmvols[volidx]
|
||||
if vol is biggestfs:
|
||||
continue
|
||||
@@ -460,6 +511,10 @@ def install_to_disk(imgpath):
|
||||
biggestextents = pes - takeaway
|
||||
for volidx in lvmvols:
|
||||
vol = lvmvols[volidx]
|
||||
if volidx in vgmap:
|
||||
# was handled previously
|
||||
continue
|
||||
|
||||
if vol is biggestfs:
|
||||
extents = biggestextents
|
||||
else:
|
||||
@@ -546,7 +601,13 @@ def install_to_disk(imgpath):
|
||||
|
||||
|
||||
|
||||
subprocess.check_call(['umount', '/run/imginst/targ'])
|
||||
while True:
|
||||
try:
|
||||
subprocess.check_call(['umount', '/run/imginst/targ'])
|
||||
break
|
||||
except subprocess.CalledProcessError:
|
||||
print("Failed to unmount /run/imginst/targ, retrying")
|
||||
time.sleep(1)
|
||||
for vol in allvols:
|
||||
subprocess.check_call(['mount', vol['targetdisk'], '/run/imginst/targ/' + vol['mount']])
|
||||
fixup('/run/imginst/targ', allvols)
|
||||
|
||||
@@ -130,4 +130,9 @@ 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
|
||||
|
||||
@@ -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,129 @@
|
||||
#!/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)
|
||||
for adp in devbyadp:
|
||||
adaplist = subprocess.check_output(['localcli', 'storage', 'core', 'adapter', 'listdetailed', '--adapter', adp])
|
||||
if not isinstance(adaplist, str):
|
||||
adaplist = adaplist.decode('utf8')
|
||||
for line in adaplist.split('\n'):
|
||||
if not line.strip():
|
||||
continue
|
||||
if 'Driver Name:' in line:
|
||||
driver = ' '.join(line.split()[2:])
|
||||
for dev in devbyadp[adp]:
|
||||
disks[dev]['adapter_driver'] = driver
|
||||
return disks
|
||||
|
||||
def main():
|
||||
disks = []
|
||||
alldisks = list_disks()
|
||||
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')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,9 +1,11 @@
|
||||
#!/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
|
||||
python3 /tmp/getinstalldisk
|
||||
rootpw=$(grep ^rootpassword: /etc/confluent/confluent.deploycfg|sed -e 's/^rootpassword: //')
|
||||
echo rootpw --iscrypted $rootpw > /tmp/rootpw
|
||||
export BOOT_CMDLINE=ks=/etc/confluent/ks.cfg
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
@@ -52,13 +52,40 @@ while ! grep NODENAME /custom-installation/confluent/confluent.info; do
|
||||
ipconfig -d $MYIP::$MYGW:$MYNM::$NIC
|
||||
echo $NIC > /tmp/autodetectnic
|
||||
else
|
||||
configure_networking
|
||||
for dsrv in $deploysrvs; do
|
||||
if openssl s_client -connect $dsrv:443 > /dev/null 2>&1; then
|
||||
deploysrvs=$dsrv
|
||||
break
|
||||
rmmod cdc_ether 2> /dev/null
|
||||
while [ ! -f /run/confirmednic ]; do
|
||||
ALLNETDEVS=$(ip a|grep LOWER_UP|grep MULTICAST|awk '{print $2}'|sed -e s/://)
|
||||
rm -rf /run/net* /run/dhcpcd /var/lib/dhcpcd
|
||||
for dev in $(ip a|grep MULTICAST|awk '{print $2}'|sed -e s/://); do
|
||||
ip a flush $dev
|
||||
echo 1 > /proc/sys/net/ipv6/conf/$dev/addr_gen_mode
|
||||
echo 0 > /proc/sys/net/ipv6/conf/$dev/addr_gen_mode
|
||||
done
|
||||
unset DEVICE DEVICE6 IP IP6 dev
|
||||
[ -z "$1" ] || DEVICE=$1
|
||||
shift
|
||||
configure_networking
|
||||
echo $DEVICE > /tmp/autodetectnic
|
||||
for dsrv in $deploysrvs; do
|
||||
(if openssl s_client -connect $dsrv:443 > /dev/null 2>&1; then
|
||||
echo $dsrv > /run/confirmednic
|
||||
break
|
||||
fi) &
|
||||
chkpid=$!
|
||||
( sleep 10 && kill $chkpid ) &
|
||||
timeoutpid=$!
|
||||
wait $chkpid
|
||||
kill $timeoutpid 2> /dev/null
|
||||
unset chkpid timeoutpid
|
||||
done
|
||||
if [ ! -f /run/confirmednic ]; then
|
||||
echo "No connectivity to deployment servers, retrying..."
|
||||
[ -z "$1" ] && set -- $ALLNETDEVS
|
||||
fi
|
||||
done
|
||||
deploysrvs=$(cat /run/confirmednic)
|
||||
rm /run/confirmednic
|
||||
|
||||
fi
|
||||
MGR=$deploysrvs
|
||||
NODENAME=$(grep ^nodename: /tmp/idntmnt/cnflnt.yml | awk '{print $2}')
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
@@ -53,13 +53,40 @@ while ! grep NODENAME /custom-installation/confluent/confluent.info; do
|
||||
ipconfig -d $MYIP::$MYGW:$MYNM::$NIC
|
||||
echo $NIC > /tmp/autodetectnic
|
||||
else
|
||||
configure_networking
|
||||
for dsrv in $deploysrvs; do
|
||||
if openssl s_client -connect $dsrv:443 > /dev/null 2>&1; then
|
||||
deploysrvs=$dsrv
|
||||
break
|
||||
rmmod cdc_ether 2> /dev/null
|
||||
while [ ! -f /run/confirmednic ]; do
|
||||
ALLNETDEVS=$(ip a|grep LOWER_UP|grep MULTICAST|awk '{print $2}'|sed -e s/://)
|
||||
|
||||
rm -rf /run/net* /run/dhcpcd /var/lib/dhcpcd
|
||||
for dev in $(ip a|grep MULTICAST|awk '{print $2}'|sed -e s/://); do
|
||||
ip a flush $dev
|
||||
echo 1 > /proc/sys/net/ipv6/conf/$dev/addr_gen_mode
|
||||
echo 0 > /proc/sys/net/ipv6/conf/$dev/addr_gen_mode
|
||||
done
|
||||
unset DEVICE DEVICE6 IP IP6 dev
|
||||
[ -z "$1" ] || DEVICE=$1
|
||||
shift
|
||||
configure_networking
|
||||
echo $DEVICE > /tmp/autodetectnic
|
||||
for dsrv in $deploysrvs; do
|
||||
(if openssl s_client -connect $dsrv:443 > /dev/null 2>&1; then
|
||||
echo $dsrv > /run/confirmednic
|
||||
break
|
||||
fi) &
|
||||
chkpid=$!
|
||||
( sleep 10 && kill $chkpid ) &
|
||||
timeoutpid=$!
|
||||
wait $chkpid
|
||||
kill $timeoutpid 2> /dev/null
|
||||
unset chkpid timeoutpid
|
||||
done
|
||||
if [ ! -f /run/confirmednic ]; then
|
||||
echo "No connectivity to deployment servers, retrying..."
|
||||
[ -z "$1" ] && set -- $ALLNETDEVS
|
||||
fi
|
||||
done
|
||||
deploysrvs=$(cat /run/confirmednic)
|
||||
rm /run/confirmednic
|
||||
fi
|
||||
MGR=$deploysrvs
|
||||
NODENAME=$(grep ^nodename: /tmp/idntmnt/cnflnt.yml | awk '{print $2}')
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
#!/bin/sh
|
||||
sed -i 's/label: ubuntu/label: Ubuntu/' $2/profile.yaml && \
|
||||
ln -s $1/casper/vmlinuz $2/boot/kernel && \
|
||||
ln -s $1/casper/initrd $2/boot/initramfs/distribution && \
|
||||
mkdir -p $2/boot/efi/boot && \
|
||||
#!/bin/bash
|
||||
set -e
|
||||
sed -i 's/label: ubuntu/label: Ubuntu/' $2/profile.yaml
|
||||
if [ -e $1/casper/hwe-vmlinuz ]; then
|
||||
ln -s $1/casper/hwe-vmlinuz $2/boot/kernel
|
||||
else
|
||||
ln -s $1/casper/vmlinuz $2/boot/kernel
|
||||
fi
|
||||
if [ -e $1/casper/hwe-initrd ]; then
|
||||
ln -s $1/casper/hwe-initrd $2/boot/initramfs/distribution
|
||||
else
|
||||
ln -s $1/casper/initrd $2/boot/initramfs/distribution
|
||||
fi
|
||||
mkdir -p $2/boot/efi/boot
|
||||
if [ -d $1/EFI/boot/ ]; then
|
||||
ln -s $1/EFI/boot/* $2/boot/efi/boot
|
||||
elif [ -d $1/efi/boot/ ]; then
|
||||
ln -s $1/efi/boot/* $2/boot/efi/boot
|
||||
else
|
||||
echo "Unrecogrized boot contents in media" >&2
|
||||
echo "Unrecognized boot contents in media" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@ function test_mgr() {
|
||||
return 1
|
||||
}
|
||||
|
||||
function initconfluentscriptstmp() {
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
mkdir -p /opt/confluent/tmpexec
|
||||
confluentscripttmpdir=$(mktemp -d /opt/confluent/tmpexec/confluentscripts.XXXXXXXXX)
|
||||
fi
|
||||
}
|
||||
|
||||
function confluentpython() {
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
@@ -72,7 +79,8 @@ fetch_remote() {
|
||||
}
|
||||
|
||||
source_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -86,7 +94,8 @@ source_remote_parts() {
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
apiclient=/opt/confluent/bin/apiclient
|
||||
if [ -f /etc/confluent/apiclient ]; then
|
||||
apiclient=/etc/confluent/apiclient
|
||||
@@ -105,10 +114,7 @@ source_remote() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Sourcing $1 from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Sourcing from $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -135,9 +141,9 @@ run_remote() {
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
if [ -z "$confluentscripttmpdir" ]; then
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unsettmpdir=1
|
||||
fi
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
fetch_remote $1
|
||||
@@ -170,7 +176,8 @@ run_remote_python() {
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/
|
||||
confluentscripttmpdir=$(mktemp -d /tmp/confluentscripts.XXXXXXXXX)
|
||||
unset confluentscripttmpdir
|
||||
initconfluentscriptstmp
|
||||
echo Executing in $confluentscripttmpdir
|
||||
cd $confluentscripttmpdir
|
||||
mkdir -p $(dirname $1)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
class SilentException(Exception):
|
||||
pass
|
||||
|
||||
class DiskInfo(object):
|
||||
def __init__(self, devname):
|
||||
if devname.startswith('nvme') and 'c' in devname:
|
||||
@@ -24,9 +27,11 @@ class DiskInfo(object):
|
||||
continue
|
||||
k, v = prop.split('=', 1)
|
||||
if k == 'DEVTYPE' and v != 'disk':
|
||||
if v == 'partition':
|
||||
raise SilentException('Partition')
|
||||
raise Exception('Not a disk')
|
||||
elif k == 'DM_NAME':
|
||||
raise Exception('Device Mapper')
|
||||
raise SilentException('Device Mapper')
|
||||
elif k == 'ID_MODEL':
|
||||
self.model = v
|
||||
elif k == 'DEVPATH':
|
||||
@@ -50,13 +55,17 @@ class DiskInfo(object):
|
||||
self.driver = v.replace('"', '')
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
elif k == 'ATTR{ro}' and v == '"1"':
|
||||
raise Exception("Device is read-only")
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if self.driver == 'sr':
|
||||
raise Exception('cd/dvd')
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
self.size = int(sizesrc.read()) * 512
|
||||
if int(self.size) < 536870912:
|
||||
raise Exception("Device too small for install")
|
||||
if int(self.size) < 2147483648:
|
||||
raise Exception("Device too small for install ({}MiB)".format(int(self.size)/1024/1024))
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
@@ -89,9 +98,11 @@ def main():
|
||||
try:
|
||||
disk = DiskInfo(disk)
|
||||
disks.append(disk)
|
||||
except SilentException:
|
||||
pass
|
||||
except Exception as e:
|
||||
print("Skipping {0}: {1}".format(disk, str(e)))
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: x.priority)]
|
||||
nd = [x.name for x in sorted(disks, key=lambda x: [x.priority, x.size])]
|
||||
if nd:
|
||||
open('/tmp/installdisk', 'w').write(nd[0])
|
||||
|
||||
|
||||
@@ -2,20 +2,23 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"net"
|
||||
"net/http"
|
||||
"crypto/x509"
|
||||
"crypto/tls"
|
||||
"os"
|
||||
"strings"
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ApiClient struct {
|
||||
server string
|
||||
server string
|
||||
urlserver string
|
||||
apikey string
|
||||
nodename string
|
||||
apikey string
|
||||
nodename string
|
||||
webclient *http.Client
|
||||
}
|
||||
|
||||
@@ -24,7 +27,7 @@ func NewApiClient(cafile string, keyfile string, nodename string, server string)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cacerts := x509.NewCertPool()
|
||||
cacerts := x509.NewCertPool()
|
||||
cacerts.AppendCertsFromPEM(currcacerts)
|
||||
apikey := []byte("")
|
||||
if keyfile != "" {
|
||||
@@ -32,7 +35,7 @@ func NewApiClient(cafile string, keyfile string, nodename string, server string)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if apikey[len(apikey) - 1] == 0xa {
|
||||
if apikey[len(apikey)-1] == 0xa {
|
||||
apikey = apikey[:len(apikey)-1]
|
||||
}
|
||||
}
|
||||
@@ -40,7 +43,9 @@ func NewApiClient(cafile string, keyfile string, nodename string, server string)
|
||||
cinfo, err := os.ReadFile("/etc/confluent/confliuent.info")
|
||||
if err != nil {
|
||||
nodename, err = os.Hostname()
|
||||
if err != nil { return nil, err }
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
cinfolines := bytes.Split(cinfo, []byte("\n"))
|
||||
if bytes.Contains(cinfolines[0], []byte("NODENAME")) {
|
||||
@@ -48,6 +53,20 @@ func NewApiClient(cafile string, keyfile string, nodename string, server string)
|
||||
nodename = string(cnodebytes[0])
|
||||
}
|
||||
}
|
||||
// Test connectivity with up to 3 retries
|
||||
var conn net.Conn
|
||||
for i := 0; i < 3; i++ {
|
||||
conn, err = net.Dial("tcp", net.JoinHostPort(server, "443"))
|
||||
if err == nil {
|
||||
conn.Close()
|
||||
break
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
fmt.Print("Connection attempt failed, retrying...\n")
|
||||
if i == 2 {
|
||||
return nil, fmt.Errorf("failed to connect after 3 attempts: %v", err)
|
||||
}
|
||||
}
|
||||
urlserver := server
|
||||
if strings.Contains(server, ":") {
|
||||
if strings.Contains(server, "%") && !strings.Contains(server, "%25") {
|
||||
@@ -58,10 +77,11 @@ func NewApiClient(cafile string, keyfile string, nodename string, server string)
|
||||
server = server[:strings.Index(server, "%")]
|
||||
}
|
||||
}
|
||||
|
||||
webclient := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: cacerts,
|
||||
RootCAs: cacerts,
|
||||
ServerName: server,
|
||||
},
|
||||
},
|
||||
@@ -70,34 +90,42 @@ func NewApiClient(cafile string, keyfile string, nodename string, server string)
|
||||
return &vc, nil
|
||||
}
|
||||
|
||||
func (apiclient *ApiClient) RegisterKey(crypted string, hmac string) (error) {
|
||||
func (apiclient *ApiClient) RegisterKey(crypted string, hmac string) error {
|
||||
cryptbytes := []byte(crypted)
|
||||
cryptbuffer := bytes.NewBuffer(cryptbytes)
|
||||
_, err := apiclient.request("/confluent-api/self/registerapikey", "", cryptbuffer, "", hmac)
|
||||
return err
|
||||
}
|
||||
|
||||
func (apiclient *ApiClient) Fetch(url string, outputfile string, mime string, body io.Reader) (error) {
|
||||
func (apiclient *ApiClient) Fetch(url string, outputfile string, mime string, body io.Reader) error {
|
||||
outp, err := os.Create(outputfile)
|
||||
if err != nil { return err }
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer outp.Close()
|
||||
rsp, err := apiclient.request(url, mime, body, "", "")
|
||||
if err != nil { return err }
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(outp, rsp)
|
||||
return err
|
||||
}
|
||||
|
||||
func (apiclient *ApiClient) GrabText(url string, mime string, body io.Reader) (string, error){
|
||||
func (apiclient *ApiClient) GrabText(url string, mime string, body io.Reader) (string, error) {
|
||||
rsp, err := apiclient.request(url, mime, body, "", "")
|
||||
if err != nil { return "", err }
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
rspdata, err := io.ReadAll(rsp)
|
||||
if err != nil { return "", err }
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
rsptxt := string(rspdata)
|
||||
return rsptxt, nil
|
||||
}
|
||||
|
||||
func (apiclient *ApiClient) request(url string, mime string, body io.Reader, method string, hmac string) (io.ReadCloser, error) {
|
||||
if ! strings.Contains(url, "https://") {
|
||||
if !strings.Contains(url, "https://") {
|
||||
url = fmt.Sprintf("https://%s%s", apiclient.urlserver, url)
|
||||
}
|
||||
if method == "" {
|
||||
@@ -114,8 +142,12 @@ func (apiclient *ApiClient) request(url string, mime string, body io.Reader, met
|
||||
} else {
|
||||
rq, err = http.NewRequest(method, url, body)
|
||||
}
|
||||
if err != nil { return nil, err }
|
||||
if (mime != "") { rq.Header.Set("Accept", mime) }
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if mime != "" {
|
||||
rq.Header.Set("Accept", mime)
|
||||
}
|
||||
rq.Header.Set("CONFLUENT_NODENAME", apiclient.nodename)
|
||||
if len(hmac) > 0 {
|
||||
rq.Header.Set("CONFLUENT_CRYPTHMAC", hmac)
|
||||
@@ -124,11 +156,12 @@ func (apiclient *ApiClient) request(url string, mime string, body io.Reader, met
|
||||
rq.Header.Set("CONFLUENT_APIKEY", apiclient.apikey)
|
||||
}
|
||||
rsp, err := apiclient.webclient.Do(rq)
|
||||
if err != nil { return nil, err }
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if rsp.StatusCode >= 300 {
|
||||
err = errors.New(rsp.Status)
|
||||
return nil, err
|
||||
}
|
||||
return rsp.Body, err
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
#include <sys/mount.h>
|
||||
#define __USE_GNU
|
||||
#include <sched.h>
|
||||
#include <string.h>
|
||||
int main(int argc, char* argv[]) {
|
||||
unshare(CLONE_NEWNS);
|
||||
if (argc < 2 || strcmp(argv[1], "-s")) {
|
||||
unshare(CLONE_NEWNS);
|
||||
}
|
||||
mount("/dev", "/sysroot/dev", NULL, MS_MOVE, NULL);
|
||||
mount("/proc", "/sysroot/proc", NULL, MS_MOVE, NULL);
|
||||
mount("/sys", "/sysroot/sys", NULL, MS_MOVE, NULL);
|
||||
|
||||
@@ -27,6 +27,25 @@ 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']):
|
||||
return True
|
||||
return False
|
||||
#dmesg snippets
|
||||
#[1203637.865870] neighbour: ndisc_cache: neighbor table overflow!
|
||||
#[1205244.122606] neighbour: arp_cache: neighbor table overflow!
|
||||
|
||||
|
||||
def fprint(txt):
|
||||
sys.stdout.write(txt)
|
||||
@@ -202,6 +221,13 @@ if __name__ == '__main__':
|
||||
emprint('Failed access, if selinux is enabled, `setsebool -P httpd_can_network_connect=1`, otherwise check web proxy configuration')
|
||||
else:
|
||||
emprint('Not Running (Example resolution: systemctl enable httpd --now)')
|
||||
fprint('IP neighbor table issue check:')
|
||||
if check_neigh_overflow():
|
||||
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')
|
||||
@@ -209,7 +235,26 @@ if __name__ == '__main__':
|
||||
emprint('TFTP failure, PXE will not work, though media and HTTP boot can still work. (Example resolution: osdeploy initialize -p)')
|
||||
fprint('SSH root user public key: ')
|
||||
if glob.glob('/var/lib/confluent/public/site/ssh/*.rootpubkey'):
|
||||
print('OK')
|
||||
if not glob.glob('/root/.ssh/id_*.pub'):
|
||||
emprint('No SSH keys for root user, passwordless SSH from managers to nodes may not work (Example resolution: osdeploy initialize -u)')
|
||||
for userpub in glob.glob('/root/.ssh/id_*.pub'):
|
||||
with open(userpub) as f:
|
||||
pubkey = f.read().strip()
|
||||
for sitepubkey in glob.glob('/var/lib/confluent/public/site/ssh/*.rootpubkey'):
|
||||
with open(sitepubkey) as sf:
|
||||
spubkey = sf.read().strip()
|
||||
for keyline in spubkey.split('\n'):
|
||||
if keyline == pubkey:
|
||||
print('OK')
|
||||
break
|
||||
else:
|
||||
continue
|
||||
break
|
||||
else:
|
||||
continue
|
||||
break
|
||||
else:
|
||||
emprint('No matching public key found for root user (Example resolution: osdeploy initialize -u)')
|
||||
else:
|
||||
emprint('No trusted ssh keys for root user, passwordless SSH from managers to nodes may not work (Example resolution: osdeploy initialize -u)')
|
||||
if sshutil.sshver() > 7.6:
|
||||
@@ -240,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():
|
||||
@@ -388,7 +435,9 @@ if __name__ == '__main__':
|
||||
else:
|
||||
emprint('Unknown error attempting confluent automation ssh:')
|
||||
sys.stderr.buffer.write(srun.stderr)
|
||||
os.kill(int(sshutil.agent_pid), signal.SIGTERM)
|
||||
if sshutil.agent_pid:
|
||||
os.kill(int(sshutil.agent_pid), signal.SIGTERM)
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("Skipping node checks, no node specified (Example: confluent_selfcheck -n n1)")
|
||||
# possible checks:
|
||||
|
||||
@@ -53,22 +53,61 @@ def get_ip_addresses():
|
||||
def check_apache_config(path):
|
||||
keypath = None
|
||||
certpath = None
|
||||
chainpath = None
|
||||
with open(path, 'r') as openf:
|
||||
webconf = openf.read()
|
||||
insection = False
|
||||
# we always manipulate the first VirtualHost section
|
||||
# since we are managing IP based SANs, then SNI
|
||||
# can never match anything but the first VirtualHost
|
||||
for line in webconf.split('\n'):
|
||||
line = line.strip()
|
||||
line = line.split('#')[0]
|
||||
if line.startswith('SSLCertificateFile'):
|
||||
_, certpath = line.split(None, 1)
|
||||
if line.startswith('SSLCertificateKeyFile'):
|
||||
if not certpath and line.startswith('SSLCertificateFile'):
|
||||
insection = True
|
||||
if not certpath:
|
||||
_, certpath = line.split(None, 1)
|
||||
if not keypath and line.startswith('SSLCertificateKeyFile'):
|
||||
insection = True
|
||||
_, keypath = line.split(None, 1)
|
||||
if not chainpath and line.startswith('SSLCertificateChainFile'):
|
||||
insection = True
|
||||
_, chainpath = line.split(None, 1)
|
||||
if insection and line.startswith('</VirtualHost>'):
|
||||
break
|
||||
return keypath, certpath, chainpath
|
||||
|
||||
def check_nginx_config(path):
|
||||
keypath = None
|
||||
certpath = None
|
||||
# again, we only care about the first server section
|
||||
# since IP won't trigger SNI matches down the configuration
|
||||
with open(path, 'r') as openf:
|
||||
webconf = openf.read()
|
||||
for line in webconf.split('\n'):
|
||||
if keypath and certpath:
|
||||
break
|
||||
line = line.strip()
|
||||
line = line.split('#')[0]
|
||||
for segment in line.split(';'):
|
||||
if not certpath and segment.startswith('ssl_certificate'):
|
||||
_, certpath = segment.split(None, 1)
|
||||
if not keypath and segment.startswith('ssl_certificate_key'):
|
||||
_, keypath = segment.split(None, 1)
|
||||
if keypath:
|
||||
keypath = keypath.strip('"')
|
||||
if certpath:
|
||||
certpath = certpath.strip('"')
|
||||
return keypath, certpath
|
||||
|
||||
def get_certificate_paths():
|
||||
keypath = None
|
||||
certpath = None
|
||||
chainpath = None
|
||||
ngkeypath = None
|
||||
ngbundlepath = None
|
||||
if os.path.exists('/etc/httpd/conf.d/ssl.conf'): # redhat way
|
||||
keypath, certpath = check_apache_config('/etc/httpd/conf.d/ssl.conf')
|
||||
keypath, certpath, chainpath = check_apache_config('/etc/httpd/conf.d/ssl.conf')
|
||||
if not keypath and os.path.exists('/etc/apache2'): # suse way
|
||||
for currpath, _, files in os.walk('/etc/apache2'):
|
||||
for fname in files:
|
||||
@@ -77,11 +116,32 @@ def get_certificate_paths():
|
||||
kploc = check_apache_config(os.path.join(currpath,
|
||||
fname))
|
||||
if keypath and kploc[0] and keypath != kploc[0]:
|
||||
return None, None # Ambiguous...
|
||||
return {'error': 'Ambiguous...'}
|
||||
if kploc[0]:
|
||||
keypath, certpath = kploc
|
||||
|
||||
return keypath, certpath
|
||||
keypath, certpath, chainpath = kploc
|
||||
if os.path.exists('/etc/nginx'): # nginx way
|
||||
for currpath, _, files in os.walk('/etc/nginx'):
|
||||
if ngkeypath:
|
||||
break
|
||||
for fname in files:
|
||||
if not fname.endswith('.conf'):
|
||||
continue
|
||||
ngkeypath, ngbundlepath = check_nginx_config(os.path.join(currpath,
|
||||
fname))
|
||||
if ngkeypath:
|
||||
break
|
||||
tlsmateriallocation = {}
|
||||
if keypath:
|
||||
tlsmateriallocation.setdefault('keys', []).append(keypath)
|
||||
if ngkeypath:
|
||||
tlsmateriallocation.setdefault('keys', []).append(ngkeypath)
|
||||
if certpath:
|
||||
tlsmateriallocation.setdefault('certs', []).append(certpath)
|
||||
if chainpath:
|
||||
tlsmateriallocation.setdefault('chains', []).append(chainpath)
|
||||
if ngbundlepath:
|
||||
tlsmateriallocation.setdefault('bundles', []).append(ngbundlepath)
|
||||
return tlsmateriallocation
|
||||
|
||||
def assure_tls_ca():
|
||||
keyout, certout = ('/etc/confluent/tls/cakey.pem', '/etc/confluent/tls/cacert.pem')
|
||||
@@ -208,8 +268,12 @@ def create_simple_ca(keyout, certout):
|
||||
|
||||
def create_certificate(keyout=None, certout=None, csrout=None):
|
||||
if not keyout:
|
||||
keyout, certout = get_certificate_paths()
|
||||
if not keyout:
|
||||
tlsmateriallocation = get_certificate_paths()
|
||||
keyout = tlsmateriallocation.get('keys', [None])[0]
|
||||
certout = tlsmateriallocation.get('certs', [None])[0]
|
||||
if not certout:
|
||||
certout = tlsmateriallocation.get('bundles', [None])[0]
|
||||
if not keyout or not certout:
|
||||
raise Exception('Unable to locate TLS certificate path automatically')
|
||||
assure_tls_ca()
|
||||
shortname = socket.gethostname().split('.')[0]
|
||||
@@ -291,6 +355,29 @@ def create_certificate(keyout=None, certout=None, csrout=None):
|
||||
'-startdate', '19700101010101Z', '-enddate', '21000101010101Z',
|
||||
'-extfile', extconfig
|
||||
])
|
||||
for keycopy in tlsmateriallocation.get('keys', []):
|
||||
if keycopy != keyout:
|
||||
shutil.copy2(keyout, keycopy)
|
||||
for certcopy in tlsmateriallocation.get('certs', []):
|
||||
if certcopy != certout:
|
||||
shutil.copy2(certout, certcopy)
|
||||
cacert = None
|
||||
with open('/etc/confluent/tls/cacert.pem', 'rb') as cacertfile:
|
||||
cacert = cacertfile.read()
|
||||
for bundlecopy in tlsmateriallocation.get('bundles', []):
|
||||
if bundlecopy != certout:
|
||||
shutil.copy2(certout, bundlecopy)
|
||||
with open(bundlecopy, 'ab') as bundlefile:
|
||||
bundlefile.write(b'\n')
|
||||
bundlefile.write(cacert)
|
||||
for chaincopy in tlsmateriallocation.get('chains', []):
|
||||
if chaincopy != certout:
|
||||
with open(chaincopy, 'wb') as chainfile:
|
||||
chainfile.write(cacert)
|
||||
else:
|
||||
with open(chaincopy, 'ab') as chainfile:
|
||||
chainfile.write(b'\n')
|
||||
chainfile.write(cacert)
|
||||
finally:
|
||||
os.remove(tmpconfig)
|
||||
if needcsr:
|
||||
|
||||
@@ -376,7 +376,7 @@ node = {
|
||||
'the managed node. If not specified, then console '
|
||||
'is disabled. "ipmi" should be specified for most '
|
||||
'systems if console is desired.'),
|
||||
'validvalues': ('ssh', 'ipmi', 'openbmc', 'tsmsol', 'vcenter'),
|
||||
'validvalues': ('ssh', 'ipmi', 'openbmc', 'tsmsol', 'vcenter', 'proxmox'),
|
||||
},
|
||||
# 'virtualization.host': {
|
||||
# 'description': ('Hypervisor where this node does/should reside'),
|
||||
@@ -478,6 +478,9 @@ node = {
|
||||
'This would be the default name per the deployed OS and can be a comma delimited list to denote members of '
|
||||
'a team or a single interface for VLAN/PKEY connections.'
|
||||
},
|
||||
'net.mtu': {
|
||||
'description': 'MTU to apply to this connection',
|
||||
},
|
||||
'net.vlan_id': {
|
||||
'description': 'Ethernet VLAN or InfiniBand PKEY to use for this connection. '
|
||||
'Specify the parent device using net.interface_names.'
|
||||
|
||||
@@ -300,6 +300,10 @@ def _init_core():
|
||||
'default': 'ipmi',
|
||||
}),
|
||||
},
|
||||
'certificate_authorities': PluginCollection({
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'ipmi',
|
||||
}),
|
||||
'clear': PluginRoute({
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'ipmi',
|
||||
@@ -498,6 +502,22 @@ def _init_core():
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'ipmi',
|
||||
}),
|
||||
'core': PluginCollection({
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'ipmi',
|
||||
}),
|
||||
'adapters': PluginCollection({
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'ipmi',
|
||||
}),
|
||||
'disks': PluginCollection({
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'ipmi',
|
||||
}),
|
||||
'misc': PluginCollection({
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'ipmi',
|
||||
}),
|
||||
'updatestatus': PluginRoute({
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'ipmi',
|
||||
|
||||
@@ -43,6 +43,8 @@ libc = ctypes.CDLL(ctypes.util.find_library('c'))
|
||||
|
||||
|
||||
def address_is_somewhat_trusted(address, nodename, cfm):
|
||||
if netutil.ip_on_same_subnet(address.split('%')[0], 'fe80::', 64):
|
||||
return True
|
||||
if netutil.address_is_local(address):
|
||||
return True
|
||||
authnets = cfm.get_node_attributes(nodename, 'trusted.subnets')
|
||||
|
||||
@@ -316,6 +316,8 @@ def list_matching_nodes(criteria):
|
||||
retnodes = []
|
||||
for node in known_nodes:
|
||||
for mac in known_nodes[node]:
|
||||
if mac not in known_info:
|
||||
continue
|
||||
info = known_info[mac]
|
||||
if _info_matches(info, criteria):
|
||||
retnodes.append(node)
|
||||
@@ -613,7 +615,11 @@ def handle_api_request(configmanager, inputdata, operation, pathcomponents):
|
||||
return [msg.AssignedResource(inputdata['node'])]
|
||||
elif operation == 'delete':
|
||||
mac = _get_mac_from_query(pathcomponents)
|
||||
del known_info[mac]
|
||||
for node in known_nodes:
|
||||
if mac in known_nodes[node]:
|
||||
del known_nodes[node][mac]
|
||||
if mac in known_info:
|
||||
del known_info[mac]
|
||||
return [msg.DeletedResource(mac)]
|
||||
raise exc.NotImplementedException(
|
||||
'Unable to {0} to {1}'.format(operation, '/'.join(pathcomponents)))
|
||||
@@ -1195,7 +1201,9 @@ def search_smms_by_cert(currsmm, cert, cfg):
|
||||
cd = cfg.get_node_attributes(currsmm, ['hardwaremanagement.manager',
|
||||
'pubkeys.tls_hardwaremanager'])
|
||||
smmaddr = cd.get(currsmm, {}).get('hardwaremanagement.manager', {}).get('value', None)
|
||||
wc = webclient.SecureHTTPConnection(currsmm, verifycallback=cv)
|
||||
if not smmaddr:
|
||||
smmaddr = currsmm
|
||||
wc = webclient.SecureHTTPConnection(smmaddr, verifycallback=cv)
|
||||
neighs = wc.grab_json_response('/scripts/neighdata.json')
|
||||
except Exception:
|
||||
return None
|
||||
@@ -1354,7 +1362,8 @@ def eval_node(cfg, handler, info, nodename, manual=False):
|
||||
errorstr = 'The detected node {0} was detected using switch, ' \
|
||||
'however the relevant port has too many macs learned ' \
|
||||
'for this type of device ({1}) to be discovered by ' \
|
||||
'switch.'.format(nodename, handler.devname)
|
||||
'switch. If this should be an enclosure, make sure there are ' \
|
||||
'defined nodes for the enclosure'.format(nodename, handler.devname)
|
||||
log.log({'error': errorstr})
|
||||
return
|
||||
if not discover_node(cfg, handler, info, nodename, manual):
|
||||
|
||||
@@ -68,14 +68,19 @@ class NodeHandler(generic.NodeHandler):
|
||||
self._srvroot = srvroot
|
||||
return self._srvroot
|
||||
|
||||
def get_manager_url(self, wc):
|
||||
mgrs = self.srvroot(wc).get('Managers', {}).get('@odata.id', None)
|
||||
if not mgrs:
|
||||
raise Exception("No Managers resource on BMC")
|
||||
rsp = wc.grab_json_response(mgrs)
|
||||
if len(rsp.get('Members', [])) != 1:
|
||||
raise Exception("Can not handle multiple Managers")
|
||||
mgrurl = rsp['Members'][0]['@odata.id']
|
||||
return mgrurl
|
||||
|
||||
def mgrinfo(self, wc):
|
||||
if not self._mgrinfo:
|
||||
mgrs = self.srvroot(wc)['Managers']['@odata.id']
|
||||
rsp = wc.grab_json_response(mgrs)
|
||||
if len(rsp['Members']) != 1:
|
||||
raise Exception("Can not handle multiple Managers")
|
||||
mgrurl = rsp['Members'][0]['@odata.id']
|
||||
self._mgrinfo = wc.grab_json_response(mgrurl)
|
||||
self._mgrinfo = wc.grab_json_response(self.get_manager_url(wc))
|
||||
return self._mgrinfo
|
||||
|
||||
|
||||
@@ -281,7 +286,7 @@ class NodeHandler(generic.NodeHandler):
|
||||
compip = compip.split('%')[0]
|
||||
ipkey = 'IPv6Addresses'
|
||||
else:
|
||||
ipkey = 'IPv6Addresses'
|
||||
ipkey = 'IPv4Addresses'
|
||||
actualnic = None
|
||||
for curractnic in actualnics:
|
||||
currnicinfo = wc.grab_json_response(curractnic)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import base64
|
||||
import codecs
|
||||
import confluent.discovery.handlers.imm as immhandler
|
||||
import confluent.discovery.handlers.xcc3 as xcc3handler
|
||||
import confluent.exceptions as exc
|
||||
import confluent.netutil as netutil
|
||||
import confluent.util as util
|
||||
@@ -477,6 +478,13 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
tmpaccount = None
|
||||
while status != 200:
|
||||
tries -= 1
|
||||
rsp, status = wc.grab_json_response_with_status(
|
||||
'/redfish/v1/AccountService/Accounts/{0}'.format(uid))
|
||||
if status >= 500:
|
||||
if tries < 0:
|
||||
raise Exception('Redfish account management failure')
|
||||
eventlet.sleep(30)
|
||||
continue
|
||||
rsp, status = wc.grab_json_response_with_status(
|
||||
'/redfish/v1/AccountService/Accounts/{0}'.format(uid),
|
||||
{'UserName': username}, method='PATCH')
|
||||
@@ -708,6 +716,13 @@ def remote_nodecfg(nodename, cfm):
|
||||
raise Exception('Cannot remote configure a system without known '
|
||||
'address')
|
||||
info = {'addresses': [ipaddr]}
|
||||
nh = NodeHandler(info, cfm)
|
||||
ipaddr = ipaddr[0]
|
||||
wc = webclient.SecureHTTPConnection(
|
||||
ipaddr, 443, verifycallback=lambda x: True)
|
||||
rsp = wc.grab_json_response('/DeviceDescription.json')
|
||||
if isinstance(rsp, list):
|
||||
nh = NodeHandler(info, cfm)
|
||||
else:
|
||||
nh = xcc3handler.NodeHandler(info, cfm)
|
||||
nh.config(nodename)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user