2
0
mirror of https://github.com/xcat2/confluent.git synced 2026-06-18 01:20:47 +00:00

Merge branch 'lenovo:master' into master

This commit is contained in:
weragrzeda
2023-09-21 13:57:12 +02:00
committed by GitHub
31 changed files with 566 additions and 71 deletions
+19
View File
@@ -53,6 +53,8 @@ argparser.add_option('-p', '--prompt', action='store_true',
argparser.add_option('-m', '--maxnodes', type='int',
help='Prompt if trying to set attributes on more '
'than specified number of nodes')
argparser.add_option('-s', '--set', dest='set', metavar='settings.batch',
default=False, help='set attributes using a batch file')
(options, args) = argparser.parse_args()
@@ -109,6 +111,23 @@ elif options.clear or options.environment or options.prompt:
sys.stderr.write('Attribute names required with specified options\n')
argparser.print_help()
exitcode = 400
elif options.set:
arglist = [noderange]
showtype='current'
argfile = open(options.set, 'r')
argset = argfile.readline()
while argset:
try:
argset = argset[:argset.index('#')]
except ValueError:
pass
argset = argset.strip()
if argset:
arglist += shlex.split(argset)
argset = argfile.readline()
session.stop_if_noderange_over(noderange, options.maxnodes)
exitcode=client.updateattrib(session,arglist,nodetype, noderange, options, None)
if exitcode != 0:
sys.exit(exitcode)
@@ -7,7 +7,8 @@ nodeattrib(8) -- List or change confluent nodes attributes
`nodeattrib <noderange> [<nodeattribute1=value1> <nodeattribute2=value2> ...]`
`nodeattrib -c <noderange> <nodeattribute1> <nodeattribute2> ...`
`nodeattrib -e <noderange> <nodeattribute1> <nodeattribute2> ...`
`nodeattrib -p <noderange> <nodeattribute1> <nodeattribute2> ...`
`nodeattrib -p <noderange> <nodeattribute1> <nodeattribute2> ...`
`nodeattrib <noderange> -s <attributes.batch>`
## DESCRIPTION
@@ -58,7 +59,10 @@ to a blank value will allow masking a group defined attribute with an empty valu
* `-p`, `--prompt`:
Request interactive prompting to provide values rather than the command line
or environment variables.
* `-s`, `--set`:
Set attributes using a batch file
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
Prompt if trying to set attributes on more than
specified number of nodes.
@@ -112,9 +112,10 @@ def get_interface_name(iname, settings):
return None
class NetplanManager(object):
def __init__(self):
def __init__(self, deploycfg):
self.cfgbydev = {}
self.read_connections()
self.deploycfg = deploycfg
def read_connections(self):
for plan in glob.glob('/etc/netplan/*.y*ml'):
@@ -174,6 +175,19 @@ class NetplanManager(object):
else:
needcfgwrite = True
cfgroutes.append({'via': gwaddr, 'to': 'default'})
dnsips = self.deploycfg.get('nameservers', [])
dnsdomain = self.deploycfg.get('dnsdomain', '')
if dnsips:
currdnsips = self.getcfgarrpath([devname, 'nameservers', 'addresses'])
for dnsip in dnsips:
if dnsip not in currdnsips:
needcfgwrite = True
currdnsips.append(dnsip)
if dnsdomain:
currdnsdomain = self.getcfgarrpath([devname, 'nameservers', 'search'])
if dnsdomain not in currdnsdomain:
needcfgwrite = True
currdnsdomain.append(dnsdomain)
if needcfgwrite:
needcfgapply = True
newcfg = {'network': {'version': 2, 'ethernets': {devname: self.cfgbydev[devname]}}}
@@ -403,6 +417,7 @@ if __name__ == '__main__':
myaddrs = apiclient.get_my_addresses()
srvs, _ = apiclient.scan_confluents()
doneidxs = set([])
dc = None
for srv in srvs:
try:
s = socket.create_connection((srv, 443))
@@ -422,6 +437,9 @@ if __name__ == '__main__':
continue
status, nc = apiclient.HTTPSClient(usejson=True, host=srv).grab_url_with_status('/confluent-api/self/netcfg')
nc = json.loads(nc)
if not dc:
status, dc = apiclient.HTTPSClient(usejson=True, host=srv).grab_url_with_status('/confluent-api/self/deploycfg2')
dc = json.loads(dc)
iname = get_interface_name(idxmap[curridx], nc.get('default', {}))
if iname:
for iname in iname.split(','):
@@ -448,7 +466,7 @@ if __name__ == '__main__':
del netname_to_interfaces['default']
rm_tmp_llas(tmpllas)
if os.path.exists('/usr/sbin/netplan'):
nm = NetplanManager()
nm = NetplanManager(dc)
if os.path.exists('/usr/bin/nmcli'):
nm = NetworkManager(devtypes)
elif os.path.exists('/usr/sbin/wicked'):
@@ -11,6 +11,13 @@ import struct
import sys
import subprocess
def get_partname(devname, idx):
if devname[-1] in '0123456789':
return '{}p{}'.format(devname, idx)
else:
return '{}{}'.format(devname, idx)
def get_next_part_meta(img, imgsize):
if img.tell() == imgsize:
return None
@@ -53,10 +60,13 @@ class PartedRunner():
def __init__(self, disk):
self.disk = disk
def run(self, command):
def run(self, command, check=True):
command = command.split()
command = ['parted', '-a', 'optimal', '-s', self.disk] + command
return subprocess.check_output(command).decode('utf8')
if check:
return subprocess.check_output(command).decode('utf8')
else:
return subprocess.run(command, stdout=subprocess.PIPE).stdout.decode('utf8')
def fixup(rootdir, vols):
devbymount = {}
@@ -166,6 +176,8 @@ def fixup(rootdir, vols):
partnum = re.search('(\d+)$', targdev).group(1)
targblock = re.search('(.*)\d+$', targdev).group(1)
if targblock:
if targblock.endswith('p') and 'nvme' in targblock:
targblock = targblock[:-1]
shimpath = subprocess.check_output(['find', os.path.join(rootdir, 'boot/efi'), '-name', 'shimx64.efi']).decode('utf8').strip()
shimpath = shimpath.replace(rootdir, '/').replace('/boot/efi', '').replace('//', '/').replace('/', '\\')
subprocess.check_call(['efibootmgr', '-c', '-d', targblock, '-l', shimpath, '--part', partnum])
@@ -224,7 +236,8 @@ def install_to_disk(imgpath):
instdisk = diskin.read()
instdisk = '/dev/' + instdisk
parted = PartedRunner(instdisk)
dinfo = parted.run('unit s print')
# do this safer, unit s print might bomb
dinfo = parted.run('unit s print', check=False)
dinfo = dinfo.split('\n')
sectors = 0
sectorsize = 0
@@ -258,7 +271,7 @@ def install_to_disk(imgpath):
if end > sectors:
end = sectors
parted.run('mkpart primary {}s {}s'.format(curroffset, end))
vol['targetdisk'] = instdisk + '{0}'.format(volidx)
vol['targetdisk'] = get_partname(instdisk , volidx)
curroffset += size + 1
if not lvmvols:
if swapsize:
@@ -268,10 +281,10 @@ def install_to_disk(imgpath):
if end > sectors:
end = sectors
parted.run('mkpart swap {}s {}s'.format(curroffset, end))
subprocess.check_call(['mkswap', instdisk + '{}'.format(volidx + 1)])
subprocess.check_call(['mkswap', get_partname(instdisk, volidx + 1)])
else:
parted.run('mkpart lvm {}s 100%'.format(curroffset))
lvmpart = instdisk + '{}'.format(volidx + 1)
lvmpart = get_partname(instdisk, volidx + 1)
subprocess.check_call(['pvcreate', '-ff', '-y', lvmpart])
subprocess.check_call(['vgcreate', 'localstorage', lvmpart])
vginfo = subprocess.check_output(['vgdisplay', 'localstorage', '--units', 'b']).decode('utf8')
@@ -189,8 +189,15 @@ cat > /run/NetworkManager/system-connections/$ifname.nmconnection << EOC
EOC
echo id=${ifname} >> /run/NetworkManager/system-connections/$ifname.nmconnection
echo uuid=$(uuidgen) >> /run/NetworkManager/system-connections/$ifname.nmconnection
linktype=$(ip link |grep -A2 ${ifname}|tail -n 1|awk '{print $1}')
if [ "$linktype" = link/infiniband ]; then
linktype="infiniband"
else
linktype="ethernet"
fi
echo type=$linktype >> /run/NetworkManager/system-connections/$ifname.nmconnection
cat >> /run/NetworkManager/system-connections/$ifname.nmconnection << EOC
type=ethernet
autoconnect-retries=1
EOC
echo interface-name=$ifname >> /run/NetworkManager/system-connections/$ifname.nmconnection
@@ -199,9 +206,6 @@ multi-connect=1
permissions=
wait-device-timeout=60000
[ethernet]
mac-address-blacklist=
EOC
autoconfigmethod=$(grep ^ipv4_method: /etc/confluent/confluent.deploycfg |awk '{print $2}')
auto6configmethod=$(grep ^ipv6_method: /etc/confluent/confluent.deploycfg |awk '{print $2}')
@@ -13,6 +13,12 @@ import subprocess
bootuuid = None
def get_partname(devname, idx):
if devname[-1] in '0123456789':
return '{}p{}'.format(devname, idx)
else:
return '{}{}'.format(devname, idx)
def get_next_part_meta(img, imgsize):
if img.tell() == imgsize:
return None
@@ -202,6 +208,8 @@ def fixup(rootdir, vols):
partnum = re.search('(\d+)$', targdev).group(1)
targblock = re.search('(.*)\d+$', targdev).group(1)
if targblock:
if 'nvme' in targblock and targblock[-1] == 'p':
targblock = targblock[:-1]
shimpath = subprocess.check_output(['find', os.path.join(rootdir, 'boot/efi'), '-name', 'shimx64.efi']).decode('utf8').strip()
shimpath = shimpath.replace(rootdir, '/').replace('/boot/efi', '').replace('//', '/').replace('/', '\\')
subprocess.check_call(['efibootmgr', '-c', '-d', targblock, '-l', shimpath, '--part', partnum])
@@ -295,7 +303,7 @@ def install_to_disk(imgpath):
if end > sectors:
end = sectors
parted.run('mkpart primary {}s {}s'.format(curroffset, end))
vol['targetdisk'] = instdisk + '{0}'.format(volidx)
vol['targetdisk'] = get_partname(instdisk, volidx)
curroffset += size + 1
if not lvmvols:
if swapsize:
@@ -305,10 +313,10 @@ def install_to_disk(imgpath):
if end > sectors:
end = sectors
parted.run('mkpart swap {}s {}s'.format(curroffset, end))
subprocess.check_call(['mkswap', instdisk + '{}'.format(volidx + 1)])
subprocess.check_call(['mkswap', get_partname(instdisk, volidx + 1)])
else:
parted.run('mkpart lvm {}s 100%'.format(curroffset))
lvmpart = instdisk + '{}'.format(volidx + 1)
lvmpart = get_partname(instdisk, volidx + 1)
subprocess.check_call(['pvcreate', '-ff', '-y', lvmpart])
subprocess.check_call(['vgcreate', 'localstorage', lvmpart])
vginfo = subprocess.check_output(['vgdisplay', 'localstorage', '--units', 'b']).decode('utf8')
@@ -33,15 +33,7 @@
reboot
%packages
@^minimal-environment
#-kernel-uek # This can opt out of the UEK for the relevant distribution
bind-utils
chrony
pciutils
python3
rsync
tar
-iwl*-firmware
%include /tmp/pkglist
%include /tmp/addonpackages
%include /tmp/cryptpkglist
%end
@@ -0,0 +1,9 @@
@^minimal-environment
#-kernel-uek # This can opt out of the UEK for the relevant distribution
bind-utils
chrony
pciutils
python3
rsync
tar
-iwl*-firmware
@@ -0,0 +1,4 @@
clearpart --all --initlabel
ignoredisk --only-use %%INSTALLDISK%%
autopart --nohome %%LUKSHOOK%%
@@ -87,6 +87,7 @@ done
cryptboot=$(grep ^encryptboot: /etc/confluent/confluent.deploycfg | awk '{print $2}')
LUKSPARTY=''
touch /tmp/cryptpkglist
touch /tmp/pkglist
touch /tmp/addonpackages
if [ "$cryptboot" == "tpm2" ]; then
LUKSPARTY="--encrypted --passphrase=$(cat /etc/confluent/confluent.apikey)"
@@ -102,15 +103,18 @@ confluentpython /opt/confluent/bin/apiclient /confluent-public/os/$confluent_pro
run_remote pre.custom
run_remote_parts pre.d
confluentpython /etc/confluent/apiclient /confluent-public/os/$confluent_profile/kickstart -o /tmp/kickstart.base
if grep '^%include /tmp/pkglist' /tmp/kickstart.* > /dev/null; then
confluentpython /etc/confluent/apiclient /confluent-public/os/$confluent_profile/packagelist -o /tmp/pkglist
fi
grep '^%include /tmp/partitioning' /tmp/kickstart.* > /dev/null || touch /tmp/installdisk
if [ ! -e /tmp/installdisk ]; then
run_remote_python getinstalldisk
fi
confluentpython /etc/confluent/apiclient /confluent-public/os/$confluent_profile/partitioning -o /tmp/partitioning.template
grep '^%include /tmp/partitioning' /tmp/kickstart.* > /dev/null || rm /tmp/installdisk
if [ -e /tmp/installdisk -a ! -e /tmp/partitioning ]; then
echo clearpart --all --initlabel >> /tmp/partitioning
echo ignoredisk --only-use $(cat /tmp/installdisk) >> /tmp/partitioning
echo autopart --nohome $LUKSPARTY >> /tmp/partitioning
INSTALLDISK=$(cat /tmp/installdisk)
sed -e s/%%INSTALLDISK%%/$INSTALLDISK/ -e s/%%LUKSHOOK%%/$LUKSPARTY/ /tmp/partitioning.template > /tmp/partitioning
dd if=/dev/zero of=/dev/$(cat /tmp/installdisk) bs=1M count=1 >& /dev/null
vgchange -a n >& /dev/null
fi
@@ -154,8 +154,14 @@ cat > /run/NetworkManager/system-connections/$ifname.nmconnection << EOC
EOC
echo id=${ifname} >> /run/NetworkManager/system-connections/$ifname.nmconnection
echo uuid=$(uuidgen) >> /run/NetworkManager/system-connections/$ifname.nmconnection
linktype=$(ip link |grep -A2 ${ifname}|tail -n 1|awk '{print $1}')
if [ "$linktype" = link/infiniband ]; then
linktype="infiniband"
else
linktype="ethernet"
fi
echo type=$linktype >> /run/NetworkManager/system-connections/$ifname.nmconnection
cat >> /run/NetworkManager/system-connections/$ifname.nmconnection << EOC
type=ethernet
autoconnect-retries=1
EOC
echo interface-name=$ifname >> /run/NetworkManager/system-connections/$ifname.nmconnection
@@ -164,9 +170,6 @@ multi-connect=1
permissions=
wait-device-timeout=60000
[ethernet]
mac-address-blacklist=
EOC
autoconfigmethod=$(grep ^ipv4_method: /etc/confluent/confluent.deploycfg |awk '{print $2}')
auto6configmethod=$(grep ^ipv6_method: /etc/confluent/confluent.deploycfg |awk '{print $2}')
@@ -13,6 +13,12 @@ import subprocess
bootuuid = None
def get_partname(devname, idx):
if devname[-1] in '0123456789':
return '{}p{}'.format(devname, idx)
else:
return '{}{}'.format(devname, idx)
def get_next_part_meta(img, imgsize):
if img.tell() == imgsize:
return None
@@ -202,6 +208,8 @@ def fixup(rootdir, vols):
partnum = re.search('(\d+)$', targdev).group(1)
targblock = re.search('(.*)\d+$', targdev).group(1)
if targblock:
if targblock.endswith('p') and 'nvme' in targblock:
targblock = targblock[:-1]
shimpath = subprocess.check_output(['find', os.path.join(rootdir, 'boot/efi'), '-name', 'shimx64.efi']).decode('utf8').strip()
shimpath = shimpath.replace(rootdir, '/').replace('/boot/efi', '').replace('//', '/').replace('/', '\\')
subprocess.check_call(['efibootmgr', '-c', '-d', targblock, '-l', shimpath, '--part', partnum])
@@ -295,7 +303,7 @@ def install_to_disk(imgpath):
if end > sectors:
end = sectors
parted.run('mkpart primary {}s {}s'.format(curroffset, end))
vol['targetdisk'] = instdisk + '{0}'.format(volidx)
vol['targetdisk'] = get_partname(instdisk, volidx)
curroffset += size + 1
if not lvmvols:
if swapsize:
@@ -305,10 +313,10 @@ def install_to_disk(imgpath):
if end > sectors:
end = sectors
parted.run('mkpart swap {}s {}s'.format(curroffset, end))
subprocess.check_call(['mkswap', instdisk + '{}'.format(volidx + 1)])
subprocess.check_call(['mkswap', get_partname(instdisk, volidx + 1)])
else:
parted.run('mkpart lvm {}s 100%'.format(curroffset))
lvmpart = instdisk + '{}'.format(volidx + 1)
lvmpart = get_partname(instdisk, volidx + 1)
subprocess.check_call(['pvcreate', '-ff', '-y', lvmpart])
subprocess.check_call(['vgcreate', 'localstorage', lvmpart])
vginfo = subprocess.check_output(['vgdisplay', 'localstorage', '--units', 'b']).decode('utf8')
@@ -13,6 +13,13 @@ import subprocess
bootuuid = None
def get_partname(devname, idx):
if devname[-1] in '0123456789':
return '{}p{}'.format(devname, idx)
else:
return '{}{}'.format(devname, idx)
def get_next_part_meta(img, imgsize):
if img.tell() == imgsize:
return None
@@ -292,7 +299,7 @@ def install_to_disk(imgpath):
if end > sectors:
end = sectors
parted.run('mkpart primary {}s {}s'.format(curroffset, end))
vol['targetdisk'] = instdisk + '{0}'.format(volidx)
vol['targetdisk'] = get_partname(instdisk, volidx)
curroffset += size + 1
if not lvmvols:
if swapsize:
@@ -302,10 +309,10 @@ def install_to_disk(imgpath):
if end > sectors:
end = sectors
parted.run('mkpart swap {}s {}s'.format(curroffset, end))
subprocess.check_call(['mkswap', instdisk + '{}'.format(volidx + 1)])
subprocess.check_call(['mkswap', get_partname(instdisk, volidx + 1)])
else:
parted.run('mkpart lvm {}s 100%'.format(curroffset))
lvmpart = instdisk + '{}'.format(volidx + 1)
lvmpart = get_partname(instdisk, volidx + 1)
subprocess.check_call(['pvcreate', '-ff', '-y', lvmpart])
subprocess.check_call(['vgcreate', 'localstorage', lvmpart])
vginfo = subprocess.check_output(['vgdisplay', 'localstorage', '--units', 'b']).decode('utf8')
@@ -0,0 +1,29 @@
Ansible playbooks ending in .yml or .yaml that are placed into this directory will be executed at the
appropriate phase of the install process.
Alternatively, plays may be placed in /var/lib/confluent/private/os/<profilename>/ansible/<directory>.
This prevents public clients from being able to read the plays, which is not necessary for them to function,
and may protect them from divulging material contained in the plays or associated roles.
The 'hosts' may be omitted, and if included will be ignored, replaced with the host that is specifically
requesting the playbooks be executed.
Also, the playbooks will be executed on the deployment server. Hence it may be slower in aggregate than
running content under scripts/ which ask much less of the deployment server
Here is an example of what a playbook would look like broadly:
- name: Example
gather_facts: no
tasks:
- name: Example1
lineinfile:
path: /etc/hosts
line: 1.2.3.4 test1
create: yes
- name: Example2
lineinfile:
path: /etc/hosts
line: 1.2.3.5 test2
create: yes
@@ -0,0 +1,29 @@
Ansible playbooks ending in .yml or .yaml that are placed into this directory will be executed at the
appropriate phase of the install process.
Alternatively, plays may be placed in /var/lib/confluent/private/os/<profilename>/ansible/<directory>.
This prevents public clients from being able to read the plays, which is not necessary for them to function,
and may protect them from divulging material contained in the plays or associated roles.
The 'hosts' may be omitted, and if included will be ignored, replaced with the host that is specifically
requesting the playbooks be executed.
Also, the playbooks will be executed on the deployment server. Hence it may be slower in aggregate than
running content under scripts/ which ask much less of the deployment server
Here is an example of what a playbook would look like broadly:
- name: Example
gather_facts: no
tasks:
- name: Example1
lineinfile:
path: /etc/hosts
line: 1.2.3.4 test1
create: yes
- name: Example2
lineinfile:
path: /etc/hosts
line: 1.2.3.5 test2
create: yes
@@ -2,7 +2,10 @@
echo "Confluent first boot is running"
HOME=$(getent passwd $(whoami)|cut -d: -f 6)
export HOME
seems a potentially relevant thing to put i... by Jarrod Johnson
(
exec >> /target/var/log/confluent/confluent-firstboot.log
exec 2>> /target/var/log/confluent/confluent-firstboot.log
chmod 600 /target/var/log/confluent/confluent-firstboot.log
cp -a /etc/confluent/ssh/* /etc/ssh/
systemctl restart sshd
rootpw=$(grep ^rootpassword: /etc/confluent/confluent.deploycfg |awk '{print $2}')
@@ -18,7 +21,10 @@ done
hostnamectl set-hostname $(grep ^NODENAME: /etc/confluent/confluent.info | awk '{print $2}')
touch /etc/cloud/cloud-init.disabled
source /etc/confluent/functions
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
export confluent_mgr confluent_profile
run_remote_parts firstboot.d
run_remote_config firstboot.d
curl --capath /etc/confluent/tls -f -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" -X POST -d "status: complete" https://$confluent_mgr/confluent-api/self/updatestatus
) &
tail --pid $! -n 0 -F /target/var/log/confluent/confluent-post.log > /dev/console
@@ -8,7 +8,6 @@ chmod go-rwx /etc/confluent/*
for i in /custom-installation/ssh/*.ca; do
echo '@cert-authority *' $(cat $i) >> /target/etc/ssh/ssh_known_hosts
done
cp -a /etc/ssh/ssh_host* /target/etc/confluent/ssh/
cp -a /etc/ssh/sshd_config.d/confluent.conf /target/etc/confluent/ssh/sshd_config.d/
sshconf=/target/etc/ssh/ssh_config
@@ -19,10 +18,15 @@ echo 'Host *' >> $sshconf
echo ' HostbasedAuthentication yes' >> $sshconf
echo ' EnableSSHKeysign yes' >> $sshconf
echo ' HostbasedKeyTypes *ed25519*' >> $sshconf
cp /etc/confluent/functions /target/etc/confluent/functions
source /etc/confluent/functions
mkdir -p /target/var/log/confluent
cp /var/log/confluent/* /target/var/log/confluent/
(
exec >> /target/var/log/confluent/confluent-post.log
exec 2>> /target/var/log/confluent/confluent-post.log
chmod 600 /target/var/log/confluent/confluent-post.log
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/firstboot.sh > /target/etc/confluent/firstboot.sh
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/functions > /target/etc/confluent/functions
source /target/etc/confluent/functions
chmod +x /target/etc/confluent/firstboot.sh
cp /tmp/allnodes /target/root/.shosts
cp /tmp/allnodes /target/etc/ssh/shosts.equiv
@@ -83,6 +87,8 @@ chroot /target bash -c "source /etc/confluent/functions; run_remote_parts post.d
source /target/etc/confluent/functions
run_remote_config post
python3 /opt/confluent/bin/apiclient /confluent-api/self/updatestatus -d 'status: staged'
umount /target/sys /target/dev /target/proc
) &
tail --pid $! -n 0 -F /target/var/log/confluent/confluent-post.log > /dev/console
@@ -1,5 +1,16 @@
#!/bin/bash
deploycfg=/custom-installation/confluent/confluent.deploycfg
mkdir -p /var/log/confluent
mkdir -p /opt/confluent/bin
mkdir -p /etc/confluent
cp /custom-installation/confluent/confluent.info /custom-installation/confluent/confluent.apikey /etc/confluent/
cat /custom-installation/tls/*.pem >> /etc/confluent/ca.pem
cp /custom-installation/confluent/bin/apiclient /opt/confluent/bin
cp $deploycfg /etc/confluent/
(
exec >> /var/log/confluent/confluent-pre.log
exec 2>> /var/log/confluent/confluent-pre.log
chmod 600 /var/log/confluent/confluent-pre.log
cryptboot=$(grep encryptboot: $deploycfg|sed -e 's/^encryptboot: //')
if [ "$cryptboot" != "" ] && [ "$cryptboot" != "none" ] && [ "$cryptboot" != "null" ]; then
@@ -23,7 +34,16 @@ echo HostbasedAuthentication yes >> /etc/ssh/sshd_config.d/confluent.conf
echo HostbasedUsesNameFromPacketOnly yes >> /etc/ssh/sshd_config.d/confluent.conf
echo IgnoreRhosts no >> /etc/ssh/sshd_config.d/confluent.conf
systemctl restart sshd
mkdir -p /etc/confluent
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/functions > /etc/confluent/functions
. /etc/confluent/functions
run_remote_parts pre.d
curl -f -X POST -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" https://$confluent_mgr/confluent-api/self/nodelist > /tmp/allnodes
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/getinstalldisk > /custom-installation/getinstalldisk
python3 /custom-installation/getinstalldisk
if [ ! -e /tmp/installdisk ]; then
curl -f https://$confluent_mgr/confluent-public/os/$confluent_profile/scripts/getinstalldisk > /custom-installation/getinstalldisk
python3 /custom-installation/getinstalldisk
fi
sed -i s!%%INSTALLDISK%%!/dev/$(cat /tmp/installdisk)! /autoinstall.yaml
) &
tail --pid $! -n 0 -F /var/log/confluent/confluent-pre.log > /dev/console
+8 -1
View File
@@ -36,7 +36,7 @@ if [ "$OPKGNAME" = "confluent-server" ]; then
if grep wheezy /etc/os-release; then
sed -i 's/^\(Depends:.*\)/\1, python-confluent-client, python-lxml, python-eficompressor, python-pycryptodomex, python-dateutil, python-pyopenssl, python-msgpack/' debian/control
else
sed -i 's/^\(Depends:.*\)/\1, confluent-client, python3-lxml, python3-eficompressor, python3-pycryptodome, python3-websocket, python3-msgpack, python3-eventlet, python3-pyparsing, python3-pyghmi, python3-paramiko/' debian/control
sed -i 's/^\(Depends:.*\)/\1, confluent-client, python3-lxml, python3-eficompressor, python3-pycryptodome, python3-websocket, python3-msgpack, python3-eventlet, python3-pyparsing, python3-pyghmi, python3-paramiko, python3-pysnmp4, python3-libarchive-c, confluent-vtbufferd/' debian/control
fi
if grep wheezy /etc/os-release; then
echo 'confluent_client python-confluent-client' >> debian/pydist-overrides
@@ -49,6 +49,13 @@ if ! grep wheezy /etc/os-release; then
fi
head -n -1 debian/control > debian/control1
mv debian/control1 debian/control
cat > debian/postinst << EOF
if ! getent passwd confluent > /dev/null; then
useradd -r confluent -d /var/lib/confluent -s /usr/sbin/nologin
mkdir -p /etc/confluent
chown confluent /etc/confluent
fi
EOF
echo 'export PYBUILD_INSTALL_ARGS=--install-lib=/opt/confluent/lib/python' >> debian/rules
#echo 'Provides: python-'$DPKGNAME >> debian/control
#echo 'Conflicts: python-'$DPKGNAME >> debian/control
@@ -259,6 +259,9 @@ def get_myname():
mycachedname[1] = time.time()
return myname
def in_collective():
return bool(list(cfm.list_collective()))
def handle_connection(connection, cert, request, local=False):
global currentleader
global retrythread
@@ -713,6 +716,7 @@ def become_leader(connection):
if reassimilate is not None:
reassimilate.kill()
reassimilate = eventlet.spawn(reassimilate_missing)
cfm._ready = True
if _assimilate_missing(skipaddr):
schedule_rebalance()
@@ -371,7 +371,7 @@ node = {
'the managed node. If not specified, then console '
'is disabled. "ipmi" should be specified for most '
'systems if console is desired.'),
'validvalues': ('ssh', 'ipmi', 'tsmsol'),
'validvalues': ('ssh', 'ipmi', 'openbmc', 'tsmsol'),
},
# 'virtualization.host': {
# 'description': ('Hypervisor where this node does/should reside'),
@@ -119,6 +119,7 @@ _cfgstore = None
_pendingchangesets = {}
_txcount = 0
_hasquorum = True
_ready = False
_attraliases = {
'bmc': 'hardwaremanagement.manager',
@@ -830,6 +831,9 @@ _oldcfgstore = None
_oldtxcount = 0
def config_is_ready():
return _ready
def rollback_clear():
global _cfgstore
global _txcount
@@ -847,6 +851,8 @@ def clear_configuration():
global _txcount
global _oldcfgstore
global _oldtxcount
global _ready
_ready = False
stop_leading()
stop_following()
_oldcfgstore = _cfgstore
@@ -857,6 +863,7 @@ def clear_configuration():
def commit_clear():
global _oldtxcount
global _oldcfgstore
global _ready
# first, copy over old non-key globals, as those are
# currently defined as local to each collective member
# currently just 'autosense' which is intended to be active
@@ -876,6 +883,7 @@ def commit_clear():
pass
ConfigManager.wait_for_sync(True)
ConfigManager._bg_sync_to_file()
_ready = True
cfgleader = None
@@ -1273,6 +1281,7 @@ class ConfigManager(object):
def __init__(self, tenant, decrypt=False, username=None):
self.clientfiles = {}
global _cfgstore
self.inrestore = False
with _initlock:
if _cfgstore is None:
init()
@@ -2089,6 +2098,10 @@ class ConfigManager(object):
def _notif_attribwatchers(self, nodeattrs):
if self.tenant not in self._attribwatchers:
return
if self.inrestore:
# Do not stir up attribute watchers during a collective join or DB restore,
# it's too hectic of a time to react
return
notifdata = {}
attribwatchers = self._attribwatchers[self.tenant]
for node in nodeattrs:
@@ -2471,6 +2484,13 @@ class ConfigManager(object):
#TODO: wait for synchronization to suceed/fail??)
def _load_from_json(self, jsondata, sync=True):
self.inrestore = True
try:
self._load_from_json_backend(jsondata, sync=True)
finally:
self.inrestore = False
def _load_from_json_backend(self, jsondata, sync=True):
"""Load fresh configuration data from jsondata
:param jsondata: String of jsondata
@@ -2939,9 +2959,9 @@ def get_globals():
bkupglobals[globvar] = _cfgstore['globals'][globvar]
return bkupglobals
def init(stateless=False):
global _cfgstore
global _ready
if stateless:
_cfgstore = {}
return
@@ -2949,6 +2969,9 @@ def init(stateless=False):
ConfigManager._read_from_path()
except IOError:
_cfgstore = {}
members = list(list_collective())
if len(members) < 2:
_ready = True
if __name__ == '__main__':
+22 -4
View File
@@ -648,6 +648,8 @@ def detected_models():
def _recheck_nodes(nodeattribs, configmanager):
if not cfm.config_is_ready():
return
if rechecklock.locked():
# if already in progress, don't run again
# it may make sense to schedule a repeat, but will try the easier and less redundant way first
@@ -766,6 +768,9 @@ def eval_detected(info):
def detected(info):
global rechecker
global rechecktime
if not cfm.config_is_ready():
# drop processing of discovery data while configmanager is 'down'
return
# later, manual and CMM discovery may act on SN and/or UUID
for service in info['services']:
if service in nodehandlers:
@@ -1429,7 +1434,12 @@ def discover_node(cfg, handler, info, nodename, manual):
newnodeattribs['pubkeys.tls_hardwaremanager'] = \
util.get_fingerprint(handler.https_cert, 'sha256')
if newnodeattribs:
cfg.set_node_attributes({nodename: newnodeattribs})
currattrs = cfg.get_node_attributes(nodename, newnodeattribs)
for checkattr in newnodeattribs:
checkval = currattrs.get(nodename, {}).get(checkattr, {}).get('value', None)
if checkval != newnodeattribs[checkattr]:
cfg.set_node_attributes({nodename: newnodeattribs})
break
log.log({'info': 'Discovered {0} ({1})'.format(nodename,
handler.devname)})
if nodeconfig:
@@ -1508,7 +1518,12 @@ def do_pxe_discovery(cfg, handler, info, manual, nodename, policies):
if info['hwaddr'] != oldhwaddr:
attribs[newattrname] = info['hwaddr']
if attribs:
cfg.set_node_attributes({nodename: attribs})
currattrs = cfg.get_node_attributes(nodename, attribs)
for checkattr in attribs:
checkval = currattrs.get(nodename, {}).get(checkattr, {}).get('value', None)
if checkval != attribs[checkattr]:
cfg.set_node_attributes({nodename: attribs})
break
if info['uuid'] in known_pxe_uuids:
return True
if uuid_is_valid(info['uuid']):
@@ -1597,7 +1612,10 @@ def remotescan():
mycfm = cfm.ConfigManager(None)
myname = collective.get_myname()
for remagent in get_subscriptions():
affluent.renotify_me(remagent, mycfm, myname)
try:
affluent.renotify_me(remagent, mycfm, myname)
except Exception as e:
log.log({'error': 'Unexpected problem asking {} for discovery notifications'.format(remagent)})
def blocking_scan():
@@ -1637,7 +1655,7 @@ def start_autosense():
autosensors.add(eventlet.spawn(slp.snoop, safe_detected, slp))
#autosensors.add(eventlet.spawn(mdns.snoop, safe_detected, mdns))
autosensors.add(eventlet.spawn(pxe.snoop, safe_detected, pxe, get_node_guess_by_uuid))
remotescan()
eventlet.spawn(remotescan)
nodes_by_fprint = {}
+1 -1
View File
@@ -326,7 +326,7 @@ def run(args):
break
except Exception:
eventlet.sleep(0.5)
disco.start_detection()
eventlet.spawn_n(disco.start_detection)
eventlet.sleep(1)
consoleserver.start_console_sessions()
while 1:
+36
View File
@@ -332,6 +332,7 @@ def get_full_net_config(configmanager, node, serverip=None):
if serverip:
myaddrs = get_addresses_by_serverip(serverip)
nm = NetManager(myaddrs, node, configmanager)
defaultnic = {}
if None in attribs:
nm.process_attribs(None, attribs[None])
del attribs[None]
@@ -342,9 +343,44 @@ def get_full_net_config(configmanager, node, serverip=None):
retattrs['default'] = nm.myattribs[None]
add_netmask(retattrs['default'])
del nm.myattribs[None]
else:
nnc = get_nic_config(configmanager, node, serverip=serverip)
if nnc.get('ipv4_address', None):
defaultnic['ipv4_address'] = '{}/{}'.format(nnc['ipv4_address'], nnc['prefix'])
if nnc.get('ipv4_gateway', None):
defaultnic['ipv4_gateway'] = nnc['ipv4_gateway']
if nnc.get('ipv4_method', None):
defaultnic['ipv4_method'] = nnc['ipv4_method']
if nnc.get('ipv6_address', None):
defaultnic['ipv6_address'] = '{}/{}'.format(nnc['ipv6_address'], nnc['ipv6_prefix'])
if nnc.get('ipv6_method', None):
defaultnic['ipv6_method'] = nnc['ipv6_method']
retattrs['extranets'] = nm.myattribs
for attri in retattrs['extranets']:
add_netmask(retattrs['extranets'][attri])
if retattrs['extranets'][attri].get('ipv4_address', None) == defaultnic.get('ipv4_address', 'NOPE'):
defaultnic = {}
if retattrs['extranets'][attri].get('ipv6_address', None) == defaultnic.get('ipv6_address', 'NOPE'):
defaultnic = {}
if defaultnic:
retattrs['default'] = defaultnic
add_netmask(retattrs['default'])
ipv4addr = defaultnic.get('ipv4_address', None)
if ipv4addr and '/' in ipv4addr:
ipv4bytes = socket.inet_pton(socket.AF_INET, ipv4addr.split('/')[0])
for addr in nm.myaddrs:
if addr[0] != socket.AF_INET:
continue
if ipn_on_same_subnet(addr[0], addr[1], ipv4bytes, addr[2]):
defaultnic['current_nic'] = True
ipv6addr = defaultnic.get('ipv6_address', None)
if ipv6addr and '/' in ipv6addr:
ipv6bytes = socket.inet_pton(socket.AF_INET6, ipv6addr.split('/')[0])
for addr in nm.myaddrs:
if addr[0] != socket.AF_INET6:
continue
if ipn_on_same_subnet(addr[0], addr[1], ipv6bytes, addr[2]):
defaultnic['current_nic'] = True
return retattrs
@@ -49,10 +49,11 @@ import eventlet.green.select as select
import eventlet.green.socket as socket
import confluent.collective.manager as collective
import confluent.exceptions as exc
import confluent.log as log
import confluent.messages as msg
import confluent.noderange as noderange
import confluent.util as util
from eventlet.greenpool import GreenPool
import eventlet.green.subprocess as subprocess
@@ -502,10 +503,21 @@ def _full_updatemacmap(configmanager):
'Network topology not available to tenants')
# here's a list of switches... need to add nodes that are switches
nodelocations = configmanager.get_node_attributes(
configmanager.list_nodes(), ('type', 'net*.switch', 'net*.switchport'))
configmanager.list_nodes(), ('type', 'collective.managercandidates', 'net*.switch', 'net*.switchport'))
switches = set([])
incollective = collective.in_collective()
if incollective:
mycollectivename = collective.get_myname()
for node in nodelocations:
cfg = nodelocations[node]
if incollective:
candmgrs = cfg.get('collective.managercandidates', {}).get('value', None)
if candmgrs:
candmgrs = noderange.NodeRange(candmgrs, configmanager).nodes
if mycollectivename not in candmgrs:
# do not think about trying to find nodes that we aren't possibly
# supposed to be a manager for in a collective
continue
if cfg.get('type', {}).get('value', None) == 'switch':
switches.add(node)
for attr in cfg:
@@ -0,0 +1,160 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2015-2019 Lenovo
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This plugin provides an ssh implementation comforming to the 'console'
# specification. consoleserver or shellserver would be equally likely
# to use this.
import confluent.exceptions as cexc
import confluent.interface.console as conapi
import confluent.log as log
import confluent.util as util
import pyghmi.exceptions as pygexc
import pyghmi.redfish.command as rcmd
import pyghmi.util.webclient as webclient
import eventlet
import eventlet.green.ssl as ssl
try:
websocket = eventlet.import_patched('websocket')
wso = websocket.WebSocket
except Exception:
wso = object
def get_conn_params(node, configdata):
if 'secret.hardwaremanagementuser' in configdata:
username = configdata['secret.hardwaremanagementuser']['value']
else:
username = 'USERID'
if 'secret.hardwaremanagementpassword' in configdata:
passphrase = configdata['secret.hardwaremanagementpassword']['value']
else:
passphrase = 'PASSW0RD' # for lack of a better guess
if 'hardwaremanagement.manager' in configdata:
bmc = configdata['hardwaremanagement.manager']['value']
else:
bmc = node
bmc = bmc.split('/', 1)[0]
return {
'username': username,
'passphrase': passphrase,
'bmc': bmc,
}
_configattributes = ('secret.hardwaremanagementuser',
'secret.hardwaremanagementpassword',
'hardwaremanagement.manager')
class WrappedWebSocket(wso):
def set_verify_callback(self, callback):
self._certverify = callback
def connect(self, url, **options):
add_tls = url.startswith('wss://')
if add_tls:
hostname, port, resource, _ = websocket._url.parse_url(url)
if hostname[0] != '[' and ':' in hostname:
hostname = '[{0}]'.format(hostname)
if resource[0] != '/':
resource = '/{0}'.format(resource)
url = 'ws://{0}:443{1}'.format(hostname,resource)
else:
return super(WrappedWebSocket, self).connect(url, **options)
self.sock_opt.timeout = options.get('timeout', self.sock_opt.timeout)
self.sock, addrs = websocket._http.connect(url, self.sock_opt, websocket._http.proxy_info(**options),
options.pop('socket', None))
self.sock = ssl.wrap_socket(self.sock, cert_reqs=ssl.CERT_NONE)
# The above is supersedeed by the _certverify, which provides
# known-hosts style cert validaiton
bincert = self.sock.getpeercert(binary_form=True)
if not self._certverify(bincert):
raise pygexc.UnrecognizedCertificate('Unknown certificate', bincert)
try:
self.handshake_response = websocket._handshake.handshake(self.sock, *addrs, **options)
if self.handshake_response.status in websocket._handshake.SUPPORTED_REDIRECT_STATUSES:
options['redirect_limit'] = options.pop('redirect_limit', 3) - 1
if options['redirect_limit'] < 0:
raise Exception('Redirect limit hit')
url = self.handshake_response.headers['location']
self.sock.close()
return self.connect(url, **options)
self.connected = True
except:
if self.sock:
self.sock.close()
self.sock = None
raise
class TsmConsole(conapi.Console):
def __init__(self, node, config):
self.node = node
self.ws = None
configdata = config.get_node_attributes([node], _configattributes, decrypt=True)
connparams = get_conn_params(node, configdata[node])
self.username = connparams['username']
self.password = connparams['passphrase']
self.bmc = connparams['bmc']
self.origbmc = connparams['bmc']
if ':' in self.bmc:
self.bmc = '[{0}]'.format(self.bmc)
self.datacallback = None
self.nodeconfig = config
self.connected = False
def recvdata(self):
while self.connected:
pendingdata = self.ws.recv()
if pendingdata == '':
self.datacallback(conapi.ConsoleEvent.Disconnect)
return
self.datacallback(pendingdata)
def connect(self, callback):
self.datacallback = callback
kv = util.TLSCertVerifier(
self.nodeconfig, self.node, 'pubkeys.tls_hardwaremanager').verify_cert
wc = webclient.SecureHTTPConnection(self.origbmc, 443, verifycallback=kv)
rsp = wc.grab_json_response_with_status('/login', {'data': [self.username.decode('utf8'), self.password.decode("utf8")]}, headers={'Content-Type': 'application/json'})
bmc = self.bmc
if '%' in self.bmc:
prefix = self.bmc.split('%')[0]
bmc = prefix + ']'
self.ws = WrappedWebSocket(host=bmc)
self.ws.set_verify_callback(kv)
self.ws.connect('wss://{0}/console0'.format(self.bmc), host=bmc, cookie='XSRF-TOKEN={0}; SESSION={1}'.format(wc.cookies['XSRF-TOKEN'], wc.cookies['SESSION']))
self.connected = True
eventlet.spawn_n(self.recvdata)
return
def write(self, data):
self.ws.send(data)
def close(self):
if self.ws:
self.ws.close()
self.connected = False
self.datacallback = None
def create(nodes, element, configmanager, inputdata):
if len(nodes) == 1:
return TsmConsole(nodes[0], configmanager)
@@ -210,10 +210,12 @@ def xml2stateinfo(statdata):
stateinfo = []
sensornames = sorted([x.tag for x in statdata])
themodel = None
for model in sensorsbymodel:
if sensorsbymodel[model] == sensornames:
for model in sorted(sensorsbymodel):
if all([x in sensornames for x in sensorsbymodel[model]]):
themodel = model
break
else:
print(repr(sensornames))
thesensors = _thesensors[themodel]
#['mode', 't1', 't2a', 't2b', 't2c', 't2', 't5', 't3', 't4', 'dw', 't3', 'rh', 'setpoint', 'secflow', 'primflow', 'ps1', 'ps1a', 'ps1b', 'ps2', 'ps3', 'ps4', 'ps5a', 'ps5b', 'ps5c', 'sdp', 'valve', 'valve2', 'pumpspeed1', 'pumpspeed2', 'pumpspeed3', 'alarms', 'dt', 'p3state', 'duty']
for tagname in thesensors:
+2
View File
@@ -141,6 +141,8 @@ def sessionhdl(connection, authname, skipauth=False, cert=None):
if 'collective' in response:
return collective.handle_connection(connection, cert,
response['collective'])
while not configmanager.config_is_ready():
eventlet.sleep(1)
if 'dispatch' in response:
dreq = tlvdata.recvall(connection, response['dispatch']['length'])
return pluginapi.handle_dispatch(connection, cert, dreq,
+59 -11
View File
@@ -23,7 +23,10 @@ import subprocess
import sys
import tempfile
import time
import yaml
try:
import yaml
except ImportError:
pass
path = os.path.dirname(os.path.realpath(__file__))
path = os.path.realpath(os.path.join(path, '..', 'lib', 'python'))
if path.startswith('/opt'):
@@ -196,7 +199,13 @@ def capture_remote(args):
finfo = subprocess.check_output(['ssh', targ, 'python3', '/run/imgutil/capenv/imgutil', 'getfingerprint']).decode('utf8')
finfo = json.loads(finfo)
if finfo['oscategory'] not in ('el8', 'el9', 'ubuntu20.04', 'ubuntu22.04'):
raise Exception('Not yet supported for capture: ' + repr(finfo))
sys.stderr.write('Not yet supported for capture: ' + repr(finfo) + '\n')
sys.exit(1)
unmet = finfo.get('unmetprereqs', [])
if unmet:
for cmd in unmet:
sys.stderr.write(cmd + '\n')
sys.exit(1)
oscat = finfo['oscategory']
subprocess.check_call(['ssh', '-o', 'LogLevel=QUIET', '-t', targ, 'python3', '/run/imgutil/capenv/imgutil', 'capturelocal'])
utillib = __file__.replace('bin/imgutil', 'lib/imgutil')
@@ -437,10 +446,12 @@ def get_mydir(oscategory):
class OsHandler(object):
def __init__(self, name, version, arch, args):
self.name = name
self._interactive = True
self.version = version
self.arch = arch
self.sourcepath = None
self.osname = '{}-{}-{}'.format(name, version, arch)
self.captureprereqs = []
try:
pkglist = args.packagelist
except AttributeError:
@@ -464,13 +475,16 @@ class OsHandler(object):
except AttributeError:
self.addrepos = []
def set_interactive(self, shouldbeinteractive):
self._interactive = shouldbeinteractive
def get_json(self):
odata = [self.oscategory, self.version, self.arch, self.name]
for idx in range(len(odata)):
if not isinstance(odata[idx], str):
odata[idx] = odata[idx].decode('utf8')
info = {'oscategory': odata[0],
'version': odata[1], 'arch': odata[2], 'name': odata[3]}
'version': odata[1], 'arch': odata[2], 'name': odata[3], 'unmetprereqs': self.captureprereqs}
return json.dumps(info)
def prep_root_premount(self, args):
@@ -577,7 +591,10 @@ class SuseHandler(OsHandler):
cmd = ['chmod', 'a+x']
cmd.extend(glob.glob(os.path.join(targdir, '*')))
subprocess.check_call(cmd)
subprocess.check_call(['zypper', '-R', self.targpath, 'install'] + self.zyppargs)
if self._interactive:
subprocess.check_call(['zypper', '-R', self.targpath, 'install'] + self.zyppargs)
else:
subprocess.check_call(['zypper', '-n', '-R', self.targpath, 'install'] + self.zyppargs)
os.symlink('/usr/lib/systemd/system/sshd.service', os.path.join(self.targpath, 'etc/systemd/system/multi-user.target.wants/sshd.service'))
if os.path.exists(os.path.join(self.targpath, 'sbin/mkinitrd')):
args.cmd = ['mkinitrd']
@@ -587,12 +604,21 @@ class SuseHandler(OsHandler):
class DebHandler(OsHandler):
def __init__(self, name, version, arch, args, codename):
def __init__(self, name, version, arch, args, codename, hostpath):
self.includepkgs = []
self.targpath = None
self.codename = codename
self.oscategory = name + version
super().__init__(name, version, arch, args)
needpkgs = []
if not os.path.exists(os.path.join(hostpath, 'usr/bin/tpm2_getcap')):
needpkgs.append('tpm2-tools')
lfuses = glob.glob(os.path.join(hostpath, '/lib/*/libfuse.so.2'))
if not lfuses:
needpkgs.append('libfuse2')
if needpkgs:
needapt = 'Missing packages needed in target for capture, to add required packages: apt install ' + ' '.join(needpkgs)
self.captureprereqs.append(needapt)
def add_pkglists(self):
self.includepkgs.extend(self.list_packages())
@@ -620,11 +646,27 @@ class DebHandler(OsHandler):
class ElHandler(OsHandler):
def __init__(self, name, version, arch, args):
def __init__(self, name, version, arch, args, hostpath='/'):
self.oscategory = 'el{0}'.format(version.split('.')[0])
self.yumargs = []
super().__init__(name, version, arch, args)
needpkgs = []
if not hostpath:
return
if not os.path.exists(os.path.join(hostpath, 'usr/bin/tpm2_getcap')):
needpkgs.append('tpm2-tools')
lfuses = glob.glob(os.path.join(hostpath, '/usr/lib64/libfuse.so.2'))
if not lfuses:
needpkgs.append('fuse-libs')
if not os.path.exists(os.path.join(hostpath, '/usr/bin/ipcalc')):
needpkgs.append('ipcalc')
if not os.path.exists(os.path.join(hostpath, 'usr/sbin/dhclient')):
needpkgs.append('dhcp-client')
if not os.path.exists(os.path.join(hostpath, 'usr/sbin/mount.nfs')):
needpkgs.append('nfs-utils')
if needpkgs:
needapt = 'Missing packages needed in target for capture, to add required packages: dnf install ' + ' '.join(needpkgs)
self.captureprereqs.append(needapt)
def add_pkglists(self):
self.yumargs.extend(self.list_packages())
@@ -657,7 +699,10 @@ class ElHandler(OsHandler):
cmd = ['chmod', 'a+x']
cmd.extend(glob.glob(os.path.join(targdir, '*')))
subprocess.check_call(cmd)
subprocess.check_call(['yum'] + self.yumargs)
if self._interactive:
subprocess.check_call(['yum'] + self.yumargs)
else:
subprocess.check_call(['yum', '-y'] + self.yumargs)
with open('/proc/mounts') as mountinfo:
for line in mountinfo.readlines():
if line.startswith('selinuxfs '):
@@ -794,6 +839,7 @@ def main():
buildp.add_argument('-a', '--addpackagelist', action='append', default=[],
help='A list of additional packages to include, may be specified multiple times')
buildp.add_argument('-s', '--source', help='Directory to pull installation from, typically a subdirectory of /var/lib/confluent/distributions. By default, the repositories for the build system are used.')
buildp.add_argument('-y', '--non-interactive', help='Avoid prompting for confirmation', action='store_true')
buildp.add_argument('-v', '--volume',
help='Directory to make available in the build environment. -v / will '
'cause it to be mounted in image as /run/external/, -v /:/run/root '
@@ -996,7 +1042,7 @@ def fingerprint_source_el(files, sourcepath, args):
if arch == 'noarch':
prodinfo = open(os.path.join(sourcepath, '.discinfo')).read()
arch = prodinfo.split('\n')[2]
return ElHandler(osname, ver, arch, args)
return ElHandler(osname, ver, arch, args, None)
return None
@@ -1048,7 +1094,7 @@ def fingerprint_host_el(args, hostpath='/'):
osname = osname.replace('-release', '').replace('-', '_')
if osname == 'centos_linux':
osname = 'centos'
return ElHandler(osname, version, os.uname().machine, args)
return ElHandler(osname, version, os.uname().machine, args, hostpath)
def fingerprint_host_deb(args, hostpath='/'):
@@ -1072,7 +1118,7 @@ def fingerprint_host_deb(args, hostpath='/'):
except IOError:
pass
if osname:
return DebHandler(osname, vers, os.uname().machine, args, codename)
return DebHandler(osname, vers, os.uname().machine, args, codename, hostpath)
def fingerprint_host_suse(args, hostpath='/'):
@@ -1128,6 +1174,8 @@ def build_root(args):
sys.stderr.write(
'Unable to recognize build system os\n')
sys.exit(1)
if args.non_interactive:
oshandler.set_interactive(False)
oshandler.set_target(args.scratchdir)
oshandler.add_pkglists()
for dirname in ('proc', 'sys', 'dev', 'run'):