mirror of
https://github.com/xcat2/confluent.git
synced 2026-09-29 16:50:57 +00:00
Compare commits
117 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 203253e05f | |||
| edc4804146 | |||
| 7cfdf11bf2 | |||
| a3bd21d605 | |||
| 6d8474a16a | |||
| 5736c41daa | |||
| a5c4b64c60 | |||
| f7a940227d | |||
| ebf50359f0 | |||
| 5160023cc4 | |||
| a738b761b4 | |||
| d27ef81e32 | |||
| f5344fabaa | |||
| fa1c2f5c1e | |||
| 25c3f40559 | |||
| 5812a0eef6 | |||
| 086ce9823b | |||
| 2d6bdffebe | |||
| efdbeeae0d | |||
| a2a1142f18 | |||
| 8c89deaa95 | |||
| 1ec5231ebe | |||
| 674e2887f3 | |||
| 4768bc257a | |||
| 7610f9b963 | |||
| b29e7bc94a | |||
| 04d63a269d | |||
| e1bf22911b | |||
| d6642f1bde | |||
| 36f027ac71 | |||
| c025f4d2fc | |||
| 1238babe60 | |||
| f9a82bde00 | |||
| 48c868e935 | |||
| caf9115439 | |||
| 8b11acbcf2 | |||
| db0f91c160 | |||
| cbb46dec3a | |||
| 0afa4c217c | |||
| 47f04c8462 | |||
| 5b0e23b8d4 | |||
| 14d9284cc5 | |||
| cd251fa5d6 | |||
| 8d47395e53 | |||
| d19b5e4376 | |||
| 7a9276300a | |||
| 87ef68e26a | |||
| 55b97793fd | |||
| fa823510b6 | |||
| 99609aa669 | |||
| 906011a80b | |||
| ff7f5daac6 | |||
| 2d58741f15 | |||
| 57b74d59af | |||
| 191cd8192a | |||
| 475eaca56b | |||
| f33ddf3ab9 | |||
| 3422f3cdc5 | |||
| 4c74581f0c | |||
| 674d32e9e5 | |||
| 666059c8bf | |||
| 0137f99636 | |||
| 0c66021d3e | |||
| 014727d355 | |||
| dc262c366c | |||
| 8f99d87fda | |||
| edaaa2393d | |||
| 1ecef6f251 | |||
| 31c2c5f6f7 | |||
| c8747ac369 | |||
| f7e7d05729 | |||
| 40c74699f0 | |||
| 3903cda789 | |||
| 72049657d7 | |||
| 71cc0adadd | |||
| b4e6e7caa8 | |||
| 10ac1756f1 | |||
| 95659db00a | |||
| bddbc37e8e | |||
| 7a2b295945 | |||
| af8429ebf9 | |||
| 3ac6677d2d | |||
| 8b5744b7eb | |||
| 7fcfc05205 | |||
| 4b42bbda7e | |||
| ed41d93de5 | |||
| d36712d014 | |||
| 21cc9d66db | |||
| 4508cfa364 | |||
| 05e84f2a7c | |||
| 184727408a | |||
| e7fbbe2737 | |||
| 9a0c4ce4ce | |||
| 247a7f5d8a | |||
| 745b82a603 | |||
| 504bee2d2a | |||
| 8285f2a3de | |||
| cfa97f7a9a | |||
| cbf42469c3 | |||
| 61f793040e | |||
| 8dd66211b7 | |||
| 5a24619560 | |||
| d466595828 | |||
| b0b965db98 | |||
| 9e73979b5b | |||
| f4395abade | |||
| a194e2293e | |||
| d27577d2b7 | |||
| 1113c2a849 | |||
| 587197e934 | |||
| ef901f64af | |||
| 2ba05fb7b1 | |||
| a263851614 | |||
| eeb3a3fa65 | |||
| 56f8ca0982 | |||
| 8f94149627 | |||
| ed842fcc1a |
@@ -1,4 +1,8 @@
|
||||
*.pyc
|
||||
.*.
|
||||
confluent_client/man/man*
|
||||
confluent_client/doc/man
|
||||
.vscode
|
||||
.*.sw*
|
||||
.sw*
|
||||
.idea/*
|
||||
|
||||
@@ -373,7 +373,7 @@ def do_command(command, server):
|
||||
if argv[0] == 'exit':
|
||||
if os.environ.get('TERM', '') not in ('linux'):
|
||||
sys.stdout.write('\x1b]0;\x07')
|
||||
raise Bailout()
|
||||
raise BailOut()
|
||||
elif argv[0] in ('help', '?'):
|
||||
return print_help()
|
||||
elif argv[0] == 'cd':
|
||||
@@ -1020,6 +1020,13 @@ if __name__ == '__main__':
|
||||
main()
|
||||
except BailOut as e:
|
||||
errcode = e.errorcode
|
||||
except Exception as e:
|
||||
import traceback
|
||||
try:
|
||||
quitconfetty()
|
||||
except Exception:
|
||||
pass
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
if deadline and os.times()[4] < deadline:
|
||||
sys.stderr.write('[Exited early, hit enter to continue]')
|
||||
|
||||
@@ -8,7 +8,7 @@ import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
def create_image(directory, image):
|
||||
def create_image(directory, image, label=None):
|
||||
ents = 0
|
||||
datasz = 512
|
||||
for dir in os.walk(sys.argv[1]):
|
||||
@@ -25,8 +25,13 @@ def create_image(directory, image):
|
||||
with open(image, 'wb') as imgfile:
|
||||
imgfile.seek(datasz * 512 - 1)
|
||||
imgfile.write(b'\x00')
|
||||
subprocess.check_call(['mformat', '-i', image, '-r', '16', '-d', '1', '-t',
|
||||
str(datasz), '-s', '1','-h', '1', '::'])
|
||||
if label:
|
||||
subprocess.check_call(['mformat', '-i', image, '-v', label,
|
||||
'-r', '16', '-d', '1', '-t', str(datasz),
|
||||
'-s', '1','-h', '1', '::'])
|
||||
else:
|
||||
subprocess.check_call(['mformat', '-i', image, '-r', '16', '-d', '1', '-t',
|
||||
str(datasz), '-s', '1','-h', '1', '::'])
|
||||
# Some clustered filesystems will have the lock from mformat
|
||||
# linger after close (mformat doesn't unlock)
|
||||
# do a blocking wait for shared lock and then explicitly
|
||||
@@ -50,4 +55,7 @@ if __name__ == '__main__':
|
||||
sys.stderr.write("Usage: {0} <directory> <imagefile>".format(
|
||||
sys.argv[0]))
|
||||
sys.exit(1)
|
||||
create_image(sys.argv[1], sys.argv[2])
|
||||
label = None
|
||||
if len(sys.argv) > 3:
|
||||
label = sys.argv[3]
|
||||
create_image(sys.argv[1], sys.argv[2], label)
|
||||
@@ -134,9 +134,10 @@ def _assign_value():
|
||||
assignment[key] = value
|
||||
|
||||
|
||||
def parse_config_line(arguments):
|
||||
def parse_config_line(arguments, single=False):
|
||||
global setmode, printallbmc, forceset, key, value, needval, candidate, path, attrib
|
||||
for param in arguments:
|
||||
for pidx in range(0, len(arguments)):
|
||||
param = arguments[pidx]
|
||||
if param == 'show':
|
||||
continue # forgive muscle memory of pasu users
|
||||
if param == 'set':
|
||||
@@ -146,7 +147,12 @@ def parse_config_line(arguments):
|
||||
if needval:
|
||||
key = needval
|
||||
needval = None
|
||||
value = param
|
||||
if single:
|
||||
value = ' '.join(arguments[pidx:])
|
||||
_assign_value()
|
||||
break
|
||||
else:
|
||||
value = param
|
||||
_assign_value()
|
||||
continue
|
||||
if '=' in param or param[-1] == ':' or forceset:
|
||||
@@ -215,7 +221,7 @@ if options.batch:
|
||||
pass
|
||||
argset = argset.strip()
|
||||
if argset:
|
||||
parse_config_line(shlex.split(argset))
|
||||
parse_config_line(shlex.split(argset), single=True)
|
||||
argset = argfile.readline()
|
||||
else:
|
||||
parse_config_line(args[1:])
|
||||
|
||||
@@ -79,7 +79,7 @@ def main(args):
|
||||
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:
|
||||
sys.stderr.write('profile is a required argument to request a network deployment\n')
|
||||
sys.stderr.write('Both noderange and a profile name are required arguments to request a network deployment\n')
|
||||
return 1
|
||||
if extra:
|
||||
sys.stderr.write('Unrecognized arguments: ' + repr(extra) + '\n')
|
||||
|
||||
@@ -22,13 +22,17 @@ import io
|
||||
import numpy as np
|
||||
|
||||
import os
|
||||
import sixel
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
class DumbWriter(sixel.SixelWriter):
|
||||
def restore_position(self, output):
|
||||
return
|
||||
try:
|
||||
import sixel
|
||||
|
||||
class DumbWriter(sixel.SixelWriter):
|
||||
def restore_position(self, output):
|
||||
return
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def plot(gui, output, plotdata, bins):
|
||||
|
||||
@@ -111,6 +111,12 @@ def printerror(res, node=None):
|
||||
exitcode = 0
|
||||
if 'errorcode' in res:
|
||||
exitcode = res['errorcode']
|
||||
for node in res.get('databynode', {}):
|
||||
exitcode = res['databynode'][node].get('errorcode', exitcode)
|
||||
if 'error' in res['databynode'][node]:
|
||||
sys.stderr.write('{0}: {1}\n'.format(node, res['databynode'][node]['error']))
|
||||
if exitcode == 0:
|
||||
exitcode = 1
|
||||
if 'error' in res:
|
||||
if node:
|
||||
sys.stderr.write('{0}: {1}\n'.format(node, res['error']))
|
||||
|
||||
@@ -10,13 +10,32 @@ import ssl
|
||||
import sys
|
||||
|
||||
def get_apikey(nodename, mgr):
|
||||
sealnew = True
|
||||
if os.path.exists('/etc/confluent/confluent.apikey'):
|
||||
return open('/etc/confluent/confluent.apikey').read().strip()
|
||||
apikey = subprocess.check_output(['/opt/confluent/bin/clortho', nodename, mgr])
|
||||
if not isinstance(apikey, str):
|
||||
apikey = apikey.decode('utf8')
|
||||
if apikey.startswith('SEALED:'):
|
||||
sealnew = False
|
||||
with open('/etc/confluent/confluent.sealedapikey', 'w+') as apiout:
|
||||
apiout.write(apikey[7:])
|
||||
with open('/etc/confluent/confluent.sealedapikey') as inp:
|
||||
sp = subprocess.Popen(['/usr/bin/clevis-decrypt-tpm2'],
|
||||
stdin=inp, stdout=subprocess.PIPE)
|
||||
apikey = sp.communicate()[0]
|
||||
if not isinstance(apikey, str):
|
||||
apikey = apikey.decode('utf8')
|
||||
with open('/etc/confluent/confluent.apikey', 'w+') as apiout:
|
||||
apiout.write(apikey)
|
||||
if sealnew and os.path.exists('/usr/bin/clevis-encrypt-tpm2'):
|
||||
try:
|
||||
with open('/etc/confluent/confluent.apikey') as apin:
|
||||
sealed = subprocess.check_output(
|
||||
['/usr/bin/clevis-encrypt-tpm2', '{}'], stdin=apin)
|
||||
print(HTTPSClient().grab_url('/confluent-api/self/saveapikey', sealed).decode())
|
||||
except Exception:
|
||||
sys.stderr.write('Unable to persist API key through TPM2 sealing\n')
|
||||
apikey = apikey.strip()
|
||||
os.chmod('/etc/confluent/confluent.apikey', 0o600)
|
||||
return apikey
|
||||
@@ -47,6 +66,15 @@ class HTTPSClient(client.HTTPConnection, object):
|
||||
ifout.write(ifidx)
|
||||
if json:
|
||||
self.stdheaders['ACCEPT'] = 'application/json'
|
||||
try:
|
||||
info = open('/etc/confluent/confluent.deploycfg').read().split('\n')
|
||||
except Exception:
|
||||
info = None
|
||||
if info:
|
||||
for line in info:
|
||||
if line.startswith('deploy_server: '):
|
||||
host = line.split(': ', 1)[1]
|
||||
break
|
||||
self.stdheaders['CONFLUENT_APIKEY'] = get_apikey(node, host)
|
||||
if mgtiface:
|
||||
self.stdheaders['CONFLUENT_MGTIFACE'] = mgtiface
|
||||
@@ -104,4 +132,4 @@ if __name__ == '__main__':
|
||||
sys.exit(0)
|
||||
if os.path.exists(sys.argv[-1]):
|
||||
data = open(sys.argv[-1]).read()
|
||||
print(HTTPSClient(json=json).grab_url(sys.argv[1], data).decode())
|
||||
sys.stdout.write(HTTPSClient(json=json).grab_url(sys.argv[1], data).decode())
|
||||
|
||||
@@ -53,6 +53,7 @@ for os in rhvh4 el7 el8 genesis suse15 ubuntu20.04 esxi6 esxi7; do
|
||||
mkdir -p %{buildroot}/opt/confluent/lib/osdeploy/$os/profiles
|
||||
cp ${os}out/addons.* %{buildroot}/opt/confluent/lib/osdeploy/$os/initramfs
|
||||
cp -a $os/profiles/* %{buildroot}/opt/confluent/lib/osdeploy/$os/profiles
|
||||
find %{buildroot}/opt/confluent/lib/osdeploy/$os/profiles -name .gitignore -exec rm -f {} +
|
||||
done
|
||||
|
||||
%files
|
||||
|
||||
@@ -36,6 +36,8 @@ reboot
|
||||
chrony
|
||||
rsync
|
||||
python
|
||||
pciutils
|
||||
%include /tmp/addonpackages
|
||||
%end
|
||||
|
||||
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
label: %%DISTRO%% %%VERSION%% %%ARCH%% (Default Profile)
|
||||
kernelargs: quiet
|
||||
#installedargs: example # These arguments would be added to the installed system
|
||||
|
||||
@@ -1,12 +1,37 @@
|
||||
function set_confluent_vars() {
|
||||
if [ -z "$mgr" ]; then
|
||||
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
|
||||
fi
|
||||
if [ -z "$profile" ]; then
|
||||
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
|
||||
fi
|
||||
}
|
||||
|
||||
fetch_remote() {
|
||||
if [ -f /etc/confluent/ca.pem ]; then
|
||||
curlargs=" --cacert /etc/confluent/ca.pem"
|
||||
fi
|
||||
set_confluent_vars
|
||||
mkdir -p $(dirname $1)
|
||||
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
|
||||
if [ $? != 0 ]; then echo $1 failed to download; return 1; fi
|
||||
}
|
||||
|
||||
run_remote() {
|
||||
requestedcmd="'$*'"
|
||||
curlargs=""
|
||||
if [ -f /etc/confluent/ca.pem ]; then
|
||||
curlargs=" --cacert /etc/confluent/ca.pem"
|
||||
fi
|
||||
set_confluent_vars
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$mgr/confluent-public/os/$profile/scripts/
|
||||
tmpdir=$(mktemp -d)
|
||||
echo Executing in $tmpdir
|
||||
cd $tmpdir
|
||||
curl -f -sS https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
|
||||
mkdir -p $(dirname $1)
|
||||
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
|
||||
if [ $? != 0 ]; then echo $requestedcmd failed to download; return 1; fi
|
||||
chmod +x $1
|
||||
cmd=$1
|
||||
@@ -23,14 +48,23 @@ run_remote() {
|
||||
|
||||
run_remote_python() {
|
||||
echo
|
||||
set_confluent_vars
|
||||
if [ -f /etc/confluent/ca.pem ]; then
|
||||
curlargs=" --cacert /etc/confluent/ca.pem"
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$mgr/confluent-public/os/$profile/scripts/
|
||||
tmpdir=$(mktemp -d)
|
||||
echo Executing in $tmpdir
|
||||
cd $tmpdir
|
||||
curl -f -sS https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
|
||||
mkdir -p $(dirname $1)
|
||||
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
|
||||
if [ $? != 0 ]; then echo "'$*'" failed to download; return 1; fi
|
||||
/usr/libexec/platform-python $*
|
||||
if [ -x /usr/libexec/platform-python ]; then
|
||||
/usr/libexec/platform-python $*
|
||||
else
|
||||
/usr/bin/python $*
|
||||
fi
|
||||
retcode=$?
|
||||
echo "'$*' exited with code $retcode"
|
||||
cd - > /dev/null
|
||||
|
||||
@@ -49,7 +49,7 @@ class DiskInfo(object):
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
if self.model.lower() in ('thinksystem_m.2_vd', 'thinksystem m.2'):
|
||||
if self.model.lower() in ('thinksystem_m.2_vd', 'thinksystem m.2', 'thinksystem_m.2'):
|
||||
return 0
|
||||
if 'imsm' in self.mdcontainer:
|
||||
return 1
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# To use this script, rename or copy the mofed image to either ofed.tgz or ofed.iso
|
||||
# and modify the script below if wanting to use the iso instead of tgz
|
||||
|
||||
# It checks for mellanox devices and opts not to install, so this script could be added
|
||||
# to a general profile without causing mofed to install on non-mellanox systems
|
||||
. /etc/confluent/functions
|
||||
if lspci -d 15b3:: -n |grep 15b3 > /dev/null; then
|
||||
# Uncomment the following three lines and comment out the next
|
||||
# two lines to use the .iso instead of the tgz packaging
|
||||
#fetch_remote infiniband/mofed.iso
|
||||
#mkdir MLNX_OFED
|
||||
#mount -o loop ofed.iso MLNX_OFED
|
||||
fetch_remote infiniband/mofed.tgz
|
||||
tar xf mofed.tgz
|
||||
# The rest is common between tar and iso
|
||||
cd MLNX_OFED*
|
||||
mount -o loop ofed
|
||||
./mlnxofedinstall --force
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# Add needed base packages to the install
|
||||
cat << EOF >> /tmp/addonpackages
|
||||
perl
|
||||
pkgconf-pkg-config
|
||||
tcsh
|
||||
lsof
|
||||
tk
|
||||
gcc-gfortran
|
||||
tcl
|
||||
EOF
|
||||
@@ -2,3 +2,6 @@
|
||||
# This is a convenient place to keep customizations separate from modifying the stock scripts
|
||||
# While modification of the stock scripts is fine, it may be easier to rebase to a newer
|
||||
# stock profile if the '.custom' files are used.
|
||||
# An example for installing OFED for infiniband follows (see the file for more detail):
|
||||
#run_remote infiniband/mofed.post
|
||||
|
||||
|
||||
@@ -2,3 +2,13 @@
|
||||
# This is a convenient place to keep customizations separate from modifying the stock scripts
|
||||
# While modification of the stock scripts is fine, it may be easier to rebase to a newer
|
||||
# stock profile if the '.custom' files are used.
|
||||
#
|
||||
#Here is an example to locally configure the platform BMC according
|
||||
#to confluent configuration so that the BMC would be on the correct
|
||||
#network:
|
||||
#run_remote_python configbmc -c
|
||||
|
||||
#Some addons improve efficiency by adding dependencies during install
|
||||
#here is an example for adding OFED install prereqs to the install
|
||||
#run_remote infiniband/mofed.pre
|
||||
|
||||
|
||||
@@ -28,11 +28,19 @@ if [ "$rootpw" = null ]; then
|
||||
else
|
||||
echo "rootpw --iscrypted $rootpw" > /tmp/rootpw
|
||||
fi
|
||||
curl -f https://$mgr/confluent-public/os/$profile/profile.yaml > /tmp/instprofile.yaml
|
||||
blargs=$(grep ^installedargs: /tmp/instprofile.yaml | sed -e 's/#.*//' -e 's/^installedargs: //')
|
||||
if [ ! -z "$blargs" ]; then
|
||||
blargs=' --append="'$blargs'"'
|
||||
fi
|
||||
grubpw=$(grep ^grubpassword /etc/confluent/confluent.deploycfg | awk '{print $2}')
|
||||
if [ "$grubpw" = "null" ]; then
|
||||
touch /tmp/grubpw
|
||||
else
|
||||
echo "bootloader --iscrypted --password=$grubpw" > /tmp/grubpw
|
||||
blargs=" --iscrypted --password=$grubpw $blargs"
|
||||
fi
|
||||
if [ ! -z "$blargs" ]; then
|
||||
echo "bootloader $blargs" > /tmp/grubpw
|
||||
fi
|
||||
ssh-keygen -A
|
||||
for pubkey in /etc/ssh/ssh_host_*_key.pub; do
|
||||
@@ -45,6 +53,7 @@ if [ -f "/run/install/cmdline.d/01-autocons.conf" ]; then
|
||||
consoledev=$(cat /run/install/cmdline.d/01-autocons.conf | sed -e 's!console=!/dev/!' -e 's/,.*//')
|
||||
TMUX= tmux a <> $consoledev >&0 2>&1 &
|
||||
fi
|
||||
touch /tmp/addonpackages
|
||||
cryptboot=$(grep ^encryptboot: /etc/confluent/confluent.deploycfg | awk '{print $2}')
|
||||
LUKSPARTY=''
|
||||
if [ "$cryptboot" == "tpm2" ]; then
|
||||
|
||||
@@ -7,6 +7,13 @@ if [ -f /tmp/dd_disk ]; then
|
||||
fi
|
||||
done
|
||||
fi
|
||||
vlaninfo=$(getarg vlan)
|
||||
if [ ! -z "$vlaninfo" ]; then
|
||||
vldev=${vlaninfo#*:}
|
||||
vlid=${vlaninfo#*.}
|
||||
vlid=${vlid%:*}
|
||||
ip link add link $vldev name $vldev.$vlid type vlan id $vlid
|
||||
fi
|
||||
TRIES=0
|
||||
oum=$(umask)
|
||||
umask 0077
|
||||
@@ -35,6 +42,7 @@ cat /tls/*.pem > /etc/confluent/ca.pem
|
||||
ifidx=$(cat /tmp/confluent.ifidx)
|
||||
ifname=$(ip link |grep ^$ifidx:|awk '{print $2}')
|
||||
ifname=${ifname%:}
|
||||
ifname=${ifname%@*}
|
||||
echo $ifname > /tmp/net.ifaces
|
||||
|
||||
dnsdomain=$(grep ^dnsdomain: /etc/confluent/confluent.deploycfg)
|
||||
@@ -98,4 +106,8 @@ while read -r entry; do
|
||||
continue
|
||||
fi
|
||||
done < /etc/confluent/confluent.deploycfg
|
||||
if [ -e /lib/nm-lib.sh ]; then
|
||||
. /lib/nm-lib.sh
|
||||
nm_generate_connections
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
sed -i 's/centos/CentOS/; s/rhel/Red Hat Enterprise Linux/' $2/profile.yaml
|
||||
sed -i 's/centos/CentOS/; s/rhel/Red Hat Enterprise Linux/; s/oraclelinux/Oracle Linux/' $2/profile.yaml
|
||||
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 && \
|
||||
|
||||
@@ -34,9 +34,13 @@ reboot
|
||||
|
||||
%packages
|
||||
@^minimal-environment
|
||||
#-kernel-uek # This can opt out of the UEK for the relevant distribution
|
||||
chrony
|
||||
rsync
|
||||
python3
|
||||
tar
|
||||
pciutils
|
||||
%include /tmp/addonpackages
|
||||
%include /tmp/cryptpkglist
|
||||
%end
|
||||
|
||||
@@ -63,15 +67,16 @@ curl -f https://$mgr/confluent-public/os/$profile/scripts/prechroot.sh > /tmp/po
|
||||
|
||||
# Hook firstboot.sh
|
||||
curl -f https://$mgr/confluent-public/os/$profile/scripts/firstboot.service > /mnt/sysimage/etc/systemd/system/firstboot.service
|
||||
curl -f https://$mgr/confluent-public/os/$profile/scripts/firstboot.sh > /mnt/sysimage/etc/confluent/firstboot.sh
|
||||
chmod +x /mnt/sysimage/etc/confluent/firstboot.sh
|
||||
mkdir -p /mnt/sysimage/opt/confluent/bin
|
||||
curl -f https://$mgr/confluent-public/os/$profile/scripts/firstboot.sh > /mnt/sysimage/opt/confluent/bin/firstboot.sh
|
||||
chmod +x /mnt/sysimage/opt/confluent/bin/firstboot.sh
|
||||
%end
|
||||
|
||||
%post
|
||||
cat /etc/confluent/tls/*.pem >> /etc/pki/tls/certs/ca-bundle.crt
|
||||
systemctl enable firstboot
|
||||
chgrp ssh_keys /etc/ssh/ssh*key
|
||||
restorecon /etc/ssh/ssh*key /root/.shosts /etc/ssh/shosts.equiv /etc/ssh/ssh_config.d/* /etc/confluent/firstboot.sh
|
||||
restorecon /etc/ssh/ssh*key /root/.shosts /etc/ssh/shosts.equiv /etc/ssh/ssh_config.d/* /opt/confluent/bin/firstboot.sh
|
||||
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}')
|
||||
mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg |awk '{print $2}')
|
||||
curl -f https://$mgr/confluent-public/os/$profile/scripts/post.sh > /tmp/postinst.sh
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
label: %%DISTRO%% %%VERSION%% %%ARCH%% (Default Profile)
|
||||
kernelargs: quiet
|
||||
kernelargs: quiet # These arguments are passed to the installer
|
||||
#installedargs: example # These arguments would be added to the installed system
|
||||
|
||||
@@ -111,6 +111,14 @@ class Session(object):
|
||||
self.databuffer.raw[1:self.rsp.msg.data_len])}
|
||||
return response
|
||||
|
||||
def await_config(s, bmccfg, channel):
|
||||
vlan = bmccfg.get('bmcvlan', None)
|
||||
ipv4 = bmccfg.get('bmcipv4', None)
|
||||
prefix = bmccfg.get('prefixv4', None)
|
||||
gw = bmccfg.get('bmcgw', None)
|
||||
|
||||
|
||||
|
||||
def raw_command(self,
|
||||
netfn,
|
||||
command,
|
||||
@@ -217,8 +225,7 @@ def set_port_xcc(s, port, model):
|
||||
sys.stdout.write('Complete\n')
|
||||
|
||||
|
||||
def set_vlan(s, vlan, channel):
|
||||
ovlan = vlan
|
||||
def check_vlan(s, vlan, channel):
|
||||
if vlan == 'off':
|
||||
vlan = b'\x00\x00'
|
||||
else:
|
||||
@@ -229,7 +236,19 @@ def set_vlan(s, vlan, channel):
|
||||
currvlan = bytes(s.raw_command(0xc, 2, bytearray([channel, 0x14 ,0, 0]))['data'][1:])
|
||||
if bytearray(currvlan)[1] & 0b10000000 == 0:
|
||||
currvlan = b'\x00\x00'
|
||||
if currvlan == vlan:
|
||||
return currvlan == vlan
|
||||
|
||||
|
||||
def set_vlan(s, vlan, channel):
|
||||
ovlan = vlan
|
||||
if vlan == 'off':
|
||||
vlan = b'\x00\x00'
|
||||
else:
|
||||
vlan = int(vlan)
|
||||
if vlan:
|
||||
vlan = vlan | 32768
|
||||
vlan = struct.pack('<H', vlan)
|
||||
if check_vlan(s, ovlan, channel):
|
||||
sys.stdout.write('VLAN already configured to "{0}"\n'.format(ovlan))
|
||||
return False
|
||||
rsp = s.raw_command(0xc, 1, bytearray([channel, 0x14]) + vlan)
|
||||
@@ -237,7 +256,7 @@ def set_vlan(s, vlan, channel):
|
||||
print('VLAN configured to "{}"'.format(ovlan))
|
||||
else:
|
||||
print('Error setting vlan: ' + repr(rsp))
|
||||
return
|
||||
return True
|
||||
|
||||
|
||||
def get_lan_channel(s):
|
||||
@@ -253,13 +272,18 @@ def get_lan_channel(s):
|
||||
return chan
|
||||
return 1
|
||||
|
||||
|
||||
def check_ipv4(s, ipaddr, channel):
|
||||
ipaddr = bytearray(socket.inet_aton(ipaddr))
|
||||
rsp = s.raw_command(0xc, 2, bytearray([channel, 3, 0, 0]))['data'][-4:]
|
||||
return rsp == ipaddr
|
||||
|
||||
def set_ipv4(s, ipaddr, channel):
|
||||
oipaddr = ipaddr
|
||||
ipaddr = bytearray(socket.inet_aton(ipaddr))
|
||||
rsp = s.raw_command(0xc, 2, bytearray([channel, 3, 0, 0]))['data'][-4:]
|
||||
if rsp == ipaddr:
|
||||
if check_ipv4(s, oipaddr, channel):
|
||||
print('IP Address already set to {}'.format(oipaddr))
|
||||
return
|
||||
return False
|
||||
rsp = int(s.raw_command(0xc, 2, bytearray([channel, 4, 0, 0]))['data'][1]) & 0b1111
|
||||
if rsp != 1:
|
||||
sys.stdout.write("Changing configuration to static...")
|
||||
@@ -276,29 +300,47 @@ def set_ipv4(s, ipaddr, channel):
|
||||
sys.stdout.flush()
|
||||
print('Setting IP to {}'.format(oipaddr))
|
||||
s.raw_command(0xc, 1, bytearray([channel, 3]) + ipaddr)
|
||||
return True
|
||||
|
||||
|
||||
def check_subnet(s, prefix, channel):
|
||||
prefix = int(prefix)
|
||||
mask = bytearray(struct.pack('!I', (2**32 - 1) ^ (2**(32 - prefix) - 1)))
|
||||
rsp = s.raw_command(0xc, 2, bytearray([channel, 6, 0, 0]))['data'][-4:]
|
||||
return rsp == mask
|
||||
|
||||
def set_subnet(s, prefix, channel):
|
||||
oprefix = prefix
|
||||
prefix = int(prefix)
|
||||
mask = bytearray(struct.pack('!I', (2**32 - 1) ^ (2**(32 - prefix) - 1)))
|
||||
rsp = s.raw_command(0xc, 2, bytearray([channel, 6, 0, 0]))['data'][-4:]
|
||||
if rsp == mask:
|
||||
if check_subnet(s, prefix, channel):
|
||||
print('Subnet Mask already set to /{}'.format(oprefix))
|
||||
return
|
||||
return False
|
||||
print('Setting subnet mask to /{}'.format(oprefix))
|
||||
s.raw_command(0xc, 1, bytearray([channel, 6]) + mask)
|
||||
return True
|
||||
|
||||
|
||||
def check_gateway(s, gw, channel):
|
||||
gw = bytearray(socket.inet_aton(gw))
|
||||
rsp = s.raw_command(0xc, 2, bytearray([channel, 12, 0, 0]))['data'][-4:]
|
||||
return rsp == gw
|
||||
|
||||
def set_gateway(s, gw, channel):
|
||||
ogw = gw
|
||||
gw = bytearray(socket.inet_aton(gw))
|
||||
rsp = s.raw_command(0xc, 2, bytearray([channel, 12, 0, 0]))['data'][-4:]
|
||||
if rsp == gw:
|
||||
if check_gateway(s, ogw, channel):
|
||||
print('Gateway already set to {}'.format(ogw))
|
||||
return
|
||||
return False
|
||||
print('Setting gateway to {}'.format(ogw))
|
||||
s.raw_command(0xc, 1, bytearray([channel, 12]) + gw)
|
||||
return True
|
||||
|
||||
def dotwait():
|
||||
sys.stdout.write('.')
|
||||
sys.stdout.flush()
|
||||
time.sleep(0.5)
|
||||
|
||||
def main():
|
||||
a = argparse.ArgumentParser(description='Locally configure a BMC device')
|
||||
@@ -341,14 +383,30 @@ def main():
|
||||
channel = set_port(s, bmccfg['bmcport'], vendor, model)
|
||||
else:
|
||||
channel = get_lan_channel(s)
|
||||
awaitvlan = False
|
||||
awaitip = False
|
||||
awaitprefix = False
|
||||
awaitgw = False
|
||||
if bmccfg.get('bmcvlan', None):
|
||||
set_vlan(s, bmccfg['bmcvlan'], channel)
|
||||
awaitvlan = set_vlan(s, bmccfg['bmcvlan'], channel)
|
||||
if bmccfg.get('bmcipv4', None):
|
||||
set_ipv4(s, bmccfg['bmcipv4'], channel)
|
||||
awaitip = set_ipv4(s, bmccfg['bmcipv4'], channel)
|
||||
if bmccfg.get('prefixv4', None):
|
||||
set_subnet(s, bmccfg['prefixv4'], channel)
|
||||
awaitprefix = set_subnet(s, bmccfg['prefixv4'], channel)
|
||||
if bmccfg.get('bmcgw', None):
|
||||
set_gateway(s, bmccfg['bmcgw'], channel)
|
||||
awaitgw = set_gateway(s, bmccfg['bmcgw'], channel)
|
||||
sys.stdout.write('Waiting for changes to take effect...')
|
||||
sys.stdout.flush()
|
||||
while awaitvlan and not check_vlan(s, bmccfg['bmcvlan'], channel):
|
||||
dotwait()
|
||||
while awaitip and not check_ipv4(s, bmccfg['bmcipv4'], channel):
|
||||
dotwait()
|
||||
while awaitprefix and not check_subnet(s, bmccfg['prefixv4'], channel):
|
||||
dotwait()
|
||||
while awaitprefix and not check_gateway(s, bmccfg['bmcgw'], channel):
|
||||
dotwait()
|
||||
sys.stdout.write('done\n')
|
||||
sys.stdout.flush()
|
||||
#await_config(s, bmccfg, channel)
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ Requires=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/etc/confluent/firstboot.sh
|
||||
ExecStart=/opt/confluent/bin/firstboot.sh
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -15,6 +15,8 @@ export nodename mgr profile
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
curl -X POST -d 'status: complete' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" https://$mgr/confluent-api/self/updatestatus
|
||||
|
||||
@@ -1,12 +1,44 @@
|
||||
function set_confluent_vars() {
|
||||
if [ -z "$mgr" ]; then
|
||||
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
|
||||
fi
|
||||
if [ -z "$profile" ]; then
|
||||
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
|
||||
fi
|
||||
}
|
||||
|
||||
fetch_remote() {
|
||||
if [ -f /etc/confluent/ca.pem ]; then
|
||||
curlargs=" --cacert /etc/confluent/ca.pem"
|
||||
fi
|
||||
set_confluent_vars
|
||||
mkdir -p $(dirname $1)
|
||||
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
|
||||
if [ $? != 0 ]; then echo $1 failed to download; return 1; fi
|
||||
}
|
||||
|
||||
run_remote_parts() {
|
||||
scriptlist=$(/usr/libexec/platform-python /etc/confluent/apiclient /confluent-api/self/scriptlist/$1|sed -e 's/^- //')
|
||||
for script in $scriptlist; do
|
||||
run_remote $1.d/$script
|
||||
done
|
||||
}
|
||||
|
||||
run_remote() {
|
||||
requestedcmd="'$*'"
|
||||
curlargs=""
|
||||
if [ -f /etc/confluent/ca.pem ]; then
|
||||
curlargs=" --cacert /etc/confluent/ca.pem"
|
||||
fi
|
||||
set_confluent_vars
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$mgr/confluent-public/os/$profile/scripts/
|
||||
tmpdir=$(mktemp -d)
|
||||
echo Executing in $tmpdir
|
||||
cd $tmpdir
|
||||
curl -f -sS https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
|
||||
mkdir -p $(dirname $1)
|
||||
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
|
||||
if [ $? != 0 ]; then echo $requestedcmd failed to download; return 1; fi
|
||||
chmod +x $1
|
||||
cmd=$1
|
||||
@@ -23,12 +55,17 @@ run_remote() {
|
||||
|
||||
run_remote_python() {
|
||||
echo
|
||||
set_confluent_vars
|
||||
if [ -f /etc/confluent/ca.pem ]; then
|
||||
curlargs=" --cacert /etc/confluent/ca.pem"
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$mgr/confluent-public/os/$profile/scripts/
|
||||
tmpdir=$(mktemp -d)
|
||||
echo Executing in $tmpdir
|
||||
cd $tmpdir
|
||||
curl -f -sS https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
|
||||
mkdir -p $(dirname $1)
|
||||
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
|
||||
if [ $? != 0 ]; then echo "'$*'" failed to download; return 1; fi
|
||||
/usr/libexec/platform-python $*
|
||||
retcode=$?
|
||||
|
||||
@@ -49,7 +49,7 @@ class DiskInfo(object):
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
if self.model.lower() in ('thinksystem_m.2_vd', 'thinksystem m.2'):
|
||||
if self.model.lower() in ('thinksystem_m.2_vd', 'thinksystem m.2', 'thinksystem_m.2'):
|
||||
return 0
|
||||
if 'imsm' in self.mdcontainer:
|
||||
return 1
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# To use this script, rename or copy the mofed image to either ofed.tgz or ofed.iso
|
||||
# and modify the script below if wanting to use the iso instead of tgz
|
||||
|
||||
# It checks for mellanox devices and opts not to install, so this script could be added
|
||||
# to a general profile without causing mofed to install on non-mellanox systems
|
||||
. /etc/confluent/functions
|
||||
if lspci -d 15b3:: -n |grep 15b3 > /dev/null; then
|
||||
# Uncomment the following three lines and comment out the next
|
||||
# two lines to use the .iso instead of the tgz packaging
|
||||
#fetch_remote infiniband/mofed.iso
|
||||
#mkdir MLNX_OFED
|
||||
#mount -o loop ofed.iso MLNX_OFED
|
||||
fetch_remote infiniband/mofed.tgz
|
||||
tar xf mofed.tgz
|
||||
# The rest is common between tar and iso
|
||||
cd MLNX_OFED*
|
||||
mount -o loop ofed
|
||||
./mlnxofedinstall --force
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# Add needed base packages to the install
|
||||
cat << EOF >> /tmp/addonpackages
|
||||
perl
|
||||
pkgconf-pkg-config
|
||||
tcsh
|
||||
lsof
|
||||
tk
|
||||
gcc-gfortran
|
||||
tcl
|
||||
EOF
|
||||
@@ -2,3 +2,6 @@
|
||||
# This is a convenient place to keep customizations separate from modifying the stock scripts
|
||||
# While modification of the stock scripts is fine, it may be easier to rebase to a newer
|
||||
# stock profile if the '.custom' files are used.
|
||||
|
||||
# An example for installing OFED for infiniband follows (see the file for more detail):
|
||||
#run_remote infiniband/mofed.post
|
||||
|
||||
@@ -33,3 +33,6 @@ run_remote_python add_local_repositories
|
||||
# run_remote example.sh
|
||||
# run_remote_python example.py
|
||||
run_remote post.custom
|
||||
|
||||
# Also, scripts may be placed into 'post.d', e.g. post.d/01-runfirst.sh, post.d/02-runsecond.sh
|
||||
run_remote_parts post
|
||||
|
||||
@@ -7,3 +7,7 @@
|
||||
#to confluent configuration so that the BMC would be on the correct
|
||||
#network:
|
||||
#run_remote_python configbmc -c
|
||||
|
||||
#Some addons improve efficiency by adding dependencies during install
|
||||
#here is an example for adding OFED install prereqs to the install
|
||||
#run_remote infiniband/mofed.pre
|
||||
|
||||
@@ -34,11 +34,19 @@ if [ "$rootpw" = null ]; then
|
||||
else
|
||||
echo "rootpw --iscrypted $rootpw" > /tmp/rootpw
|
||||
fi
|
||||
curl -f https://$mgr/confluent-public/os/$profile/profile.yaml > /tmp/instprofile.yaml
|
||||
blargs=$(grep ^installedargs: /tmp/instprofile.yaml | sed -e 's/#.*//' -e 's/^installedargs: //')
|
||||
if [ ! -z "$blargs" ]; then
|
||||
blargs=' --append="'$blargs'"'
|
||||
fi
|
||||
grubpw=$(grep ^grubpassword /etc/confluent/confluent.deploycfg | awk '{print $2}')
|
||||
if [ "$grubpw" = "null" ]; then
|
||||
touch /tmp/grubpw
|
||||
else
|
||||
echo "bootloader --iscrypted --password=$grubpw" > /tmp/grubpw
|
||||
blargs=" --iscrypted --password=$grubpw $blargs"
|
||||
fi
|
||||
if [ ! -z "$blargs" ]; then
|
||||
echo "bootloader $blargs" > /tmp/grubpw
|
||||
fi
|
||||
for pubkey in /etc/ssh/ssh_host*key.pub; do
|
||||
certfile=${pubkey/.pub/-cert.pub}
|
||||
@@ -53,6 +61,7 @@ fi
|
||||
cryptboot=$(grep ^encryptboot: /etc/confluent/confluent.deploycfg | awk '{print $2}')
|
||||
LUKSPARTY=''
|
||||
touch /tmp/cryptpkglist
|
||||
touch /tmp/addonpackages
|
||||
if [ "$cryptboot" == "tpm2" ]; then
|
||||
LUKSPARTY="--encrypted --passphrase=$(cat /etc/confluent/confluent.apikey)"
|
||||
echo $cryptboot >> /tmp/cryptboot
|
||||
|
||||
@@ -15,10 +15,11 @@ mgr=$(grep MANAGER: /etc/confluent/confluent.info|head -n 1|awk '{print $2}')
|
||||
cp /opt/confluent/bin/clortho /clortho
|
||||
/clortho $node $mgr > /etc/confluent/confluent.apikey
|
||||
cat /tls/*.pem > /etc/confluent/ca.pem
|
||||
/opt/confluent/bin/apiclient /confluent-api/self/deploycfg > /etc/confluent/confluent.deploycfg
|
||||
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/^profile: //')
|
||||
/opt/confluent/bin/apiclient /confluent-api/self/deploycfg > /etc/confluent/confluent.deploycfg.new
|
||||
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg.new | sed -e 's/^profile: //')
|
||||
/opt/confluent/bin/apiclient /confluent-public/os/$profile/kickstart > /etc/confluent/ks.cfg
|
||||
/opt/confluent/bin/apiclient /confluent-public/os/$profile/scripts/modinstall > /tmp/modinstall
|
||||
mv /etc/confluent/confluent.deploycfg.new /etc/confluent/confluent.deploycfg
|
||||
export node mgr profile
|
||||
. /tmp/modinstall
|
||||
exec /bin/install
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
label: VMware ESXi %%VERSION%% Hypervisor
|
||||
label: Confluent installation of VMware ESXi %%VERSION%% Hypervisor
|
||||
ostype: esxi
|
||||
kernelargs: runweasel
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/bin/sh
|
||||
mv /etc/confluent/confluent.deploycfg /etc/confluent/confluent.newdeploycfg
|
||||
/opt/confluent/bin/apiclient /confluent-public/os/$profile/scripts/makeksnet >> /tmp/makeksnet
|
||||
mv /etc/confluent/confluent.newdeploycfg /etc/confluent/confluent.deploycfg
|
||||
chmod +x /tmp/makeksnet
|
||||
/tmp/makeksnet > /tmp/ksnet
|
||||
rootpw=$(grep ^rootpassword: /etc/confluent/confluent.deploycfg|sed -e 's/^rootpassword: //')
|
||||
|
||||
@@ -9,8 +9,11 @@ if ! grep console= /proc/cmdline >& /dev/null; then
|
||||
if [ ! -z "$autocons" ]; then
|
||||
echo "Using $(cat /tmp/01-autocons.conf)"
|
||||
tmux a <> $autocons >&0 2>&1 &
|
||||
else
|
||||
tmux a <> /dev/console >&0 2>&1 &
|
||||
fi
|
||||
else
|
||||
tmux a <> /dev/console >&0 2>&1 &
|
||||
tmux a <> /dev/tty1 >&0 2>&1 &
|
||||
fi
|
||||
echo -n "udevd: "
|
||||
@@ -25,6 +28,8 @@ modprobe hfi1
|
||||
modprobe mlx5_ib
|
||||
echo "done"
|
||||
cat > /etc/ssh/sshd_config << EOF
|
||||
Port 22
|
||||
Port 3389
|
||||
PermitRootLogin yes
|
||||
AuthorizedKeysFile .ssh/authorized_keys
|
||||
EOF
|
||||
@@ -78,7 +83,9 @@ for pubkey in /etc/ssh/ssh_host*key.pub; do
|
||||
certfile=${pubkey/.pub/-cert.pub}
|
||||
privfile=${pubkey%.pub}
|
||||
/usr/libexec/platform-python /opt/confluent/bin/apiclient /confluent-api/self/sshcert $pubkey > $certfile
|
||||
echo HostCertificate $certfile >> /etc/ssh/sshd_config
|
||||
if [ -s $certfile ]; then
|
||||
echo HostCertificate $certfile >> /etc/ssh/sshd_config
|
||||
fi
|
||||
echo HostKey $privfile >> /etc/ssh/sshd_config
|
||||
done
|
||||
/usr/sbin/sshd
|
||||
@@ -92,11 +99,3 @@ run_remote onboot.sh
|
||||
while :; do
|
||||
bash
|
||||
done
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cd -
|
||||
|
||||
|
||||
@@ -10,4 +10,7 @@ mkdir -p /var/empty/sshd
|
||||
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
|
||||
tmux new-session sh /opt/confluent/bin/rungenesis
|
||||
tmux new-session -d sh /opt/confluent/bin/rungenesis
|
||||
while :; do
|
||||
sleep 86400
|
||||
done
|
||||
|
||||
@@ -111,6 +111,14 @@ class Session(object):
|
||||
self.databuffer.raw[1:self.rsp.msg.data_len])}
|
||||
return response
|
||||
|
||||
def await_config(s, bmccfg, channel):
|
||||
vlan = bmccfg.get('bmcvlan', None)
|
||||
ipv4 = bmccfg.get('bmcipv4', None)
|
||||
prefix = bmccfg.get('prefixv4', None)
|
||||
gw = bmccfg.get('bmcgw', None)
|
||||
|
||||
|
||||
|
||||
def raw_command(self,
|
||||
netfn,
|
||||
command,
|
||||
@@ -157,6 +165,14 @@ def set_port(s, port, vendor, model):
|
||||
return 1
|
||||
|
||||
|
||||
def get_remote_config_mod(vendor, model):
|
||||
if vendor in ('IBM', 'Lenovo'):
|
||||
if _is_tsm(model):
|
||||
return 'tsm'
|
||||
else:
|
||||
return 'xcc'
|
||||
return None
|
||||
|
||||
def set_port_tsm(s, port, model):
|
||||
oport = port
|
||||
sys.stdout.write('Setting TSM port to "{}"...'.format(oport))
|
||||
@@ -217,8 +233,7 @@ def set_port_xcc(s, port, model):
|
||||
sys.stdout.write('Complete\n')
|
||||
|
||||
|
||||
def set_vlan(s, vlan, channel):
|
||||
ovlan = vlan
|
||||
def check_vlan(s, vlan, channel):
|
||||
if vlan == 'off':
|
||||
vlan = b'\x00\x00'
|
||||
else:
|
||||
@@ -229,7 +244,19 @@ def set_vlan(s, vlan, channel):
|
||||
currvlan = bytes(s.raw_command(0xc, 2, bytearray([channel, 0x14 ,0, 0]))['data'][1:])
|
||||
if bytearray(currvlan)[1] & 0b10000000 == 0:
|
||||
currvlan = b'\x00\x00'
|
||||
if currvlan == vlan:
|
||||
return currvlan == vlan
|
||||
|
||||
|
||||
def set_vlan(s, vlan, channel):
|
||||
ovlan = vlan
|
||||
if vlan == 'off':
|
||||
vlan = b'\x00\x00'
|
||||
else:
|
||||
vlan = int(vlan)
|
||||
if vlan:
|
||||
vlan = vlan | 32768
|
||||
vlan = struct.pack('<H', vlan)
|
||||
if check_vlan(s, ovlan, channel):
|
||||
sys.stdout.write('VLAN already configured to "{0}"\n'.format(ovlan))
|
||||
return False
|
||||
rsp = s.raw_command(0xc, 1, bytearray([channel, 0x14]) + vlan)
|
||||
@@ -237,7 +264,7 @@ def set_vlan(s, vlan, channel):
|
||||
print('VLAN configured to "{}"'.format(ovlan))
|
||||
else:
|
||||
print('Error setting vlan: ' + repr(rsp))
|
||||
return
|
||||
return True
|
||||
|
||||
|
||||
def get_lan_channel(s):
|
||||
@@ -253,13 +280,18 @@ def get_lan_channel(s):
|
||||
return chan
|
||||
return 1
|
||||
|
||||
|
||||
def check_ipv4(s, ipaddr, channel):
|
||||
ipaddr = bytearray(socket.inet_aton(ipaddr))
|
||||
rsp = s.raw_command(0xc, 2, bytearray([channel, 3, 0, 0]))['data'][-4:]
|
||||
return rsp == ipaddr
|
||||
|
||||
def set_ipv4(s, ipaddr, channel):
|
||||
oipaddr = ipaddr
|
||||
ipaddr = bytearray(socket.inet_aton(ipaddr))
|
||||
rsp = s.raw_command(0xc, 2, bytearray([channel, 3, 0, 0]))['data'][-4:]
|
||||
if rsp == ipaddr:
|
||||
if check_ipv4(s, oipaddr, channel):
|
||||
print('IP Address already set to {}'.format(oipaddr))
|
||||
return
|
||||
return False
|
||||
rsp = int(s.raw_command(0xc, 2, bytearray([channel, 4, 0, 0]))['data'][1]) & 0b1111
|
||||
if rsp != 1:
|
||||
sys.stdout.write("Changing configuration to static...")
|
||||
@@ -276,29 +308,47 @@ def set_ipv4(s, ipaddr, channel):
|
||||
sys.stdout.flush()
|
||||
print('Setting IP to {}'.format(oipaddr))
|
||||
s.raw_command(0xc, 1, bytearray([channel, 3]) + ipaddr)
|
||||
return True
|
||||
|
||||
|
||||
def check_subnet(s, prefix, channel):
|
||||
prefix = int(prefix)
|
||||
mask = bytearray(struct.pack('!I', (2**32 - 1) ^ (2**(32 - prefix) - 1)))
|
||||
rsp = s.raw_command(0xc, 2, bytearray([channel, 6, 0, 0]))['data'][-4:]
|
||||
return rsp == mask
|
||||
|
||||
def set_subnet(s, prefix, channel):
|
||||
oprefix = prefix
|
||||
prefix = int(prefix)
|
||||
mask = bytearray(struct.pack('!I', (2**32 - 1) ^ (2**(32 - prefix) - 1)))
|
||||
rsp = s.raw_command(0xc, 2, bytearray([channel, 6, 0, 0]))['data'][-4:]
|
||||
if rsp == mask:
|
||||
if check_subnet(s, prefix, channel):
|
||||
print('Subnet Mask already set to /{}'.format(oprefix))
|
||||
return
|
||||
return False
|
||||
print('Setting subnet mask to /{}'.format(oprefix))
|
||||
s.raw_command(0xc, 1, bytearray([channel, 6]) + mask)
|
||||
return True
|
||||
|
||||
|
||||
def check_gateway(s, gw, channel):
|
||||
gw = bytearray(socket.inet_aton(gw))
|
||||
rsp = s.raw_command(0xc, 2, bytearray([channel, 12, 0, 0]))['data'][-4:]
|
||||
return rsp == gw
|
||||
|
||||
def set_gateway(s, gw, channel):
|
||||
ogw = gw
|
||||
gw = bytearray(socket.inet_aton(gw))
|
||||
rsp = s.raw_command(0xc, 2, bytearray([channel, 12, 0, 0]))['data'][-4:]
|
||||
if rsp == gw:
|
||||
if check_gateway(s, ogw, channel):
|
||||
print('Gateway already set to {}'.format(ogw))
|
||||
return
|
||||
return False
|
||||
print('Setting gateway to {}'.format(ogw))
|
||||
s.raw_command(0xc, 1, bytearray([channel, 12]) + gw)
|
||||
return True
|
||||
|
||||
def dotwait():
|
||||
sys.stdout.write('.')
|
||||
sys.stdout.flush()
|
||||
time.sleep(0.5)
|
||||
|
||||
def main():
|
||||
a = argparse.ArgumentParser(description='Locally configure a BMC device')
|
||||
@@ -341,15 +391,40 @@ def main():
|
||||
channel = set_port(s, bmccfg['bmcport'], vendor, model)
|
||||
else:
|
||||
channel = get_lan_channel(s)
|
||||
awaitvlan = False
|
||||
awaitip = False
|
||||
awaitprefix = False
|
||||
awaitgw = False
|
||||
if bmccfg.get('bmcvlan', None):
|
||||
set_vlan(s, bmccfg['bmcvlan'], channel)
|
||||
awaitvlan = set_vlan(s, bmccfg['bmcvlan'], channel)
|
||||
if bmccfg.get('bmcipv4', None):
|
||||
set_ipv4(s, bmccfg['bmcipv4'], channel)
|
||||
awaitip = set_ipv4(s, bmccfg['bmcipv4'], channel)
|
||||
if bmccfg.get('prefixv4', None):
|
||||
set_subnet(s, bmccfg['prefixv4'], channel)
|
||||
awaitprefix = set_subnet(s, bmccfg['prefixv4'], channel)
|
||||
if bmccfg.get('bmcgw', None):
|
||||
set_gateway(s, bmccfg['bmcgw'], channel)
|
||||
#await_config(s, bmccfg, channel)
|
||||
awaitgw = set_gateway(s, bmccfg['bmcgw'], channel)
|
||||
sys.stdout.write('Waiting for changes to take effect...')
|
||||
sys.stdout.flush()
|
||||
while awaitvlan and not check_vlan(s, bmccfg['bmcvlan'], channel):
|
||||
dotwait()
|
||||
while awaitip and not check_ipv4(s, bmccfg['bmcipv4'], channel):
|
||||
dotwait()
|
||||
while awaitprefix and not check_subnet(s, bmccfg['prefixv4'], channel):
|
||||
dotwait()
|
||||
while awaitprefix and not check_gateway(s, bmccfg['bmcgw'], channel):
|
||||
dotwait()
|
||||
sys.stdout.write('done\n')
|
||||
sys.stdout.flush()
|
||||
cfgmod = get_remote_config_mod(vendor, model)
|
||||
if cfgmod:
|
||||
with open('configbmc.configmod', 'w+') as cm:
|
||||
cm.write('configmod: {0}\n'.format(cfgmod))
|
||||
sys.stdout.write('Requesting remote configuration of authentication...')
|
||||
sys.stdout.flush()
|
||||
bmccfgsrc = subprocess.check_output(
|
||||
[sys.executable, apiclient, '/confluent-api/self/remoteconfigbmc', 'configbmc.configmod'])
|
||||
sys.stdout.write('done\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -1,12 +1,35 @@
|
||||
function set_confluent_vars() {
|
||||
if [ -z "$mgr" ]; then
|
||||
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
|
||||
fi
|
||||
if [ -z "$profile" ]; then
|
||||
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg | sed -e 's/[^ ]*: //')
|
||||
fi
|
||||
}
|
||||
|
||||
fetch_remote() {
|
||||
if [ -f /etc/confluent/ca.pem ]; then
|
||||
curlargs=" --cacert /etc/confluent/ca.pem"
|
||||
fi
|
||||
set_confluent_vars
|
||||
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
|
||||
if [ $? != 0 ]; then echo $1 failed to download; return 1; fi
|
||||
}
|
||||
|
||||
run_remote() {
|
||||
requestedcmd="'$*'"
|
||||
curlargs=""
|
||||
if [ -f /etc/confluent/ca.pem ]; then
|
||||
curlargs=" --cacert /etc/confluent/ca.pem"
|
||||
fi
|
||||
set_confluent_vars
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$mgr/confluent-public/os/$profile/scripts/
|
||||
tmpdir=$(mktemp -d)
|
||||
echo Executing in $tmpdir
|
||||
cd $tmpdir
|
||||
curl -f -sS https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
|
||||
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
|
||||
if [ $? != 0 ]; then echo $requestedcmd failed to download; return 1; fi
|
||||
chmod +x $1
|
||||
cmd=$1
|
||||
@@ -23,12 +46,16 @@ run_remote() {
|
||||
|
||||
run_remote_python() {
|
||||
echo
|
||||
set_confluent_vars
|
||||
if [ -f /etc/confluent/ca.pem ]; then
|
||||
curlargs=" --cacert /etc/confluent/ca.pem"
|
||||
fi
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$mgr/confluent-public/os/$profile/scripts/
|
||||
tmpdir=$(mktemp -d)
|
||||
echo Executing in $tmpdir
|
||||
cd $tmpdir
|
||||
curl -f -sS https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
|
||||
curl -f -sS $curlargs https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
|
||||
if [ $? != 0 ]; then echo "'$*'" failed to download; return 1; fi
|
||||
/usr/libexec/platform-python $*
|
||||
retcode=$?
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
label: %%DISTRO%% %%VERSION%% %%ARCH%% (Default Profile)
|
||||
kernelargs: quiet
|
||||
#installedargs: example # These arguments would be added to the installed system
|
||||
|
||||
@@ -49,7 +49,7 @@ class DiskInfo(object):
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
if self.model.lower() in ('thinksystem_m.2_vd', 'thinksystem m.2'):
|
||||
if self.model.lower() in ('thinksystem_m.2_vd', 'thinksystem m.2', 'thinksystem_m.2'):
|
||||
return 0
|
||||
if 'imsm' in self.mdcontainer:
|
||||
return 1
|
||||
|
||||
@@ -42,11 +42,19 @@ if [ "$rootpw" = null ]; then
|
||||
else
|
||||
echo "rootpw --iscrypted $rootpw" > /tmp/rootpw
|
||||
fi
|
||||
curl -f https://$mgr/confluent-public/os/$profile/profile.yaml > /tmp/instprofile.yaml
|
||||
blargs=$(grep ^installedargs: /tmp/instprofile.yaml | sed -e 's/#.*//' -e 's/^installedargs: //')
|
||||
if [ ! -z "$blargs" ]; then
|
||||
blargs=' --append="'$blargs'"'
|
||||
fi
|
||||
grubpw=$(grep ^grubpassword /etc/confluent/confluent.deploycfg | awk '{print $2}')
|
||||
if [ "$grubpw" = "null" ]; then
|
||||
touch /tmp/grubpw
|
||||
else
|
||||
echo "bootloader --iscrypted --password=$grubpw" > /tmp/grubpw
|
||||
blargs=" --iscrypted --password=$grubpw $blargs"
|
||||
fi
|
||||
if [ ! -z "$blargs" ]; then
|
||||
echo "bootloader $blargs" > /tmp/grubpw
|
||||
fi
|
||||
ssh-keygen -A
|
||||
for pubkey in /etc/ssh/ssh_host_*_key.pub; do
|
||||
|
||||
@@ -12,7 +12,7 @@ autocons=""
|
||||
if ! grep console /proc/cmdline > /dev/null; then
|
||||
autocons=$(/opt/confluent/bin/autocons)
|
||||
if [ ! -z "$autocons" ]; then
|
||||
echo "Serial console detected from firmmware: $autocons" > ${autocons%,*}
|
||||
echo "Serial console detected from firmware: $autocons" > ${autocons%,*}
|
||||
fi
|
||||
fi
|
||||
mkdir -p /etc/confluent
|
||||
@@ -52,6 +52,9 @@ if [ -z "$mgtiface" ]; then
|
||||
else
|
||||
curl -H "CONFLUENT_MGTIFACE: $mgtiface" -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$mgr/confluent-api/self/deploycfg > /etc/confluent/confluent.deploycfg
|
||||
fi
|
||||
profilename=$(grep ^profile: /etc/confluent/confluent.deploycfg)
|
||||
profilename=${profilename#profile: }
|
||||
curl https://$mgr/confluent-public/os/$profilename/profile.yaml > /tmp/profile.yaml
|
||||
dnsdomain=$(grep ^dnsdomain: /etc/confluent/confluent.deploycfg)
|
||||
dnsdomain=${dnsdomain#dnsdomain: }
|
||||
if [ ! -z "$dnsdomain" ] && [ "$dnsdomain" != "null" ]; then
|
||||
@@ -60,11 +63,15 @@ fi
|
||||
textconsole=$(grep ^textconsole: /etc/confluent/confluent.deploycfg)
|
||||
textconsole=${textconsole#textconsole: }
|
||||
if [ "$textconsole" = "true" ] && ! grep console= /proc/cmdline > /dev/null && [ ! -z "$autocons" ]; then
|
||||
echo "Serial console autodetected and enabled, will be available upon install completion" > ${autocons%,*}
|
||||
echo "The installer will run in text mode on the graphics console. When ssh is available," > ${autocons%,*}
|
||||
echo "install progress may be checked by using ssh to access and run the screendump command" > ${autocons%,*}
|
||||
echo "Install time serial console would require the profile to have console=${autocons#/dev/}" > ${autocons%,*}
|
||||
echo "specified in the kernel command line arguments" > ${autocons%,*}
|
||||
echo "Serial console has been autodected and enabled read-only for install" > ${autocons%,*}
|
||||
echo "It will be read-write after install" > ${autocons%,*}
|
||||
echo "If a fully functional console is desired over serial, add console=${autocons#/dev/} " > ${autocons%,*}
|
||||
echo "to kerneralgs in the profile.yaml file of the profile and run 'osdeploy updateboot <profile>" > ${autocons%,*}
|
||||
#echo "Serial console autodetected and enabled, will be available upon install completion" > ${autocons%,*}
|
||||
#echo "The installer will run in text mode on the graphics console. When ssh is available," > ${autocons%,*}
|
||||
#echo "install progress may be checked by using ssh to access and run the screendump command" > ${autocons%,*}
|
||||
#echo "Install time serial console would require the profile to have console=${autocons#/dev/}" > ${autocons%,*}
|
||||
#echo "specified in the kernel command line arguments" > ${autocons%,*}
|
||||
echo ${autocons%,*} > /tmp/autoconsdev
|
||||
sed -e s'/$/ 'console=${autocons#*/dev/}/ /proc/cmdline > /etc/fakecmdline
|
||||
mount -o bind /etc/fakecmdline /proc/cmdline
|
||||
@@ -98,7 +105,7 @@ else
|
||||
fi
|
||||
fi
|
||||
nameserversec=0
|
||||
if [ ${entry%:*} = "nameservers" ]; then
|
||||
if [ "${entry%:*}" = "nameservers" ]; then
|
||||
nameserversec=1
|
||||
continue
|
||||
fi
|
||||
@@ -107,11 +114,16 @@ fi
|
||||
echo done
|
||||
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg)
|
||||
mgr=${mgr#deploy_server: }
|
||||
profilename=$(grep ^profile: /etc/confluent/confluent.deploycfg)
|
||||
profilename=${profilename#profile: }
|
||||
proto=$(grep ^protocol: /etc/confluent/confluent.deploycfg)
|
||||
proto=${proto#protocol: }
|
||||
|
||||
append=$(grep ^installedargs: /tmp/profile.yaml | sed -e 's/^installedargs: //' -e 's/#.*//')
|
||||
if [ -z "$append" ]; then
|
||||
echo "<bootloader/>" > /tmp/bootloader.xml
|
||||
else
|
||||
echo "<bootloader><global><append>$append</append></global></bootloader>" > /tmp/bootloader.xml
|
||||
fi
|
||||
|
||||
echo "<media_url>${proto}://${mgr}/confluent-public/os/${profilename}/product</media_url>" > /tmp/pkgurl
|
||||
|
||||
echo "AutoYaST: $proto://$mgr/confluent-public/os/$profilename/autoyast" >> /etc/linuxrc.d/01-confluent
|
||||
|
||||
@@ -28,6 +28,7 @@ dynamic behavior and replace with static configuration.
|
||||
</add_on_products>
|
||||
</add-on>
|
||||
%%ENDIFSLE%%
|
||||
<xi:include href="file:///tmp/bootloader.xml"/>
|
||||
<software>
|
||||
%%IFSLE%%
|
||||
<products config:type="list">
|
||||
@@ -126,6 +127,7 @@ curl -f $proto://$mgr/confluent-public/os/$profile/scripts/firstboot.sh > /mnt/e
|
||||
curl -f $proto://$mgr/confluent-public/os/$profile/scripts/post.sh > /mnt/etc/confluent/post.sh
|
||||
chmod +x /mnt/etc/confluent/firstboot.sh
|
||||
chmod +x /mnt/etc/confluent/post.sh
|
||||
cp /mnt/etc/confluent/post.sh /mnt/var/adm/autoinstall/scripts/
|
||||
]]>
|
||||
</source>
|
||||
</script>
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
label: %%DISTRO%% %%VERSION%% %%ARCH%% (Default Profile)
|
||||
kernelargs: quiet
|
||||
kernelargs: quiet # These arguments are passed to the installer
|
||||
#installedargs: example # These arguments would be added to the installed system
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script runs at the end of the final boot
|
||||
|
||||
. /etc/confluent/functions
|
||||
|
||||
# Custom scripts may go here
|
||||
# run_remote example.sh
|
||||
# run_remote_python example.py
|
||||
@@ -7,9 +7,7 @@ mgr=$(grep ^deploy_server /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|sed -e 's/^rootpassword: //')
|
||||
proto=$(grep ^protocol: /etc/confluent/confluent.deploycfg |awk '{print $2}')
|
||||
apikey=$(cat /etc/confluent/confluent.apikey)
|
||||
curl --capath /etc/confluent/tls -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" -f -X POST -d "status: complete" https://$mgr/confluent-api/self/updatestatus
|
||||
. /etc/confluent/functions
|
||||
|
||||
# Custom scripts may go here
|
||||
# run_remote example.sh
|
||||
# run_remote_python example.py
|
||||
run_remote firstboot.custom
|
||||
curl --capath /etc/confluent/tls -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" -f -X POST -d "status: complete" https://$mgr/confluent-api/self/updatestatus
|
||||
|
||||
@@ -49,7 +49,7 @@ class DiskInfo(object):
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
if self.model.lower() in ('thinksystem_m.2_vd', 'thinksystem m.2'):
|
||||
if self.model.lower() in ('thinksystem_m.2_vd', 'thinksystem m.2', 'thinksystem_m.2'):
|
||||
return 0
|
||||
if 'imsm' in self.mdcontainer:
|
||||
return 1
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script runs at the end of install in the installed system
|
||||
# but still under the installer kernel.
|
||||
|
||||
# This is a good place to run most customizations that do not have any
|
||||
# dependency upon the install target kernel being active.
|
||||
|
||||
# If there are dependencies on the kernel (drivers or special filesystems)
|
||||
# then firstboot.sh would be the script to customize.
|
||||
|
||||
. /etc/confluent/functions
|
||||
|
||||
# Examples:
|
||||
# run_remote script.sh
|
||||
# run_remote_python script.py
|
||||
@@ -21,10 +21,7 @@ chmod og-rwx /etc/confluent/*
|
||||
export mgr profile nodename
|
||||
. /etc/confluent/functions
|
||||
|
||||
run_remote post.custom
|
||||
|
||||
curl -X POST -d 'status: staged' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" https://$mgr/confluent-api/self/updatestatus
|
||||
|
||||
# Customizations may go here
|
||||
|
||||
# Examples:
|
||||
# run_remote script.sh
|
||||
# run_remote_python script.py
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
deploycfg=/custom-installation/confluent/confluent.deploycfg
|
||||
mgr=$(grep ^deploy_server $deploycfg|awk '{print $2}')
|
||||
profile=$(grep ^profile: $deploycfg|awk '{print $2}')
|
||||
export deploycfg mgr profile
|
||||
curl -f https://$mgr/confluent-public/os/$profile/scripts/post.sh > /tmp/post.sh
|
||||
. /tmp/post.sh
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
label: %%DISTRO%% %%VERSION%% %%ARCH%% (Default Profile)
|
||||
kernelargs: quiet osprofile=%%PROFILE%%
|
||||
#installedargs: example # These arguments would be added to the installed system
|
||||
|
||||
@@ -49,7 +49,7 @@ class DiskInfo(object):
|
||||
|
||||
@property
|
||||
def priority(self):
|
||||
if self.model.lower() in ('thinksystem_m.2_vd', 'thinksystem m.2'):
|
||||
if self.model.lower() in ('thinksystem_m.2_vd', 'thinksystem m.2', 'thinksystem_m.2'):
|
||||
return 0
|
||||
if 'imsm' in self.mdcontainer:
|
||||
return 1
|
||||
|
||||
@@ -29,6 +29,7 @@ if grep ^ntpservers: /target/etc/confluent/confluent.deploycfg > /dev/null; then
|
||||
sed -i "s/#NTP=/NTP=$ntps/" /target/etc/systemd/timesyncd.conf
|
||||
fi
|
||||
textcons=$(grep ^textconsole: /target/etc/confluent/confluent.deploycfg |awk '{print $2}')
|
||||
updategrub=0
|
||||
if [ "$textcons" = "true" ] && ! grep console= /proc/cmdline > /dev/null; then
|
||||
cons=""
|
||||
if [ -f /custom-installation/autocons.info ]; then
|
||||
@@ -36,11 +37,18 @@ if [ "$textcons" = "true" ] && ! grep console= /proc/cmdline > /dev/null; then
|
||||
fi
|
||||
if [ ! -z "$cons" ]; then
|
||||
sed -i 's/GRUB_CMDLINE_LINUX="\([^"]*\)"/GRUB_CMDLINE_LINUX="\1 console='${cons#/dev/}'"/' /target/etc/default/grub
|
||||
mount -o bind /dev /target/dev
|
||||
mount -o bind /proc /target/proc
|
||||
mount -o bind /sys /target/sys
|
||||
chroot /target update-grub
|
||||
umount /target/sys /target/dev /target/proc
|
||||
updategrub=1
|
||||
fi
|
||||
fi
|
||||
kargs=$(curl https://$mgr/confluent-public/os/$profile/profile.yaml | grep ^installedargs: | sed -e 's/#.*//')
|
||||
if [ ! -z "$kargs" ]; then
|
||||
sed -i 's/GRUB_CMDLINE_LINUX="\([^"]*\)"/GRUB_CMDLINE_LINUX="\1 '"${kargs}"'"/' /target/etc/default/grub
|
||||
fi
|
||||
if [ 1 = $updategrub ]; then
|
||||
mount -o bind /dev /target/dev
|
||||
mount -o bind /proc /target/proc
|
||||
mount -o bind /sys /target/sys
|
||||
chroot /target update-grub
|
||||
umount /target/sys /target/dev /target/proc
|
||||
fi
|
||||
|
||||
|
||||
@@ -36,7 +36,8 @@ unsigned char* genpasswd(int len) {
|
||||
int main(int argc, char* argv[]) {
|
||||
int sock, ret;
|
||||
char slen;
|
||||
unsigned char currlen, currtype;
|
||||
unsigned char currtype;
|
||||
size_t currlen;
|
||||
unsigned char* passwd;
|
||||
unsigned char* cryptedpass;
|
||||
unsigned char* macaddr;
|
||||
@@ -107,10 +108,21 @@ int main(int argc, char* argv[]) {
|
||||
ret = read(sock, buffer, 2);
|
||||
while (buffer[0] != 255) {
|
||||
currtype = buffer[0];
|
||||
currlen = buffer[1];
|
||||
if (currtype & 0b10000000) {
|
||||
currlen = buffer[1] << 8;
|
||||
read(sock, buffer, 1);
|
||||
currlen |= buffer[0];
|
||||
} else {
|
||||
currlen = buffer[1];
|
||||
}
|
||||
memset(buffer, 0, MAXPACKET);
|
||||
if (currlen > 1000) {
|
||||
fprintf(stderr, "Received oversized message\n");
|
||||
exit(1);
|
||||
}
|
||||
if (currlen) {
|
||||
ret = read(sock, buffer, currlen); // Max is 255, well under MAX_PACKET
|
||||
ret = read(sock, buffer, currlen); // Max is 1000, well under MAX_PACKET
|
||||
buffer[currlen] = 0;
|
||||
}
|
||||
if (currtype == 2) {
|
||||
dprintf(sock, "\x03%c", currlen);
|
||||
@@ -118,6 +130,10 @@ int main(int argc, char* argv[]) {
|
||||
slen = strlen(cryptedpass) & 0xff;
|
||||
dprintf(sock, "\x04%c%s", slen, cryptedpass);
|
||||
ret = write(sock, "\x00\x00", 2);
|
||||
} else if (currtype == 128) {
|
||||
printf("SEALED:%s", buffer);
|
||||
printf("\n");
|
||||
exit(0);
|
||||
} else if (currtype == 5) {
|
||||
printf("%s", passwd);
|
||||
printf("\n");
|
||||
|
||||
@@ -19,6 +19,7 @@ import confluent.collective.invites as invites
|
||||
import confluent.config.configmanager as cfm
|
||||
import confluent.exceptions as exc
|
||||
import confluent.log as log
|
||||
import confluent.noderange as noderange
|
||||
import confluent.tlvdata as tlvdata
|
||||
import confluent.util as util
|
||||
import eventlet
|
||||
@@ -27,6 +28,7 @@ import eventlet.green.ssl as ssl
|
||||
import eventlet.green.threading as threading
|
||||
import greenlet
|
||||
import random
|
||||
import time
|
||||
import sys
|
||||
try:
|
||||
import OpenSSL.crypto as crypto
|
||||
@@ -38,6 +40,7 @@ except ImportError:
|
||||
currentleader = None
|
||||
follower = None
|
||||
retrythread = None
|
||||
failovercheck = None
|
||||
|
||||
class ContextBool(object):
|
||||
def __init__(self):
|
||||
@@ -193,15 +196,21 @@ def connect_to_collective(cert, member):
|
||||
raise Exception("Certificate mismatch in the collective")
|
||||
return remote
|
||||
|
||||
|
||||
mycachedname = [None, 0]
|
||||
def get_myname():
|
||||
if mycachedname[1] > time.time() - 15:
|
||||
return mycachedname[0]
|
||||
try:
|
||||
with open('/etc/confluent/cfg/myname', 'r') as f:
|
||||
return f.read().strip()
|
||||
mycachedname[0] = f.read().strip()
|
||||
mycachedname[1] = time.time()
|
||||
return mycachedname[0]
|
||||
except IOError:
|
||||
myname = socket.gethostname()
|
||||
with open('/etc/confluent/cfg/myname', 'w') as f:
|
||||
f.write(myname)
|
||||
mycachedname[0] = myname
|
||||
mycachedname[1] = time.time()
|
||||
return myname
|
||||
|
||||
def handle_connection(connection, cert, request, local=False):
|
||||
@@ -567,6 +576,7 @@ def become_leader(connection):
|
||||
if dronecandidate in skipem or member == myname:
|
||||
continue
|
||||
eventlet.spawn_n(try_assimilate, dronecandidate)
|
||||
schedule_rebalance()
|
||||
|
||||
|
||||
def startup():
|
||||
@@ -576,7 +586,53 @@ def startup():
|
||||
return
|
||||
eventlet.spawn_n(start_collective)
|
||||
|
||||
def check_managers():
|
||||
global failovercheck
|
||||
if not follower:
|
||||
c = cfm.ConfigManager(None)
|
||||
collinfo = {}
|
||||
populate_collinfo(collinfo)
|
||||
availmanagers = {}
|
||||
offlinemgrs = set(collinfo['offline'])
|
||||
offlinemgrs.add('')
|
||||
for offline in collinfo['offline']:
|
||||
nodes = noderange.NodeRange(
|
||||
'collective.manager=={}'.format(offline), c).nodes
|
||||
managercandidates = c.get_node_attributes(
|
||||
nodes, 'collective.managercandidates')
|
||||
expandednoderanges = {}
|
||||
for node in nodes:
|
||||
if node not in managercandidates:
|
||||
continue
|
||||
targets = managercandidates[node].get('collective.managercandidates', {}).get('value', None)
|
||||
if not targets:
|
||||
continue
|
||||
if not availmanagers:
|
||||
for active in collinfo['active']:
|
||||
availmanagers[active] = len(
|
||||
noderange.NodeRange(
|
||||
'collective.manager=={}'.format(active), c).nodes)
|
||||
availmanagers[collinfo['leader']] = len(
|
||||
noderange.NodeRange(
|
||||
'collective.manager=={}'.format(
|
||||
collinfo['leader']), c).nodes)
|
||||
if targets not in expandednoderanges:
|
||||
expandednoderanges[targets] = set(
|
||||
noderange.NodeRange(targets, c).nodes) - offlinemgrs
|
||||
targets = sorted(expandednoderanges[targets], key=availmanagers.get)
|
||||
if not targets:
|
||||
continue
|
||||
c.set_node_attributes({node: {'collective.manager': {'value': targets[0]}}})
|
||||
availmanagers[targets[0]] += 1
|
||||
failovercheck = None
|
||||
|
||||
def schedule_rebalance():
|
||||
global failovercheck
|
||||
if not failovercheck:
|
||||
failovercheck = eventlet.spawn_after(10, check_managers)
|
||||
|
||||
def start_collective():
|
||||
cfm.membership_callback = schedule_rebalance
|
||||
global follower
|
||||
global retrythread
|
||||
if follower:
|
||||
|
||||
@@ -129,6 +129,14 @@ node = {
|
||||
'Generally this is not directly modified, but is modified '
|
||||
'by the "nodedeploy" command'),
|
||||
},
|
||||
'deployment.sealedapikey': {
|
||||
'description': 'This attribute is used by some images to save a sealed '
|
||||
'version of a node apikey, so that a subsequent run with '
|
||||
'same TPM2 will use the TPM2 to protect the API key rather '
|
||||
'than local network verification. If this is set, then '
|
||||
'an api key request will receive this if the api key grant '
|
||||
'is not armed',
|
||||
},
|
||||
#'id': {
|
||||
# 'description': ('Numeric identifier for node')
|
||||
#},
|
||||
@@ -181,6 +189,10 @@ node = {
|
||||
# 'autonode.servername, so that would not need to be '
|
||||
# 'copied ')
|
||||
# },
|
||||
# 'collective.allowedmanagers': {
|
||||
# 'description': ('Restricted set of deployment and managers in automatic selectien
|
||||
# },
|
||||
# ssh.equivnodes - control the list of nodes that go into equiv...
|
||||
'collective.manager': {
|
||||
'description': ('When in collective mode, the member of the '
|
||||
'collective currently considered to be responsible '
|
||||
@@ -189,6 +201,14 @@ node = {
|
||||
'indicates candidate managers, either for '
|
||||
'high availability or load balancing purposes.')
|
||||
},
|
||||
'collective.managercandidates': {
|
||||
'description': ('A noderange of nodes permitted to be a manager for '
|
||||
'the node. This controls failover and deployment. If '
|
||||
'not defined, all managers may deploy and no '
|
||||
'automatic failover will be performed. '
|
||||
'Using this requires that collective members be '
|
||||
'defined as nodes for noderange expansion')
|
||||
},
|
||||
'deployment.pendingprofile': {
|
||||
'description': ('An OS profile that is pending deployment. This indicates to '
|
||||
'the network boot subsystem what should be offered when a potential '
|
||||
@@ -328,7 +348,7 @@ node = {
|
||||
'description': ('Indicate logging level to apply to console. Valid '
|
||||
'values are currently "full", "interactive", and '
|
||||
'"none". Defaults to "full".'),
|
||||
'validvalues': ('full', 'interactive', 'none'),
|
||||
'validvalues': ('full', 'memory', 'interactive', 'none'),
|
||||
},
|
||||
'console.method': {
|
||||
'description': ('Indicate the method used to access the console of '
|
||||
@@ -522,6 +542,13 @@ node = {
|
||||
'description': ('Password to use when connecting to the hardware '
|
||||
'manager'),
|
||||
},
|
||||
'ssh.trustnodes': {
|
||||
'description': ('Nodes that are allowed to ssh into the node, '
|
||||
'expressed in noderange syntax. This is used during '
|
||||
'deployment if the confluent SSH certificate '
|
||||
'authority is configured. Default behavior is for '
|
||||
'all nodes to trust each other.'),
|
||||
},
|
||||
'pubkeys.addpolicy': {
|
||||
'description': ('Policy to use when encountering unknown public '
|
||||
'keys. Choices are "automatic" to accept and '
|
||||
|
||||
@@ -115,6 +115,7 @@ _attraliases = {
|
||||
}
|
||||
_validroles = ('Administrator', 'Operator', 'Monitor')
|
||||
|
||||
membership_callback = None
|
||||
|
||||
def attrib_supports_expression(attrib):
|
||||
if not isinstance(attrib, str):
|
||||
@@ -409,6 +410,8 @@ def _push_rpc(stream, payload):
|
||||
except Exception:
|
||||
logException()
|
||||
del cfgstreams[stream]
|
||||
if membership_callback:
|
||||
membership_callback()
|
||||
stream.close()
|
||||
|
||||
|
||||
@@ -615,6 +618,8 @@ def relay_slaved_requests(name, listener):
|
||||
except Exception:
|
||||
pass
|
||||
del cfgstreams[name]
|
||||
if membership_callback:
|
||||
membership_callback()
|
||||
cfgstreams[name] = listener
|
||||
lh = StreamHandler(listener)
|
||||
_hasquorum = len(cfgstreams) >= (
|
||||
@@ -682,6 +687,8 @@ def relay_slaved_requests(name, listener):
|
||||
_push_rpc,
|
||||
[(cfgstreams[s], payload) for s in cfgstreams]):
|
||||
pass
|
||||
if membership_callback:
|
||||
membership_callback()
|
||||
if not cfgstreams and not cfgleader: # last one out, set cfgleader to boolean to mark dead collective
|
||||
stop_following(True)
|
||||
return False
|
||||
@@ -739,6 +746,8 @@ def stop_leading():
|
||||
del cfgstreams[stream]
|
||||
except KeyError:
|
||||
pass # may have already been deleted..
|
||||
if membership_callback:
|
||||
membership_callback()
|
||||
|
||||
|
||||
_oldcfgstore = None
|
||||
|
||||
@@ -234,7 +234,7 @@ class ConsoleHandler(object):
|
||||
self._isondemand = False
|
||||
else:
|
||||
if (attrvalue[self.node]['console.logging']['value'] not in (
|
||||
'full', '', 'buffer')):
|
||||
'full', '', 'memory')):
|
||||
self._isondemand = True
|
||||
if (attrvalue[self.node]['console.logging']['value']) in ('none', 'memory'):
|
||||
self._dologging = False
|
||||
@@ -609,10 +609,10 @@ class ConsoleHandler(object):
|
||||
if self.shiftin is not None: # detected that terminal requested a
|
||||
# shiftin character set, relay that to the terminal that cannected
|
||||
retdata += b'\x1b)' + self.shiftin
|
||||
if self.appmodedetected:
|
||||
retdata += b'\x1b[?1h'
|
||||
else:
|
||||
retdata += b'\x1b[?1l'
|
||||
#if self.appmodedetected:
|
||||
# retdata += b'\x1b[?1h'
|
||||
#else:
|
||||
# retdata += b'\x1b[?1l'
|
||||
return retdata, connstate
|
||||
|
||||
def write(self, data):
|
||||
|
||||
@@ -22,6 +22,16 @@ import eventlet
|
||||
import eventlet.green.socket as socket
|
||||
import eventlet.greenpool
|
||||
import os
|
||||
import struct
|
||||
|
||||
# cred grant tlvs:
|
||||
# 0, 0 - null
|
||||
# 1, len, <nodename>
|
||||
# 2, len, token - echo request
|
||||
# 3, len, token - echo reply
|
||||
# 4, len, crypted - crypted apikey
|
||||
# 5, 0, accept key
|
||||
# 128, len, len, key - sealed key
|
||||
|
||||
class CredServer(object):
|
||||
def __init__(self):
|
||||
@@ -38,11 +48,20 @@ class CredServer(object):
|
||||
client.close()
|
||||
return
|
||||
nodename = util.stringify(client.recv(tlv[1]))
|
||||
tlv = bytearray(client.recv(2))
|
||||
apiarmed = self.cfm.get_node_attributes(nodename, 'deployment.apiarmed')
|
||||
apiarmed = apiarmed.get(nodename, {}).get('deployment.apiarmed', {}).get(
|
||||
tlv = bytearray(client.recv(2)) # should always be null
|
||||
apimats = self.cfm.get_node_attributes(nodename,
|
||||
['deployment.apiarmed', 'deployment.sealedapikey'])
|
||||
apiarmed = apimats.get(nodename, {}).get('deployment.apiarmed', {}).get(
|
||||
'value', None)
|
||||
if not apiarmed:
|
||||
if apimats.get(nodename, {}).get(
|
||||
'deployment.sealedapikey', {}).get('value', None):
|
||||
sealed = apimats[nodename]['deployment.sealedapikey'][
|
||||
'value']
|
||||
if not isinstance(sealed, bytes):
|
||||
sealed = sealed.encode('utf8')
|
||||
reply = b'\x80' + struct.pack('>H', len(sealed) + 1) + sealed + b'\x00'
|
||||
client.send(reply)
|
||||
client.close()
|
||||
return
|
||||
if apiarmed not in ('once', 'continuous'):
|
||||
|
||||
@@ -230,6 +230,8 @@ def send_discovery_datum(info):
|
||||
yield msg.KeyValueData({'serialnumber': sn})
|
||||
yield msg.KeyValueData({'modelnumber': mn})
|
||||
yield msg.KeyValueData({'uuid': uuid})
|
||||
if 'enclosure.uuid' in info:
|
||||
yield msg.KeyValueData({'enclosure_uuid': info['enclosure.uuid']})
|
||||
if 'enclosure.bay' in info:
|
||||
yield msg.KeyValueData({'bay': int(info['enclosure.bay'])})
|
||||
yield msg.KeyValueData({'macs': [info.get('hwaddr', '')]})
|
||||
@@ -240,6 +242,16 @@ def send_discovery_datum(info):
|
||||
yield msg.KeyValueData({'types': types})
|
||||
if 'otheraddresses' in info:
|
||||
yield msg.KeyValueData({'otheripaddrs': list(info['otheraddresses'])})
|
||||
if 'location' in info:
|
||||
yield msg.KeyValueData({'location': info['location']})
|
||||
if 'room' in info:
|
||||
yield msg.KeyValueData({'room': info['room']})
|
||||
if 'rack' in info:
|
||||
yield msg.KeyValueData({'rack': info['rack']})
|
||||
if 'u' in info:
|
||||
yield msg.KeyValueData({'lowest_u': info['u']})
|
||||
if 'hostname' in info:
|
||||
yield msg.KeyValueData({'hostname': info['hostname']})
|
||||
|
||||
|
||||
def _info_matches(info, criteria):
|
||||
@@ -772,12 +784,14 @@ def get_chained_smm_name(nodename, cfg, handler, nl=None, checkswitch=True):
|
||||
'extend a single enclosure')
|
||||
cd = cfg.get_node_attributes(nodename, ['hardwaremanagement.manager',
|
||||
'pubkeys.tls_hardwaremanager'])
|
||||
smmaddr = cd[nodename]['hardwaremanagement.manager']['value']
|
||||
pkey = cd[nodename].get('pubkeys.tls_hardwaremanager', {}).get(
|
||||
'value', None)
|
||||
if not pkey:
|
||||
# We cannot continue through a break in the chain
|
||||
return None, False
|
||||
smmaddr = cd.get(nodename, {}).get('hardwaremanagement.manager', {}).get('value', None)
|
||||
if not smmaddr:
|
||||
return None, False
|
||||
if pkey:
|
||||
cv = util.TLSCertVerifier(
|
||||
cfg, nodename, 'pubkeys.tls_hardwaremanager').verify_cert
|
||||
@@ -855,6 +869,14 @@ def get_nodename(cfg, handler, info):
|
||||
# while this started by switch, it was disambiguated
|
||||
info['verified'] = v
|
||||
return newnodename, None
|
||||
else:
|
||||
errorstr = ('Attempt to discover SMM in chain but '
|
||||
'unable to follow chain to the specific '
|
||||
'SMM, it may be waiting on an upstream '
|
||||
'SMM, chain starts with {0}'.format(
|
||||
nodename))
|
||||
log.log({'error': errorstr})
|
||||
return None, None
|
||||
if (nodename and
|
||||
not handler.discoverable_by_switch(macinfo['maccount'])):
|
||||
if handler.devname == 'SMM':
|
||||
@@ -1040,6 +1062,20 @@ def eval_node(cfg, handler, info, nodename, manual=False):
|
||||
|
||||
|
||||
def discover_node(cfg, handler, info, nodename, manual):
|
||||
if manual:
|
||||
if not cfg.is_node(nodename):
|
||||
raise exc.InvalidArgumentException(
|
||||
'{0} is not a defined node, must be defined before an '
|
||||
'endpoint may be assigned to it'.format(nodename))
|
||||
if handler.https_supported:
|
||||
currcert = handler.https_cert
|
||||
if currcert:
|
||||
currprint = util.get_fingerprint(currcert, 'sha256')
|
||||
prevnode = nodes_by_fprint.get(currprint, None)
|
||||
if prevnode and prevnode != nodename:
|
||||
raise exc.InvalidArgumentException(
|
||||
'Attempt to assign {0} conflicts with existing node {1} '
|
||||
'based on TLS certificate.'.format(nodename, prevnode))
|
||||
known_nodes[nodename][info['hwaddr']] = info
|
||||
if info['hwaddr'] in unknown_info:
|
||||
del unknown_info[info['hwaddr']]
|
||||
@@ -1129,11 +1165,13 @@ def do_pxe_discovery(cfg, handler, info, manual, nodename, policies):
|
||||
# use uuid based scheme in lieu of tls cert, ideally only
|
||||
# for stateless 'discovery' targets like pxe, where data does not
|
||||
# change
|
||||
uuidinfo = cfg.get_node_attributes(nodename, ['id.uuid', 'id.serial', 'id.model', 'net*.bootable'])
|
||||
uuidinfo = cfg.get_node_attributes(nodename, ['id.uuid', 'id.serial', 'id.model', 'net*.hwaddr', 'net*.bootable'])
|
||||
if manual or policies & set(('open', 'pxe')):
|
||||
enrich_pxe_info(info)
|
||||
attribs = {}
|
||||
olduuid = uuidinfo.get(nodename, {}).get('id.uuid', None)
|
||||
if isinstance(olduuid, dict):
|
||||
olduuid = olduuid.get('value', None)
|
||||
uuid = info.get('uuid', None)
|
||||
if uuid and uuid != olduuid:
|
||||
attribs['id.uuid'] = info['uuid']
|
||||
@@ -1146,7 +1184,9 @@ def do_pxe_discovery(cfg, handler, info, manual, nodename, policies):
|
||||
for attrname in uuidinfo.get(nodename, {}):
|
||||
if attrname.endswith('.bootable') and uuidinfo[nodename][attrname].get('value', None):
|
||||
newattrname = attrname[:-8] + 'hwaddr'
|
||||
attribs[newattrname] = info['hwaddr']
|
||||
oldhwaddr = uuidinfo.get(nodename, {}).get(newattrname, {}).get('value', None)
|
||||
if info['hwaddr'] != oldhwaddr:
|
||||
attribs[newattrname] = info['hwaddr']
|
||||
if attribs:
|
||||
cfg.set_node_attributes({nodename: attribs})
|
||||
if info['uuid'] in known_pxe_uuids:
|
||||
@@ -1278,11 +1318,11 @@ known_pxe_uuids = {}
|
||||
def _map_unique_ids(nodes=None):
|
||||
global nodes_by_uuid
|
||||
global nodes_by_fprint
|
||||
nodes_by_uuid = {}
|
||||
nodes_by_fprint = {}
|
||||
# Map current known ids based on uuid and fingperprints for fast lookup
|
||||
cfg = cfm.ConfigManager(None)
|
||||
if nodes is None:
|
||||
nodes_by_uuid = {}
|
||||
nodes_by_fprint = {}
|
||||
nodes = cfg.list_nodes()
|
||||
bigmap = cfg.get_node_attributes(nodes,
|
||||
('id.uuid',
|
||||
@@ -1304,7 +1344,7 @@ def _map_unique_ids(nodes=None):
|
||||
del nodes_by_uuid[uuid_by_nodes[node]]
|
||||
if node in fprint_by_nodes:
|
||||
del nodes_by_fprint[fprint_by_nodes[node]]
|
||||
uuid = bigmap[node].get('id.uuid', {}).get('value', None)
|
||||
uuid = bigmap[node].get('id.uuid', {}).get('value', '').lower()
|
||||
if uuid_is_valid(uuid):
|
||||
nodes_by_uuid[uuid] = node
|
||||
fprint = bigmap[node].get(
|
||||
|
||||
@@ -47,10 +47,28 @@ class NodeHandler(bmchandler.NodeHandler):
|
||||
self.info['uuid'] = uuidprefix + '-' + '-'.join(
|
||||
wronguuid.split('-')[3:])
|
||||
self.info['uuid'] = self.info['uuid'].lower()
|
||||
room = slpattrs.get('room-id', [None])[0]
|
||||
if room:
|
||||
self.info['room'] = room
|
||||
rack = slpattrs.get('rack-id', [None])[0]
|
||||
if rack:
|
||||
self.info['rack'] = rack
|
||||
name = slpattrs.get('name', [None])[0]
|
||||
if name:
|
||||
self.info['hostname'] = name
|
||||
unumber = slpattrs.get('lowest-u', [None])[0]
|
||||
if unumber:
|
||||
self.info['u'] = unumber
|
||||
location = slpattrs.get('location', [None])[0]
|
||||
if location:
|
||||
self.info['location'] = location
|
||||
if ff not in ('dense-computing', 'BC2'):
|
||||
# do not probe unless it's a dense platform
|
||||
return
|
||||
self.isdense = True
|
||||
encuuid = slpattrs.get('chassis-uuid', [None])[0]
|
||||
if encuuid:
|
||||
self.info['enclosure.uuid'] = encuuid
|
||||
slot = int(slpattrs.get('slot', ['0'])[0])
|
||||
if slot != 0:
|
||||
self.info['enclosure.bay'] = slot
|
||||
|
||||
@@ -41,7 +41,7 @@ def fixuuid(baduuid):
|
||||
class NodeHandler(bmchandler.NodeHandler):
|
||||
is_enclosure = True
|
||||
devname = 'SMM'
|
||||
maxmacs = 6 # support an enclosure, but try to avoid catching daisy chain
|
||||
maxmacs = 14 # support an enclosure, but try to avoid catching daisy chain
|
||||
|
||||
def scan(self):
|
||||
# the UUID is in a weird order, fix it up to match
|
||||
@@ -82,6 +82,14 @@ class NodeHandler(bmchandler.NodeHandler):
|
||||
|
||||
def _webconfignet(self, wc, nodename):
|
||||
cfg = self.configmanager
|
||||
if 'service:lenovo-smm2' in self.info.get('services', []):
|
||||
# need to enable ipmi for now..
|
||||
wc.request('POST', '/data', 'set=DoCmd(0x06,0x40,0x01,0x82,0x84)')
|
||||
rsp = wc.getresponse()
|
||||
rsp.read()
|
||||
wc.request('POST', '/data', 'set=DoCmd(0x06,0x40,0x01,0x42,0x44)')
|
||||
rsp = wc.getresponse()
|
||||
rsp.read()
|
||||
cd = cfg.get_node_attributes(
|
||||
nodename, ['hardwaremanagement.manager'])
|
||||
smmip = cd.get(nodename, {}).get('hardwaremanagement.manager', {}).get('value', None)
|
||||
@@ -196,7 +204,7 @@ class NodeHandler(bmchandler.NodeHandler):
|
||||
raise Exception('Cannot support default password and setting password rules at same time')
|
||||
if passwd == 'PASSW0RD':
|
||||
# We must avoid hitting the web interface due to forced password change, best effert
|
||||
self._bmcconfig(nodename)
|
||||
raise Exception('Using the default password is no longer supported')
|
||||
else:
|
||||
# Switch to full web based configuration, to mitigate risks with the SMM
|
||||
wc = self._webconfigcreds(username, passwd)
|
||||
|
||||
@@ -229,6 +229,19 @@ class NodeHandler(generic.NodeHandler):
|
||||
rsp, status = wc.grab_json_response_with_status('/api/session', method='DELETE')
|
||||
|
||||
|
||||
def remote_nodecfg(nodename, cfm):
|
||||
cfg = cfm.get_node_attributes(
|
||||
nodename, 'hardwaremanagement.manager')
|
||||
ipaddr = cfg.get(nodename, {}).get('hardwaremanagement.manager', {}).get(
|
||||
'value', None)
|
||||
ipaddr = getaddrinfo(ipaddr, 0)[0][-1]
|
||||
if not ipaddr:
|
||||
raise Excecption('Cannot remote configure a system without known '
|
||||
'address')
|
||||
info = {'addresses': [ipaddr]}
|
||||
nh = NodeHandler(info, cfm)
|
||||
nh.config(nodename)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import confluent.config.configmanager as cfm
|
||||
c = cfm.ConfigManager(None)
|
||||
|
||||
@@ -39,6 +39,9 @@ def fixup_uuid(uuidprop):
|
||||
return '-'.join(uuid).upper()
|
||||
|
||||
|
||||
class LockedUserException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class NodeHandler(immhandler.NodeHandler):
|
||||
@@ -66,11 +69,13 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
if ff not in ('dense-computing', [u'dense-computing']):
|
||||
# skip preconfig for non-SD530 servers
|
||||
return
|
||||
currfirm = self.info.get('attributes', {}).get('firmware-image-info', [''])[0]
|
||||
if not currfirm.startswith('TEI'):
|
||||
return
|
||||
self.trieddefault = None # Reset state on a preconfig attempt
|
||||
# attempt to enable SMM
|
||||
#it's normal to get a 'not supported' (193) for systems without an SMM
|
||||
# need to branch on 3.00+ firmware
|
||||
currfirm = self.info.get('attributes', {}).get('firmware-image-info', [''])[0]
|
||||
currfirm = currfirm.split(':')
|
||||
if len(currfirm) > 1:
|
||||
currfirm = float(currfirm[1])
|
||||
@@ -136,8 +141,14 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
'Content-Type': 'application/json'}
|
||||
wc.request('POST', '/api/login', adata, headers)
|
||||
rsp = wc.getresponse()
|
||||
try:
|
||||
rspdata = json.loads(rsp.read())
|
||||
except Exception:
|
||||
rspdata = {}
|
||||
if rsp.status != 200 and password == 'PASSW0RD':
|
||||
rsp.read()
|
||||
if rspdata.get('locktime', 0) > 0:
|
||||
raise LockedUserException(
|
||||
'The user "{0}" has been locked out for too many incorrect password attempts'.format(username))
|
||||
adata = json.dumps({
|
||||
'username': username,
|
||||
'password': newpassword,
|
||||
@@ -146,16 +157,21 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
'Content-Type': 'application/json'}
|
||||
wc.request('POST', '/api/login', adata, headers)
|
||||
rsp = wc.getresponse()
|
||||
try:
|
||||
rspdata = json.loads(rsp.read())
|
||||
except Exception:
|
||||
rspdata = {}
|
||||
if rsp.status == 200:
|
||||
pwdchanged = True
|
||||
password = newpassword
|
||||
else:
|
||||
rsp.read()
|
||||
return (None, None)
|
||||
if rspdata.get('locktime', 0) > 0:
|
||||
raise LockedUserException(
|
||||
'The user "{0}" has been locked out for too many incorrect password attempts'.format(username))
|
||||
return (None, rspdata)
|
||||
if rsp.status == 200:
|
||||
self._currcreds = (username, password)
|
||||
wc.set_basic_credentials(username, password)
|
||||
rspdata = json.loads(rsp.read())
|
||||
wc.set_header('Content-Type', 'application/json')
|
||||
wc.set_header('Authorization', 'Bearer ' + rspdata['access_token'])
|
||||
if '_csrf_token' in wc.cookies:
|
||||
@@ -178,12 +194,16 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
wc = self.wc
|
||||
self.set_password_policy('', wc)
|
||||
return (wc, pwdchanged)
|
||||
return (None, None)
|
||||
elif rspdata.get('locktime', 0) > 0:
|
||||
raise LockedUserException(
|
||||
'The user "{0}" has been locked out by too many incorrect password attempts'.format(username))
|
||||
return (None, rspdata)
|
||||
|
||||
@property
|
||||
def wc(self):
|
||||
passwd = None
|
||||
isdefault = True
|
||||
errinfo = {}
|
||||
if self._wc is None:
|
||||
self._wc = webclient.SecureHTTPConnection(
|
||||
self.ipaddr, 443, verifycallback=self.validate_cert)
|
||||
@@ -205,6 +225,9 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
'secret.hardwaremanagementpassword'], decrypt=True)
|
||||
user, passwd, isdefault = self.get_node_credentials(
|
||||
nodename, creds, 'USERID', 'PASSW0RD')
|
||||
if not inpreconfig and isdefault:
|
||||
raise Exception('Default user/password is not supported. Please set "secret.hardwaremanagementuser" and "secret.hardwaremanagementpassword" for {} to a non-default value. If the XCC is currently at defaults, it will automatically change to the specified values'.format(nodename))
|
||||
savedexc = None
|
||||
if not self.trieddefault:
|
||||
if not passwd:
|
||||
# So in preconfig context, we don't have admin permission to
|
||||
@@ -215,7 +238,12 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
# This is replacing one well known password (PASSW0RD) with another
|
||||
# (TempW0rd42)
|
||||
passwd = 'TempW0rd42'
|
||||
wc, pwdchanged = self.get_webclient('USERID', 'PASSW0RD', passwd)
|
||||
try:
|
||||
wc, pwdchanged = self.get_webclient('USERID', 'PASSW0RD', passwd)
|
||||
except LockedUserException as lue:
|
||||
wc = None
|
||||
pwdchanged = 'The user "USERID" has been locked out by too many incorrect password attempts'
|
||||
savedexc = lue
|
||||
if wc:
|
||||
if pwdchanged:
|
||||
if inpreconfig:
|
||||
@@ -223,16 +251,26 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
else:
|
||||
self._needpasswordchange = False
|
||||
return wc
|
||||
else:
|
||||
errinfo = pwdchanged
|
||||
self.trieddefault = True
|
||||
if isdefault:
|
||||
return
|
||||
self._atdefaultcreds = False
|
||||
if self.tmppasswd:
|
||||
wc, _ = self.get_webclient('USERID', self.tmppasswd, passwd)
|
||||
if savedexc:
|
||||
raise savedexc
|
||||
wc, errinfo = self.get_webclient('USERID', self.tmppasswd, passwd)
|
||||
else:
|
||||
wc, _ = self.get_webclient(user, passwd, None)
|
||||
if user == 'USERID' and savedexc:
|
||||
raise savedexc
|
||||
wc, errinfo = self.get_webclient(user, passwd, None)
|
||||
if wc:
|
||||
return wc
|
||||
else:
|
||||
if errinfo.get('description', '') == 'Invalid credentials':
|
||||
raise Exception('The stored confluent password for user "{}" was not accepted by the XCC'.format(user))
|
||||
raise Exception('Error connecting to webservice: ' + repr(errinfo))
|
||||
|
||||
def set_password_policy(self, strruleset, wc):
|
||||
ruleset = {'USER_GlobalMinPassChgInt': '0'}
|
||||
@@ -286,9 +324,13 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
wc.grab_json_response('/api/function',
|
||||
{'USER_UserPassChange': '{0},{1}'.format(uid, passwd)})
|
||||
if username != 'USERID':
|
||||
wc.grab_json_response(
|
||||
rsp, status = wc.grab_json_response_with_status(
|
||||
'/api/function',
|
||||
{'USER_UserModify': '{0},{1},,1,4,0,0,0,0,,8,'.format(uid, username)})
|
||||
if status == 200 and rsp.get('return', 0) == 762:
|
||||
rsp, status = wc.grab_json_response_with_status(
|
||||
'/api/function',
|
||||
{'USER_UserModify': '{0},{1},,1,Administrator,0,0,0,0,,8,'.format(uid, username)})
|
||||
self.tmppasswd = None
|
||||
self._currcreds = (username, passwd)
|
||||
|
||||
@@ -338,6 +380,10 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
nwc.grab_json_response('/api/function', {'USER_UserCreate': userparams})
|
||||
nwc.grab_json_response('/api/providers/logout')
|
||||
nwc, pwdchanged = self.get_webclient(user, tpass, passwd)
|
||||
if not nwc:
|
||||
if not pwdchanged:
|
||||
pwdchanged = 'Unknown'
|
||||
raise Exception('Error converting from sha356account: ' + repr(pwdchanged))
|
||||
if not pwdchanged:
|
||||
nwc.grab_json_response(
|
||||
'/api/function',
|
||||
@@ -431,3 +477,16 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
if em:
|
||||
self.configmanager.set_node_attributes(
|
||||
{em: {'id.uuid': enclosureuuid}})
|
||||
|
||||
def remote_nodecfg(nodename, cfm):
|
||||
cfg = cfm.get_node_attributes(
|
||||
nodename, 'hardwaremanagement.manager')
|
||||
ipaddr = cfg.get(nodename, {}).get('hardwaremanagement.manager', {}).get(
|
||||
'value', None)
|
||||
ipaddr = getaddrinfo(ipaddr, 0)[0][-1]
|
||||
if not ipaddr:
|
||||
raise Excecption('Cannot remote configure a system without known '
|
||||
'address')
|
||||
info = {'addresses': [ipaddr]}
|
||||
nh = NodeHandler(info, cfm)
|
||||
nh.config(nodename)
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
# option 97 = UUID (wireformat)
|
||||
|
||||
import confluent.config.configmanager as cfm
|
||||
import confluent.collective.manager as collective
|
||||
import confluent.noderange as noderange
|
||||
import confluent.log as log
|
||||
import confluent.netutil as netutil
|
||||
import ctypes
|
||||
@@ -264,9 +266,7 @@ def proxydhcp():
|
||||
if not myipn:
|
||||
continue
|
||||
if opts.get(77, None) == b'iPXE':
|
||||
cfd = cfg.get_node_attributes(node, ('deployment.*'))
|
||||
profile = cfd.get(node, {}).get(
|
||||
'deployment.pendingprofile', {}).get('value', None)
|
||||
profile = get_deployment_profile(node, cfg)
|
||||
if not profile:
|
||||
continue
|
||||
myip = socket.inet_ntoa(myipn)
|
||||
@@ -423,22 +423,36 @@ def remap_nodes(nodeattribs, configmanager):
|
||||
for node in updates:
|
||||
for attrib in updates[node]:
|
||||
if attrib == 'id.uuid':
|
||||
uuidmap[updates[node][attrib]['value']] = node
|
||||
uuidmap[updates[node][attrib]['value'].lower()] = node
|
||||
elif 'hwaddr' in attrib:
|
||||
macmap[updates[node][attrib]['value']] = node
|
||||
macmap[updates[node][attrib]['value'].lower()] = node
|
||||
|
||||
|
||||
def get_deployment_profile(node, cfg, cfd=None):
|
||||
if not cfd:
|
||||
cfd = cfg.get_node_attributes(node, ('deployment.*'))
|
||||
profile = cfd.get(node, {}).get('deployment.pendingprofile', {}).get('value', None)
|
||||
if not profile:
|
||||
return None
|
||||
candmgrs = cfd.get(node, {}).get('collective.managercandidates', {}).get('value', None)
|
||||
if candmgrs:
|
||||
candmgrs = noderange.NodeRange(candmgrs, cfg).nodes
|
||||
if collective.get_myname() not in candmgrs:
|
||||
return None
|
||||
return profile
|
||||
|
||||
staticassigns = {}
|
||||
myipbypeer = {}
|
||||
def check_reply(node, info, packet, sock, cfg, reqview):
|
||||
httpboot = info['architecture'] == 'uefi-httpboot'
|
||||
replen = 275 # default is going to be 286
|
||||
cfd = cfg.get_node_attributes(node, ('deployment.*'))
|
||||
profile = cfd.get(node, {}).get('deployment.pendingprofile', {}).get('value', None)
|
||||
myipn = info['netinfo']['recvip']
|
||||
myipn = socket.inet_aton(myipn)
|
||||
profile = get_deployment_profile(node, cfg, cfd)
|
||||
if not profile:
|
||||
return
|
||||
myipn = info['netinfo']['recvip']
|
||||
myipn = socket.inet_aton(myipn)
|
||||
|
||||
rqtype = packet[53][0]
|
||||
insecuremode = cfd.get(node, {}).get('deployment.useinsecureprotocols',
|
||||
{}).get('value', 'never')
|
||||
|
||||
@@ -360,6 +360,9 @@ def _add_attributes(parsed):
|
||||
return
|
||||
|
||||
|
||||
def unicast_scan(address):
|
||||
pass
|
||||
|
||||
def query_srvtypes(target):
|
||||
"""Query the srvtypes advertised by the target
|
||||
|
||||
@@ -465,15 +468,16 @@ def snoop(handler, protocol=None):
|
||||
# will now yield dupe info over time
|
||||
known_peers = set([])
|
||||
peerbymacaddress = {}
|
||||
neighutil.update_neigh()
|
||||
while r:
|
||||
for s in r:
|
||||
(rsp, peer) = s.recvfrom(9000)
|
||||
ip = peer[0].partition('%')[0]
|
||||
if ip not in neighutil.neightable:
|
||||
continue
|
||||
if peer in known_peers:
|
||||
continue
|
||||
if ip not in neighutil.neightable:
|
||||
neighutil.update_neigh()
|
||||
if ip not in neighutil.neightable:
|
||||
continue
|
||||
known_peers.add(peer)
|
||||
mac = neighutil.neightable[ip]
|
||||
if mac in peerbymacaddress:
|
||||
|
||||
@@ -29,12 +29,15 @@
|
||||
|
||||
|
||||
import confluent.config.configmanager as cfm
|
||||
import confluent.collective.manager as collective
|
||||
import confluent.neighutil as neighutil
|
||||
import confluent.noderange as noderange
|
||||
import confluent.util as util
|
||||
import confluent.log as log
|
||||
import confluent.netutil as netutil
|
||||
import eventlet.green.select as select
|
||||
import eventlet.green.socket as socket
|
||||
import eventlet.greenpool as gp
|
||||
import time
|
||||
try:
|
||||
from eventlet.green.urllib.request import urlopen
|
||||
@@ -118,7 +121,6 @@ def snoop(handler, byehandler=None, protocol=None, uuidlookup=None):
|
||||
newmacs = set([])
|
||||
machandlers = {}
|
||||
r, _, _ = select.select((net4, net6), (), (), 60)
|
||||
neighutil.update_neigh()
|
||||
while r:
|
||||
for s in r:
|
||||
(rsp, peer) = s.recvfrom(9000)
|
||||
@@ -128,10 +130,12 @@ def snoop(handler, byehandler=None, protocol=None, uuidlookup=None):
|
||||
method, _, _ = rsp[0].split(b' ', 2)
|
||||
if method == b'NOTIFY':
|
||||
ip = peer[0].partition('%')[0]
|
||||
if ip not in neighutil.neightable:
|
||||
continue
|
||||
if peer in known_peers:
|
||||
continue
|
||||
if ip not in neighutil.neightable:
|
||||
neighutil.update_neigh()
|
||||
if ip not in neighutil.neightable:
|
||||
continue
|
||||
mac = neighutil.neightable[ip]
|
||||
known_peers.add(peer)
|
||||
newmacs.add(mac)
|
||||
@@ -184,10 +188,15 @@ def snoop(handler, byehandler=None, protocol=None, uuidlookup=None):
|
||||
# planned for
|
||||
cfg = cfm.ConfigManager(None)
|
||||
cfd = cfg.get_node_attributes(
|
||||
node, 'deployment.pendingprofile')
|
||||
node, ['deployment.pendingprofile', 'collective.managercandidates'])
|
||||
if not cfd.get(node, {}).get(
|
||||
'deployment.pendingprofile', {}).get('value', None):
|
||||
break
|
||||
candmgrs = cfd.get(node, {}).get('collective.managercandidates', {}).get('value', None)
|
||||
if candmgrs:
|
||||
candmgrs = noderange.NodeRange(candmgrs, cfg).nodes
|
||||
if collective.get_myname() not in candmgrs:
|
||||
break
|
||||
currtime = time.time()
|
||||
seconds = int(currtime)
|
||||
msecs = int(currtime * 1000 % 1000)
|
||||
@@ -200,6 +209,8 @@ def snoop(handler, byehandler=None, protocol=None, uuidlookup=None):
|
||||
cfg, node, ifidx=iface)
|
||||
if ncfg.get('matchesnodename', None):
|
||||
reply += 'DEFAULTNET: 1\r\n'
|
||||
elif not netutil.address_is_local(peer[0]):
|
||||
continue
|
||||
if not isinstance(reply, bytes):
|
||||
reply = reply.encode('utf8')
|
||||
s.sendto(reply, peer)
|
||||
@@ -274,13 +285,26 @@ def _find_service(service, target):
|
||||
if timeout < 0:
|
||||
timeout = 0
|
||||
r, _, _ = select.select((net4, net6), (), (), timeout)
|
||||
querypool = gp.GreenPool()
|
||||
pooltargs = []
|
||||
for nid in peerdata:
|
||||
for url in peerdata[nid].get('urls', ()):
|
||||
if url.endswith('/desc.tmpl'):
|
||||
info = urlopen(url).read()
|
||||
if b'<friendlyName>Athena</friendlyName>' in info:
|
||||
peerdata[nid]['services'] = ['service:thinkagile-storage']
|
||||
yield peerdata[nid]
|
||||
pooltargs.append((url, peerdata[nid]))
|
||||
for pi in querypool.imap(check_cpstorage, pooltargs):
|
||||
if pi is not None:
|
||||
yield pi
|
||||
|
||||
def check_cpstorage(urldata):
|
||||
url, data = urldata
|
||||
try:
|
||||
info = urlopen(url, timeout=1).read()
|
||||
if b'<friendlyName>Athena</friendlyName>' in info:
|
||||
data['services'] = ['service:thinkagile-storage']
|
||||
return data
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def _parse_ssdp(peer, rsp, peerdata):
|
||||
|
||||
@@ -34,6 +34,8 @@ def handle_connection(incoming, outgoing):
|
||||
for mysock in r:
|
||||
data = mysock.recv(32768)
|
||||
if not data:
|
||||
incoming.close()
|
||||
outgoing.close()
|
||||
return
|
||||
if mysock == incoming:
|
||||
outgoing.sendall(data)
|
||||
@@ -72,6 +74,7 @@ def forward_video():
|
||||
vidclient.setsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG, 1456)
|
||||
except Exception:
|
||||
conn.close()
|
||||
vidclient.close()
|
||||
continue
|
||||
eventlet.spawn_n(handle_connection, conn, vidclient)
|
||||
|
||||
|
||||
@@ -65,16 +65,6 @@ opmap = {
|
||||
}
|
||||
|
||||
|
||||
class RobustCookie(Cookie.SimpleCookie):
|
||||
# this is very bad form, but BaseCookie has a terrible flaw
|
||||
def _BaseCookie__set(self, K, rval, cval):
|
||||
try:
|
||||
super(RobustCookie, self)._BaseCookie__set(K, rval, cval)
|
||||
except Cookie.CookieError:
|
||||
# empty value if SimpleCookie rejects
|
||||
dict.__setitem__(self, K, Cookie.Morsel())
|
||||
|
||||
|
||||
def group_creation_resources():
|
||||
yield confluent.messages.Attributes(
|
||||
kv={'name': None}, desc="Name of the group").html() + '<br>'
|
||||
@@ -284,11 +274,10 @@ def _authorize_request(env, operation):
|
||||
if element.startswith('/sessions/current/'):
|
||||
element = None
|
||||
if 'HTTP_COOKIE' in env:
|
||||
#attempt to use the cookie. If it matches
|
||||
cc = RobustCookie()
|
||||
cc.load(env['HTTP_COOKIE'])
|
||||
if 'confluentsessionid' in cc:
|
||||
sessionid = cc['confluentsessionid'].value
|
||||
cidx = (env['HTTP_COOKIE']).find('confluentsessionid=')
|
||||
if cidx >= 0:
|
||||
sessionid = env['HTTP_COOKIE'][cidx+19:cidx+51]
|
||||
sessid = sessionid
|
||||
sessid = sessionid
|
||||
if sessionid in httpsessions:
|
||||
if _csrf_valid(env, httpsessions[sessionid]):
|
||||
|
||||
@@ -148,11 +148,10 @@ def get_fingerprint(switch, port, configmanager, portmatch):
|
||||
def _extract_extended_desc(info, source, integritychecked):
|
||||
source = str(source)
|
||||
info['verified'] = bool(integritychecked)
|
||||
if source.startswith('Lenovo SMM;'):
|
||||
info['peerdescription'] = 'Lenovo SMM'
|
||||
if ';S2=' in source:
|
||||
info['peersha256fingerprint'] = source.replace('Lenovo SMM;S2=',
|
||||
'')
|
||||
if source.startswith('Lenovo ') and ';S2=' in source:
|
||||
desc, fprint = source.split(';S2=', 1)
|
||||
info['peerdescription'] = desc
|
||||
info['peersha256fingerprint'] = fprint
|
||||
else:
|
||||
info['peerdescription'] = source
|
||||
|
||||
|
||||
@@ -138,7 +138,9 @@ def _affluent_map_switch(args):
|
||||
wc = webclient.SecureHTTPConnection(
|
||||
switch, 443, verifycallback=kv, timeout=5)
|
||||
wc.set_basic_credentials(user, password)
|
||||
macs = wc.grab_json_response('/affluent/macs/by-port')
|
||||
macs, retcode = wc.grab_json_response_with_status('/affluent/macs/by-port')
|
||||
if retcode != 200:
|
||||
raise Exception("No affluent detected")
|
||||
_macsbyswitch[switch] = macs
|
||||
|
||||
for iface in macs:
|
||||
@@ -193,6 +195,7 @@ def _map_switch_backend(args):
|
||||
haveqbridge = False
|
||||
mactobridge = {}
|
||||
conn = snmp.Session(switch, password, user)
|
||||
ifnamemap = get_portnamemap(conn)
|
||||
for vb in conn.walk('1.3.6.1.2.1.17.7.1.2.2.1.2'):
|
||||
haveqbridge = True
|
||||
oid, bridgeport = vb
|
||||
@@ -214,16 +217,32 @@ def _map_switch_backend(args):
|
||||
*([int(x) for x in oid[-6:]])
|
||||
)
|
||||
mactobridge[macaddr] = int(bridgeport)
|
||||
#ciscoiftovlanmap = {}
|
||||
vlanstocheck = set([])
|
||||
for vb in conn.walk('.1.3.6.1.4.1.9.9.68.1.2.2.1.2'):
|
||||
vlanstocheck.add(vb[1])
|
||||
#ciscotrunktovlanmap = {}
|
||||
for vb in conn.walk('.1.3.6.1.4.1.9.9.46.1.6.1.1.5'):
|
||||
vlanstocheck.add(vb[1])
|
||||
if not vlanstocheck:
|
||||
vlanstocheck.add(None)
|
||||
bridgetoifmap = {}
|
||||
for vb in conn.walk('1.3.6.1.2.1.17.1.4.1.2'):
|
||||
bridgeport, ifidx = vb
|
||||
bridgeport = int(str(bridgeport).rsplit('.', 1)[1])
|
||||
try:
|
||||
bridgetoifmap[bridgeport] = int(ifidx)
|
||||
except ValueError:
|
||||
# ifidx might be '', skip in such a case
|
||||
continue
|
||||
ifnamemap = get_portnamemap(conn)
|
||||
for vlan in vlanstocheck:
|
||||
if vlan:
|
||||
if user:
|
||||
conn = snmp.Session(switch, password, user, 'vlan-{}'.format(vlan))
|
||||
else:
|
||||
if not isinstance(password, str):
|
||||
password = password.decode('utf8')
|
||||
conn = snmp.Session(switch, '{}@{}'.format(password, vlan))
|
||||
for vb in conn.walk('1.3.6.1.2.1.17.1.4.1.2'):
|
||||
bridgeport, ifidx = vb
|
||||
bridgeport = int(str(bridgeport).rsplit('.', 1)[1])
|
||||
try:
|
||||
bridgetoifmap[bridgeport] = int(ifidx)
|
||||
except ValueError:
|
||||
# ifidx might be '', skip in such a case
|
||||
continue
|
||||
maccounts = {}
|
||||
bridgetoifvalid = False
|
||||
for mac in mactobridge:
|
||||
@@ -375,12 +394,19 @@ def _full_updatemacmap(configmanager):
|
||||
continue
|
||||
if curswitch not in _switchportmap:
|
||||
_switchportmap[curswitch] = {}
|
||||
if portname in _switchportmap[curswitch]:
|
||||
log.log({'error': 'Duplicate switch topology config '
|
||||
'for {0} and {1}'.format(
|
||||
node,
|
||||
if (portname in _switchportmap[curswitch] and
|
||||
_switchportmap[curswitch][portname] != node):
|
||||
if _switchportmap[curswitch][portname] is None:
|
||||
errstr = ('Duplicate switch attributes for {0} and '
|
||||
'a previously logged duplicate'.format(
|
||||
node))
|
||||
else:
|
||||
errstr = ('Duplicate switch topology config '
|
||||
'for {0} and {1}'.format(
|
||||
node,
|
||||
_switchportmap[curswitch][
|
||||
portname])})
|
||||
portname]))
|
||||
log.log({'error': errstr})
|
||||
_switchportmap[curswitch][portname] = None
|
||||
else:
|
||||
_switchportmap[curswitch][portname] = node
|
||||
|
||||
@@ -40,6 +40,22 @@ from libarchive.ffi import (
|
||||
def relax_umask():
|
||||
os.umask(0o22)
|
||||
|
||||
|
||||
def makedirs(path, mode):
|
||||
try:
|
||||
os.makedirs(path, 0o755)
|
||||
except OSError as e:
|
||||
if e.errno != 17:
|
||||
raise
|
||||
|
||||
def symlink(src, targ):
|
||||
try:
|
||||
os.symlink(src, targ)
|
||||
except OSError as e:
|
||||
if e.errno != 17:
|
||||
raise
|
||||
|
||||
|
||||
def update_boot(profilename):
|
||||
if profilename.startswith('/var/lib/confluent/public'):
|
||||
profiledir = profilename
|
||||
@@ -59,6 +75,7 @@ def update_boot(profilename):
|
||||
update_boot_esxi(profiledir, profile, label)
|
||||
|
||||
def update_boot_esxi(profiledir, profile, label):
|
||||
profname = os.path.basename(profiledir)
|
||||
kernelargs = profile.get('kernelargs', '')
|
||||
oum = os.umask(0o22)
|
||||
bootcfg = open('{0}/distribution/BOOT.CFG'.format(profiledir), 'r').read()
|
||||
@@ -89,7 +106,7 @@ def update_boot_esxi(profiledir, profile, label):
|
||||
else:
|
||||
newbootcfg += cfgline + '\n'
|
||||
efibootcfg += cfgline + '\n'
|
||||
os.makedirs('{0}/boot/efi/boot/'.format(profiledir), 0o755)
|
||||
makedirs('{0}/boot/efi/boot/'.format(profiledir), 0o755)
|
||||
bcfgout = os.open('{0}/boot/efi/boot/boot.cfg'.format(profiledir), os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0o644)
|
||||
bcfg = os.fdopen(bcfgout, 'w')
|
||||
try:
|
||||
@@ -102,7 +119,7 @@ def update_boot_esxi(profiledir, profile, label):
|
||||
bcfg.write(newbootcfg)
|
||||
finally:
|
||||
bcfg.close()
|
||||
os.symlink('/var/lib/confluent/public/site/initramfs.tgz',
|
||||
symlink('/var/lib/confluent/public/site/initramfs.tgz',
|
||||
'{0}/boot/site.tgz'.format(profiledir))
|
||||
for fn in filesneeded:
|
||||
if fn.startswith('/'):
|
||||
@@ -110,8 +127,10 @@ def update_boot_esxi(profiledir, profile, label):
|
||||
sourcefile = '{0}/distribution/{1}'.format(profiledir, fn)
|
||||
if not os.path.exists(sourcefile):
|
||||
sourcefile = '{0}/distribution/{1}'.format(profiledir, fn.upper())
|
||||
os.symlink(sourcefile, '{0}/boot/{1}'.format(profiledir, fn))
|
||||
os.symlink('{0}/distribution/EFI/BOOT/BOOTX64.EFI'.format(profiledir), '{0}/boot/efi/boot/bootx64.efi'.format(profiledir))
|
||||
symlink(sourcefile, '{0}/boot/{1}'.format(profiledir, fn))
|
||||
symlink('{0}/distribution/EFI/BOOT/BOOTX64.EFI'.format(profiledir), '{0}/boot/efi/boot/bootx64.efi'.format(profiledir))
|
||||
if os.path.exists('{0}/distribution/EFI/BOOT/CRYPTO64.EFI'.format(profiledir)):
|
||||
symlink('{0}/distribution/EFI/BOOT/CRYPTO64.EFI'.format(profiledir), '{0}/boot/efi/boot/crypto64.efi'.format(profiledir))
|
||||
ipout = os.open(profiledir + '/boot.ipxe', os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0o644)
|
||||
ipxeout = os.fdopen(ipout, 'w')
|
||||
try:
|
||||
@@ -124,10 +143,11 @@ def update_boot_esxi(profiledir, profile, label):
|
||||
ipxeout.close()
|
||||
subprocess.check_call(
|
||||
['/opt/confluent/bin/dir2img', '{0}/boot'.format(profiledir),
|
||||
'{0}/boot.img'.format(profiledir)], preexec_fn=relax_umask)
|
||||
'{0}/boot.img'.format(profiledir), profname], preexec_fn=relax_umask)
|
||||
|
||||
|
||||
def update_boot_linux(profiledir, profile, label):
|
||||
profname = os.path.basename(profiledir)
|
||||
kernelargs = profile.get('kernelargs', '')
|
||||
grubcfg = "set timeout=5\nmenuentry '"
|
||||
grubcfg += label
|
||||
@@ -162,7 +182,7 @@ def update_boot_linux(profiledir, profile, label):
|
||||
ipxeout.close()
|
||||
subprocess.check_call(
|
||||
['/opt/confluent/bin/dir2img', '{0}/boot'.format(profiledir),
|
||||
'{0}/boot.img'.format(profiledir)], preexec_fn=relax_umask)
|
||||
'{0}/boot.img'.format(profiledir), profname], preexec_fn=relax_umask)
|
||||
|
||||
|
||||
def extract_entries(entries, flags=0, callback=None, totalsize=None, extractlist=None):
|
||||
@@ -213,6 +233,7 @@ def check_centos(isoinfo):
|
||||
ver = None
|
||||
arch = None
|
||||
cat = None
|
||||
isstream = ''
|
||||
for entry in isoinfo[0]:
|
||||
if 'centos-release-7' in entry:
|
||||
dotsplit = entry.split('.')
|
||||
@@ -225,9 +246,25 @@ def check_centos(isoinfo):
|
||||
arch = entry.split('.')[-2]
|
||||
cat = 'el8'
|
||||
break
|
||||
elif 'centos-stream-release-8' in entry:
|
||||
ver = entry.split('-')[3]
|
||||
arch = entry.split('.')[-2]
|
||||
cat = 'el8'
|
||||
isstream = '_stream'
|
||||
break
|
||||
elif 'centos-linux-release-8' in entry:
|
||||
ver = entry.split('-')[3]
|
||||
arch = entry.split('.')[-2]
|
||||
cat = 'el8'
|
||||
break
|
||||
else:
|
||||
return None
|
||||
return {'name': 'centos-{0}-{1}'.format(ver, arch), 'method': EXTRACT, 'category': cat}
|
||||
if arch == 'noarch' and '.discinfo' in isoinfo[1]:
|
||||
prodinfo = isoinfo[1]['.discinfo']
|
||||
arch = prodinfo.split(b'\n')[2]
|
||||
if not isinstance(arch, str):
|
||||
arch = arch.decode('utf-8')
|
||||
return {'name': 'centos{2}-{0}-{1}'.format(ver, arch, isstream), 'method': EXTRACT, 'category': cat}
|
||||
|
||||
def check_esxi(isoinfo):
|
||||
if '.DISCINFO' not in isoinfo[1]:
|
||||
@@ -324,9 +361,26 @@ def check_sles(isoinfo):
|
||||
return None
|
||||
|
||||
|
||||
def _priv_check_oraclelinux(isoinfo):
|
||||
ver = None
|
||||
arch = None
|
||||
for entry in isoinfo[0]:
|
||||
if 'oraclelinux-release-' in entry and 'release-el7' not in entry:
|
||||
ver = entry.split('-')[2]
|
||||
arch = entry.split('.')[-2]
|
||||
break
|
||||
else:
|
||||
return None
|
||||
major = ver.split('.', 1)[0]
|
||||
return {'name': 'oraclelinux-{0}-{1}'.format(ver, arch), 'method': EXTRACT,
|
||||
'category': 'el{0}'.format(major)}
|
||||
|
||||
def check_rhel(isoinfo):
|
||||
ver = None
|
||||
arch = None
|
||||
isoracle = _priv_check_oraclelinux(isoinfo)
|
||||
if isoracle:
|
||||
return isoracle
|
||||
for entry in isoinfo[0]:
|
||||
if 'redhat-release-7' in entry:
|
||||
dotsplit = entry.split('.')
|
||||
|
||||
@@ -110,7 +110,7 @@ def exithandler():
|
||||
|
||||
atexit.register(exithandler)
|
||||
|
||||
_ipmiworkers = greenpool.GreenPool()
|
||||
_ipmiworkers = greenpool.GreenPool(128)
|
||||
|
||||
_ipmithread = None
|
||||
_ipmiwaiters = []
|
||||
|
||||
@@ -100,7 +100,9 @@ class SshShell(conapi.Console):
|
||||
while self.connected:
|
||||
pendingdata = self.shell.recv(8192)
|
||||
if not pendingdata:
|
||||
self.datacallback(conapi.ConsoleEvent.Disconnect)
|
||||
self.ssh.close()
|
||||
if self.datacallback:
|
||||
self.datacallback(conapi.ConsoleEvent.Disconnect)
|
||||
return
|
||||
self.datacallback(pendingdata)
|
||||
|
||||
@@ -110,7 +112,7 @@ class SshShell(conapi.Console):
|
||||
# that would rather not use the nodename as anything but an opaque
|
||||
# identifier
|
||||
self.datacallback = callback
|
||||
if self.username is not '':
|
||||
if self.username is not b'':
|
||||
self.logon()
|
||||
else:
|
||||
self.inputmode = 0
|
||||
@@ -126,12 +128,14 @@ class SshShell(conapi.Console):
|
||||
password=self.password, allow_agent=False,
|
||||
look_for_keys=False)
|
||||
except paramiko.AuthenticationException:
|
||||
self.ssh.close()
|
||||
self.inputmode = 0
|
||||
self.username = b''
|
||||
self.password = b''
|
||||
self.datacallback('\r\nlogin as: ')
|
||||
return
|
||||
except paramiko.ssh_exception.NoValidConnectionsError as e:
|
||||
self.ssh.close()
|
||||
self.datacallback(str(e))
|
||||
self.inputmode = 0
|
||||
self.username = b''
|
||||
@@ -139,6 +143,7 @@ class SshShell(conapi.Console):
|
||||
self.datacallback('\r\nlogin as: ')
|
||||
return
|
||||
except cexc.PubkeyInvalid as pi:
|
||||
self.ssh.close()
|
||||
self.keyaction = ''
|
||||
self.candidatefprint = pi.fingerprint
|
||||
self.datacallback(pi.message)
|
||||
@@ -148,6 +153,7 @@ class SshShell(conapi.Console):
|
||||
self.datacallback('\r\nEnter "disconnect" or "accept": ')
|
||||
return
|
||||
except paramiko.SSHException as pi:
|
||||
self.ssh.close()
|
||||
self.inputmode = -2
|
||||
warn = str(pi)
|
||||
if warnhostkey:
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import confluent.config.configmanager as configmanager
|
||||
import confluent.collective.manager as collective
|
||||
import confluent.netutil as netutil
|
||||
import confluent.noderange as noderange
|
||||
import confluent.sshutil as sshutil
|
||||
import confluent.util as util
|
||||
import eventlet.green.socket as socket
|
||||
import eventlet.green.subprocess as subprocess
|
||||
import confluent.discovery.handlers.xcc as xcc
|
||||
import confluent.discovery.handlers.tsm as tsm
|
||||
import crypt
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import yaml
|
||||
|
||||
@@ -33,7 +37,7 @@ def get_extra_names(nodename, cfg):
|
||||
currnames = currnames.split(',')
|
||||
for currname in currnames:
|
||||
names.add(currname)
|
||||
if domain not in currname:
|
||||
if domain and domain not in currname:
|
||||
names.add('{0}.{1}'.format(currname, domain))
|
||||
return names
|
||||
|
||||
@@ -199,7 +203,7 @@ def handle_request(env, start_response):
|
||||
start_response('200 OK', (('Content-Type', 'text/plain'),))
|
||||
yield cert
|
||||
elif env['PATH_INFO'] == '/self/nodelist':
|
||||
nodes, _ = get_cluster_list(cfg)
|
||||
nodes, _ = get_cluster_list(nodename, cfg)
|
||||
if isgeneric:
|
||||
start_response('200 OK', (('Content-Type', 'text/plain'),))
|
||||
for node in util.natural_sort(nodes):
|
||||
@@ -207,6 +211,24 @@ def handle_request(env, start_response):
|
||||
else:
|
||||
start_response('200 OK', (('Content-Type', retype),))
|
||||
yield dumper(sorted(nodes))
|
||||
elif env['PATH_INFO'] == '/self/remoteconfigbmc':
|
||||
if reqbody:
|
||||
try:
|
||||
reqbody = yaml.safe_load(reqbody)
|
||||
except Exception:
|
||||
reqbody = None
|
||||
if not reqbody:
|
||||
start_response('400 bad request', ())
|
||||
cfgmod = reqbody.get('configmod', 'unspecified')
|
||||
if cfgmod == 'xcc':
|
||||
xcc.remote_nodecfg(nodename, cfg)
|
||||
elif cfgmod == 'tsm':
|
||||
tsm.remote_nodecfg(nodename, cfg)
|
||||
else:
|
||||
start_response('500 unsupported configmod', ())
|
||||
yield 'Unsupported configmod "{}"'.format(cfgmod)
|
||||
start_response('200 Ok', ())
|
||||
yield 'complete'
|
||||
elif env['PATH_INFO'] == '/self/updatestatus':
|
||||
update = yaml.safe_load(reqbody)
|
||||
if update['status'] == 'staged':
|
||||
@@ -236,15 +258,58 @@ def handle_request(env, start_response):
|
||||
else:
|
||||
start_response('500 Error', (('Content-Type', 'text/plain'),))
|
||||
yield 'No pending profile detected, unable to accept status update'
|
||||
elif env['PATH_INFO'] == '/self/saveapikey':
|
||||
cfg.set_node_attributes({
|
||||
nodename: {'deployment.sealedapikey': {'value': reqbody}}})
|
||||
start_response('200 OK', ())
|
||||
yield ''
|
||||
elif env['PATH_INFO'].startswith('/self/scriptlist/'):
|
||||
scriptcat = env['PATH_INFO'].replace('/self/scriptlist/', '')
|
||||
if '..' in scriptcat:
|
||||
start_response('400 Bad Requst', ())
|
||||
yield ''
|
||||
return
|
||||
deployinfo = cfg.get_node_attributes(
|
||||
nodename, ('deployment.*',))
|
||||
deployinfo = deployinfo.get(nodename, {})
|
||||
profile = deployinfo.get(
|
||||
'deployment.pendingprofile', {}).get('value', '')
|
||||
if not profile:
|
||||
profile = deployinfo.get(
|
||||
'deployment.stagedprofile', {}).get('value', '')
|
||||
if not profile:
|
||||
profile = deployinfo.get(
|
||||
'deployment.profile', {}).get('value', '')
|
||||
slist = None
|
||||
try:
|
||||
slist = os.listdir('/var/lib/confluent/public/os/{0}/scripts/{1}.d/'.format(profile, scriptcat))
|
||||
except OSError:
|
||||
pass
|
||||
if slist:
|
||||
start_response('200 OK', (('Content-Type', 'application/yaml'),))
|
||||
yield yaml.safe_dump(util.natural_sort(slist), default_flow_style=False)
|
||||
else:
|
||||
start_response('200 OK', ())
|
||||
yield ''
|
||||
else:
|
||||
start_response('404 Not Found', ())
|
||||
yield 'Not found'
|
||||
|
||||
|
||||
def get_cluster_list(cfg=None):
|
||||
def get_cluster_list(nodename=None, cfg=None):
|
||||
if cfg is None:
|
||||
cfg = configmanager.ConfigManager(None)
|
||||
nodes = set(cfg.list_nodes())
|
||||
nodes = None
|
||||
if nodename is not None:
|
||||
sshpeers = cfg.get_node_attributes(nodename, 'ssh.trustnodes')
|
||||
sshpeers = sshpeers.get(nodename, {}).get('ssh.trustnodes', {}).get(
|
||||
'value', None)
|
||||
if sshpeers:
|
||||
nodes = noderange.NodeRange(sshpeers, cfg).nodes
|
||||
autonodes = False
|
||||
if nodes is None:
|
||||
autonodes = True
|
||||
nodes = set(cfg.list_nodes())
|
||||
domain = None
|
||||
for node in list(util.natural_sort(nodes)):
|
||||
if domain is None:
|
||||
@@ -253,12 +318,13 @@ def get_cluster_list(cfg=None):
|
||||
'value', None)
|
||||
for extraname in get_extra_names(node, cfg):
|
||||
nodes.add(extraname)
|
||||
for mgr in configmanager.list_collective():
|
||||
nodes.add(mgr)
|
||||
if domain and domain not in mgr:
|
||||
nodes.add('{0}.{1}'.format(mgr, domain))
|
||||
myname = collective.get_myname()
|
||||
nodes.add(myname)
|
||||
if domain and domain not in myname:
|
||||
nodes.add('{0}.{1}'.format(myname, domain))
|
||||
if autonodes:
|
||||
for mgr in configmanager.list_collective():
|
||||
nodes.add(mgr)
|
||||
if domain and domain not in mgr:
|
||||
nodes.add('{0}.{1}'.format(mgr, domain))
|
||||
myname = collective.get_myname()
|
||||
nodes.add(myname)
|
||||
if domain and domain not in myname:
|
||||
nodes.add('{0}.{1}'.format(myname, domain))
|
||||
return nodes, domain
|
||||
|
||||
@@ -111,6 +111,8 @@ class ShellSession(consoleserver.ConsoleSession):
|
||||
|
||||
def destroy(self):
|
||||
try:
|
||||
activesessions[(self.configmanager.tenant, self.node,
|
||||
self.username)][self.sessionid].close()
|
||||
del activesessions[(self.configmanager.tenant, self.node,
|
||||
self.username)][self.sessionid]
|
||||
except KeyError:
|
||||
|
||||
@@ -79,7 +79,7 @@ def randomstring(length=20):
|
||||
if length % 4 > 0:
|
||||
chunksize += 1
|
||||
strval = base64.urlsafe_b64encode(os.urandom(chunksize * 3))
|
||||
return stringify(strval[0:length-1])
|
||||
return stringify(strval[0:length])
|
||||
|
||||
|
||||
def securerandomnumber(low=0, high=4294967295):
|
||||
|
||||
@@ -13,11 +13,15 @@ rpmbuild -bb confluent-genesis.spec
|
||||
rm -rf /usr/lib/dracut/modules.d/97genesis
|
||||
cd -
|
||||
# getting src rpms would be nice, but centos isn't consistent..
|
||||
# skipcpio | xzcat | cpio -dumiv
|
||||
# rpm -qf $(find . -type f | sed -e 's/^.//') |sort -u|grep -v 'not owned' > rpmlist
|
||||
# /usr/lib/dracut/skipcpio /opt/confluent/genesis/x86_64/boot/initramfs/distribution | xzcat | cpio -dumiv
|
||||
# rpm -qf $(find . -type f | sed -e 's/^.//') |sort -u|grep -v 'not owned' > ../rpmlist
|
||||
# for f in $(find . -type f | sed -e 's/^.//'); do echo -n $f:; rpm -qf $f ; done > ../annotedrprmlist
|
||||
# for i in $(cat rpmlist); do rpm -qi $i|grep Source; done |awk '{print $4}'|sort -u > srcrpmlist
|
||||
# for i in $(cat ../srcrpmlist); do wget http://vault.centos.org/8.2.2004/BaseOS/Source/SPackages/$i; done
|
||||
# http://vault.centos.org/8.2.2004/AppStream/Source/SPackages/$i
|
||||
# for i in $(cat ../srcrpmlist); do wget --continue http://vault.centos.org/8.2.2004/BaseOS/Source/SPackages/$i; done
|
||||
# ls > downloadedsrcpmlist
|
||||
# diff -u srcpmlist downloadedsrcrpmlist
|
||||
# diff -u srcrpmlist downloadedsrcpmrlist |grep ^-|grep -v srcrpmlist
|
||||
# for i in $(diff -u srcrpmlist downloadedsrcpmrlist |grep ^-|grep -v srcrpmlist|sed -e s/-//); do wget --continue http://vault.centos.org/8.2.2004/AppStream/Source/SPackages/$i; done
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
%define arch x86_64
|
||||
Version: 3.0.0
|
||||
Version: 3.1.0
|
||||
Release: 1
|
||||
Name: confluent-genesis-%{arch}
|
||||
BuildArch: noarch
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# To use this script, rename or copy the mofed image to either ofed.tgz or ofed.iso
|
||||
# and modify the script below if wanting to use the iso instead of tgz
|
||||
|
||||
# It checks for mellanox devices and opts not to install, so this script could be added
|
||||
# to a general profile without causing mofed to install on non-mellanox systems
|
||||
. /etc/confluent/functions
|
||||
if lspci -d 15b3:: -n |grep 15b3 > /dev/null; then
|
||||
# Uncomment the following three lines and comment out the next
|
||||
# two lines to use the .iso instead of the tgz packaging
|
||||
#fetch_remote ofed/ofed.iso
|
||||
#mkdir MLNX_OFED
|
||||
#mount -o loop ofed.iso MLNX_OFED
|
||||
fetch_remote mofed/mofed.tgz
|
||||
tar xf mofed.tgz
|
||||
# The rest is common between tar and iso
|
||||
cd MLNX_OFED*
|
||||
mount -o loop ofed
|
||||
./mlnxofedinstall --force
|
||||
fi
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
mkdir -p /etc/pki/tls/certs
|
||||
echo -n "" >> /tmp/net.ifaces
|
||||
cat /tls/*.0 >> /etc/pki/tls/certs/ca-bundle.crt
|
||||
if ! grep console= /proc/cmdline >& /dev/null; then
|
||||
autocons=$(/opt/confluent/bin/autocons)
|
||||
if [ -n "$autocons" ]; then
|
||||
echo console=$autocons |sed -e 's!/dev/!!' >> /tmp/01-autocons.conf
|
||||
autocons=${autocons%,*}
|
||||
echo $autocons > /tmp/01-autocons.devnode
|
||||
echo "Detected firmware specified console at $(cat /tmp/01-autocons.conf)" > $autocons
|
||||
echo "Modify profile.yaml and run updateboot to have nodeconsole work by adding console=$(cat /tmp/01-autocons.conf)" > $autocons
|
||||
fi
|
||||
fi
|
||||
if grep console=ttyS /proc/cmdline >& /dev/null; then
|
||||
echo "Serial console has been requested in the kernel arguments, the local video may not show progress" > /dev/tty1
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
#!/bin/sh
|
||||
[ -e /tmp/confluent.initq ] && return 0
|
||||
if [ -f /tmp/dd_disk ]; then
|
||||
for dd in $(cat /tmp/dd_disk); do
|
||||
if [ -e $dd ]; then
|
||||
driver-updates --disk $dd $dd
|
||||
fi
|
||||
done
|
||||
fi
|
||||
TRIES=0
|
||||
oum=$(umask)
|
||||
umask 0077
|
||||
mkdir -p /etc/confluent
|
||||
echo -n > /etc/confluent/confluent.info
|
||||
umask $oum
|
||||
cd /sys/class/net
|
||||
while ! grep ^EXTMGRINFO: /etc/confluent/confluent.info | awk -F'|' '{print $3}' | grep 1 >& /dev/null && [ "$TRIES" -lt 60 ]; do
|
||||
TRIES=$((TRIES + 1))
|
||||
for currif in *; do
|
||||
ip link set $currif up
|
||||
done
|
||||
/opt/confluent/bin/copernicus -t > /etc/confluent/confluent.info
|
||||
done
|
||||
cd /
|
||||
grep ^EXTMGRINFO: /etc/confluent/confluent.info || return 0 # Do absolutely nothing if no data at all yet
|
||||
echo -n "" > /tmp/confluent.initq
|
||||
# restart cmdline
|
||||
echo -n "" > /etc/cmdline.d/01-confluent.conf
|
||||
mkdir -p /var/log/xcat
|
||||
|
||||
#TODO: blkid --label <whatever> to find mounted api
|
||||
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
|
||||
mgr=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info| sed -e 's/^EXTMGRINFO: //' | awk -F'|' '{print $1 " " $2 " " $3}' |grep 1$ | awk 'NR < 2')
|
||||
if [ -z "$mgr" ]; then
|
||||
mgr=$(grep ^EXTMGRINFO: /etc/confluent/confluent.info| sed -e 's/^EXTMGRINFO: //' | awk -F'|' '{print $1 " " $2 " " $3}' | awk 'NR < 2')
|
||||
fi
|
||||
mgtiface=$(echo $mgr | awk '{print $2}')
|
||||
mgr=$(echo $mgr | awk '{print $1}')
|
||||
if [ ! -f /etc/confluent/confluent.apikey ]; then
|
||||
/opt/confluent/bin/clortho $nodename $mgr > /etc/confluent/confluent.apikey
|
||||
fi
|
||||
if echo $mgr | grep '%' > /dev/null; then
|
||||
echo $mgr | awk -F% '{print $2}' > /tmp/confluent.ifidx
|
||||
fi
|
||||
apikey=$(cat /etc/confluent/confluent.apikey)
|
||||
if echo $mgr | grep ':' > /dev/null; then
|
||||
mgr="[$mgr]"
|
||||
fi
|
||||
curl -f -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" -H "CONFLUENT_MGTIFACE: $mgtiface" https://$mgr/confluent-api/self/deploycfg > /etc/confluent/confluent.deploycfg
|
||||
|
||||
|
||||
|
||||
|
||||
cat /tls/*.pem > /etc/confluent/ca.pem
|
||||
ifidx=$(cat /tmp/confluent.ifidx)
|
||||
ifname=$(ip link |grep ^$ifidx:|awk '{print $2}')
|
||||
ifname=${ifname%:}
|
||||
echo $ifname > /tmp/net.ifaces
|
||||
|
||||
dnsdomain=$(grep ^dnsdomain: /etc/confluent/confluent.deploycfg)
|
||||
dnsdomain=${dnsdomain#dnsdomain: }
|
||||
hostname=$nodename
|
||||
if [ ! -z "$dnsdomain" ] && [ "$dnsdomain" != "null" ]; then
|
||||
hostname=$hostname.$dnsdomain
|
||||
fi
|
||||
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg)
|
||||
mgr=${mgr#deploy_server: }
|
||||
if ! grep XCAT /proc/cmdline > /dev/null; then
|
||||
echo XCAT=$mgr:3001 >> /etc/cmdline.d/01-confluent.conf
|
||||
fi
|
||||
profilename=$(grep ^profile: /etc/confluent/confluent.deploycfg)
|
||||
profilename=${profilename#profile: }
|
||||
proto=$(grep ^protocol: /etc/confluent/confluent.deploycfg)
|
||||
proto=${proto#protocol: }
|
||||
textconsole=$(grep ^textconsole: /etc/confluent/confluent.deploycfg)
|
||||
textconsole=${textconsole#textconsole: }
|
||||
if [ "$textconsole" = "true" ] && ! grep console= /proc/cmdline > /dev/null; then
|
||||
autocons=$(cat /tmp/01-autocons.devnode)
|
||||
if [ ! -z "$autocons" ]; then
|
||||
echo Auto-configuring installed system to use text console
|
||||
echo Auto-configuring installed system to use text console > $autocons
|
||||
cp /tmp/01-autocons.conf /etc/cmdline.d/
|
||||
else
|
||||
echo "Unable to automatically detect requested text console"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo imgurl=$proto://$mgr/confluent-public/os/$profilename/rootimg.cpio.gz >> /etc/cmdline.d/01-confluent.conf
|
||||
autoconfigmethod=$(grep ipv4_method /etc/confluent/confluent.deploycfg)
|
||||
autoconfigmethod=${autoconfigmethod#ipv4_method: }
|
||||
if [ "$autoconfigmethod" = "dhcp" ]; then
|
||||
echo ip=$ifname:dhcp >> /etc/cmdline.d/01-confluent.conf
|
||||
else
|
||||
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 ipv4_netmask: /etc/confluent/confluent.deploycfg)
|
||||
v4nm=${v4nm#ipv4_netmask: }
|
||||
echo ip=$v4addr::$v4gw:$v4nm:$hostname:$ifname:none >> /etc/cmdline.d/01-confluent.conf
|
||||
mkdir -p /etc/sysconfig/network-scripts
|
||||
ifcfg=/etc/sysconfig/network-scripts/ifcfg-$ifname
|
||||
echo DEVICE=$ifname >> $ifcfg
|
||||
echo NAME=$ifname >> $ifcfg
|
||||
echo IPADDR=$v4addr >> $ifcfg
|
||||
echo GATEWAY=$v4gw >> $ifcfg
|
||||
echo NETMASK=$v4nm >> $ifcfg
|
||||
fi
|
||||
nameserversec=0
|
||||
while read -r entry; do
|
||||
if [ $nameserversec = 1 ]; then
|
||||
if [[ $entry == "-"* ]] && [[ $entry != "- ''" ]]; then
|
||||
echo nameserver=${entry#- } >> /etc/cmdline.d/01-confluent.conf
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
nameserversec=0
|
||||
if [ "${entry%:*}" = "nameservers" ]; then
|
||||
nameserversec=1
|
||||
continue
|
||||
fi
|
||||
done < /etc/confluent/confluent.deploycfg
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
BUNDLENAME=/sysroot/etc/pki/tls/certs/ca-bundle.crt
|
||||
while [ -h $BUNDLENAME ]; do
|
||||
BUNDLENAME=/sysroot/$(readlink $BUNDLENAME)
|
||||
done
|
||||
cat /tls/*.0 >> $BUNDLENAME
|
||||
mkdir -p /sysroot/etc/confluent/
|
||||
chmod 700 /sysroot/etc/confluent
|
||||
cp -a /tls /sysroot/etc/confluent
|
||||
cp /etc/confluent/* /sysroot/etc/confluent
|
||||
rootpw=$(grep ^rootpassword: /etc/confluent/confluent.deploycfg | awk '{print $2}')
|
||||
if [ "null" = "$rootpw" -o "" = $rootpw ]; then
|
||||
rootpw='*'
|
||||
fi
|
||||
sed -i "s!root:[^:]*:!root:$rootpw:!" /sysroot/etc/shadow
|
||||
mkdir -p /sysroot/root/.ssh
|
||||
chmod 700 /sysroot/root/.ssh
|
||||
cat /ssh/*.rootpubkey > /sysroot/root/.ssh/authorized_keys
|
||||
chmod 600 /sysroot/root/.ssh/authorized_keys
|
||||
mkdir -p /sysroot/etc/ssh/
|
||||
for i in /ssh/*.ca; do
|
||||
echo '@cert-authority *' $(cat $i) >> /sysroot/etc/ssh/ssh_known_hosts
|
||||
done
|
||||
cp /opt/confluent/bin/apiclient /sysroot/etc/confluent
|
||||
cp /etc/sysconfig/network-scripts/* /sysroot/etc/sysconfig/network-scripts/
|
||||
ifname=$(ip link|grep ^$(cat /tmp/confluent.ifidx) | awk '{print $2}'|sed -e 's/://')
|
||||
mkdir /sysroot/tmp
|
||||
ip link set $ifname down; ip link set $ifname up
|
||||
while ! ip addr show dev $ifname|grep fe80 > /dev/null; do
|
||||
sleep 0.1
|
||||
done
|
||||
while ip addr|grep tentative > /dev/null; do
|
||||
sleep 0.1
|
||||
done
|
||||
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
export mgr profile
|
||||
curl -Ssf https://$mgr/confluent-public/os/$profile/scripts/earlyboot.sh > /sysroot/etc/confluent/earlyboot.sh
|
||||
chroot /sysroot bash /etc/confluent/earlyboot.sh
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
mgr=$(grep ^deploy_server: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
nodename=$(grep ^NODENAME: /etc/confluent/confluent.info|awk '{print $2}')
|
||||
export mgr profile nodename
|
||||
curl -sSf https://$mgr/confluent-public/os/$profile/scripts/functions > /tmp/functions
|
||||
. /tmp/functions
|
||||
|
||||
run_remote setupssh.sh
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
run_remote() {
|
||||
requestedcmd="'$*'"
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running $requestedcmd from https://$mgr/confluent-public/os/$profile/scripts/
|
||||
tmpdir=$(mktemp -d)
|
||||
echo Executing in $tmpdir
|
||||
cd $tmpdir
|
||||
curl -f -sS https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
|
||||
if [ $? != 0 ]; then echo $requestedcmd failed to download; return 1; fi
|
||||
chmod +x $1
|
||||
cmd=$1
|
||||
if [ -x /usr/bin/chcon ]; then
|
||||
chcon system_u:object_r:bin_t:s0 $cmd >& /dev/null
|
||||
fi
|
||||
shift
|
||||
./$cmd $*
|
||||
retcode=$?
|
||||
echo "$requestedcmd exited with code $retcode"
|
||||
cd - > /dev/null
|
||||
return $retcode
|
||||
}
|
||||
|
||||
run_remote_python() {
|
||||
echo
|
||||
echo '---------------------------------------------------------------------------'
|
||||
echo Running python script "'$*'" from https://$mgr/confluent-public/os/$profile/scripts/
|
||||
tmpdir=$(mktemp -d)
|
||||
echo Executing in $tmpdir
|
||||
cd $tmpdir
|
||||
curl -f -sS https://$mgr/confluent-public/os/$profile/scripts/$1 > $1
|
||||
if [ $? != 0 ]; then echo "'$*'" failed to download; return 1; fi
|
||||
/usr/libexec/platform-python $*
|
||||
retcode=$?
|
||||
echo "'$*' exited with code $retcode"
|
||||
cd - > /dev/null
|
||||
return $retcode
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
|
||||
rm /etc/ssh/*host*key* >& /dev/null
|
||||
ssh-keygen -A
|
||||
/usr/libexec/platform-python /etc/confluent/apiclient >& /dev/null
|
||||
for pubkey in /etc/ssh/ssh_host*key.pub; do
|
||||
certfile=${pubkey/.pub/-cert.pub}
|
||||
/usr/libexec/platform-python /etc/confluent/apiclient /confluent-api/self/sshcert $pubkey > $certfile
|
||||
echo HostCertificate $certfile >> /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
|
||||
if [ -d /etc/ssh/ssh_config.d/ ]; then
|
||||
sshconf=/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 -Ssf -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" https://$mgr/confluent-api/self/nodelist > /tmp/allnodes
|
||||
cp /tmp/allnodes /etc/ssh/shosts.equiv
|
||||
cp /tmp/allnodes /root/.shosts
|
||||
rm /tmp/allnodes
|
||||
|
||||
Reference in New Issue
Block a user