mirror of
https://github.com/xcat2/confluent.git
synced 2026-06-08 08:38:33 +00:00
Merge branch 'master' into async
Need to rework httpapi further for changes to the firmware staging.
This commit is contained in:
@@ -961,8 +961,9 @@ def main():
|
||||
except IOError:
|
||||
pass
|
||||
if powerstate is None or powertime < time.time() - 10: # Check powerstate every 10 seconds
|
||||
if powerstate == None:
|
||||
powerstate = True
|
||||
powertime = time.time()
|
||||
powerstate = True
|
||||
check_power_state()
|
||||
else:
|
||||
currcommand = prompt()
|
||||
|
||||
@@ -67,7 +67,7 @@ argparser.add_option('-n', '--numreadings', type='int',
|
||||
argparser.add_option('-c', '--csv', action='store_true',
|
||||
help='Output in CSV format')
|
||||
argparser.add_option('-s', '--skipnumberless', action='store_true',
|
||||
help='Output in CSV format')
|
||||
help='Do not show non-numeric sensors')
|
||||
(options, args) = argparser.parse_args()
|
||||
repeatmode = False
|
||||
if options.interval:
|
||||
|
||||
@@ -171,7 +171,9 @@ class GroupedData(object):
|
||||
self.byoutput[outdata])))
|
||||
currout += '\n====================================\n'
|
||||
currout += outdata
|
||||
currout += '\n\n'
|
||||
if currout[-1] != '\n':
|
||||
currout += '\n'
|
||||
currout += '\n'
|
||||
output.write(currout)
|
||||
output.flush()
|
||||
|
||||
@@ -211,7 +213,9 @@ class GroupedData(object):
|
||||
else:
|
||||
currout += '\n'.join(colordiff(modaloutput.split('\n'),
|
||||
outdata.split('\n')))
|
||||
currout += '\n\n'
|
||||
if currout[-1] != '\n':
|
||||
currout += '\n'
|
||||
currout += '\n'
|
||||
if reverse:
|
||||
revoutput.append(currout)
|
||||
else:
|
||||
|
||||
@@ -226,7 +226,11 @@ class WickedManager(object):
|
||||
self.cfgbydev[devname] = currcfg
|
||||
for cfg in open(ifcfg).read().splitlines():
|
||||
cfg = cfg.split('#', 1)[0]
|
||||
kv = ' '.join(shlex.split(cfg)).split('=', 1)
|
||||
try:
|
||||
kv = ' '.join(shlex.split(cfg)).split('=', 1)
|
||||
except Exception:
|
||||
# unparseable line, likely having something we can't handle
|
||||
del self.cfgbydev[devname]
|
||||
if len(kv) != 2:
|
||||
continue
|
||||
k, v = kv
|
||||
@@ -348,7 +352,8 @@ class NetworkManager(object):
|
||||
bondcfg[stg] = deats[stg]
|
||||
if member in self.uuidbyname:
|
||||
subprocess.check_call(['nmcli', 'c', 'del', self.uuidbyname[member]])
|
||||
subprocess.check_call(['nmcli', 'c', 'add', 'type', 'bond-slave', 'master', team, 'con-name', member, 'connection.interface-name', member])
|
||||
devtype = self.devtypes.get(member, 'bond-slave')
|
||||
subprocess.check_call(['nmcli', 'c', 'add', 'type', devtype, 'master', team, 'con-name', member, 'connection.interface-name', member])
|
||||
if bondcfg:
|
||||
args = []
|
||||
for parm in bondcfg:
|
||||
|
||||
@@ -10,6 +10,15 @@ for pubkey in /etc/ssh/ssh_host*key.pub; do
|
||||
rm $certfile
|
||||
confluentpython $confapiclient /confluent-api/self/sshcert $pubkey -o $certfile
|
||||
done
|
||||
if [ -d /etc/ssh/sshd_config.d/ -a ! -e /etc/ssh/sshd_config.d/90-confluent.conf ]; then
|
||||
for cert in /etc/ssh/ssh*-cert.pub; do
|
||||
echo HostCertificate $cert >> /etc/ssh/sshd_config.d/90-confluent.conf
|
||||
done
|
||||
echo HostbasedAuthentication yes >> /etc/ssh/sshd_config.d/90-confluent.conf
|
||||
echo HostbasedUsesNameFromPacketOnly yes >> /etc/ssh/sshd_config.d/90-confluent.conf
|
||||
echo IgnoreRhosts no >> /etc/ssh/sshd_config.d/90-confluent.conf
|
||||
fi
|
||||
|
||||
TMPDIR=$(mktemp -d)
|
||||
cd $TMPDIR
|
||||
confluentpython $confapiclient /confluent-public/site/initramfs.tgz -o initramfs.tgz
|
||||
|
||||
@@ -28,11 +28,15 @@ This contains support utilities for enabling deployment of x86_64 architecture s
|
||||
#cp start_root urlmount ../stateless-bin/
|
||||
#cd ..
|
||||
ln -s el8 el9
|
||||
for os in rhvh4 el7 genesis el8 suse15 ubuntu18.04 ubuntu20.04 ubuntu22.04 ubuntu24.04 coreos el9; do
|
||||
cp -a el8 el10
|
||||
mv el10/initramfs/usr el10/initramfs/var
|
||||
for os in rhvh4 el7 genesis el8 suse15 ubuntu18.04 ubuntu20.04 ubuntu22.04 ubuntu24.04 coreos el9 el10; do
|
||||
mkdir ${os}out
|
||||
cd ${os}out
|
||||
if [ -d ../${os}bin ]; then
|
||||
cp -a ../${os}bin/opt .
|
||||
elif [ $os = el10 ]; then
|
||||
cp -a ../el9bin/opt .
|
||||
else
|
||||
cp -a ../el8bin/opt .
|
||||
fi
|
||||
@@ -78,7 +82,7 @@ cp -a esxi7 esxi8
|
||||
%install
|
||||
mkdir -p %{buildroot}/opt/confluent/share/licenses/confluent_osdeploy/
|
||||
cp LICENSE %{buildroot}/opt/confluent/share/licenses/confluent_osdeploy/
|
||||
for os in rhvh4 el7 el8 el9 genesis suse15 ubuntu20.04 ubuntu18.04 ubuntu22.04 ubuntu24.04 esxi6 esxi7 esxi8 coreos; do
|
||||
for os in rhvh4 el7 el8 el9 el10 genesis suse15 ubuntu20.04 ubuntu18.04 ubuntu22.04 ubuntu24.04 esxi6 esxi7 esxi8 coreos; do
|
||||
mkdir -p %{buildroot}/opt/confluent/lib/osdeploy/$os/initramfs
|
||||
mkdir -p %{buildroot}/opt/confluent/lib/osdeploy/$os/profiles
|
||||
cp ${os}out/addons.* %{buildroot}/opt/confluent/lib/osdeploy/$os/initramfs
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/python3
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
@@ -10,8 +11,9 @@ class DiskInfo(object):
|
||||
self.path = None
|
||||
self.model = ''
|
||||
self.size = 0
|
||||
self.driver = None
|
||||
self.driver = ''
|
||||
self.mdcontainer = ''
|
||||
self.subsystype = ''
|
||||
devnode = '/dev/{0}'.format(devname)
|
||||
qprop = subprocess.check_output(
|
||||
['udevadm', 'info', '--query=property', devnode])
|
||||
@@ -46,7 +48,9 @@ class DiskInfo(object):
|
||||
elif (k == 'DRIVERS' and not self.driver
|
||||
and v not in ('"sd"', '""')):
|
||||
self.driver = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer:
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/python3
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
@@ -10,8 +11,9 @@ class DiskInfo(object):
|
||||
self.path = None
|
||||
self.model = ''
|
||||
self.size = 0
|
||||
self.driver = None
|
||||
self.driver = ''
|
||||
self.mdcontainer = ''
|
||||
self.subsystype = ''
|
||||
devnode = '/dev/{0}'.format(devname)
|
||||
qprop = subprocess.check_output(
|
||||
['udevadm', 'info', '--query=property', devnode])
|
||||
@@ -46,7 +48,9 @@ class DiskInfo(object):
|
||||
elif (k == 'DRIVERS' and not self.driver
|
||||
and v not in ('"sd"', '""')):
|
||||
self.driver = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer:
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/python3
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
@@ -10,8 +11,9 @@ class DiskInfo(object):
|
||||
self.path = None
|
||||
self.model = ''
|
||||
self.size = 0
|
||||
self.driver = None
|
||||
self.driver = ''
|
||||
self.mdcontainer = ''
|
||||
self.subsystype = ''
|
||||
devnode = '/dev/{0}'.format(devname)
|
||||
qprop = subprocess.check_output(
|
||||
['udevadm', 'info', '--query=property', devnode])
|
||||
@@ -46,7 +48,9 @@ class DiskInfo(object):
|
||||
elif (k == 'DRIVERS' and not self.driver
|
||||
and v not in ('"sd"', '""')):
|
||||
self.driver = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer:
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/python3
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
@@ -10,8 +11,9 @@ class DiskInfo(object):
|
||||
self.path = None
|
||||
self.model = ''
|
||||
self.size = 0
|
||||
self.driver = None
|
||||
self.driver = ''
|
||||
self.mdcontainer = ''
|
||||
self.subsystype = ''
|
||||
devnode = '/dev/{0}'.format(devname)
|
||||
qprop = subprocess.check_output(
|
||||
['udevadm', 'info', '--query=property', devnode])
|
||||
@@ -46,7 +48,9 @@ class DiskInfo(object):
|
||||
elif (k == 'DRIVERS' and not self.driver
|
||||
and v not in ('"sd"', '""')):
|
||||
self.driver = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer:
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/python3
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
@@ -10,8 +11,9 @@ class DiskInfo(object):
|
||||
self.path = None
|
||||
self.model = ''
|
||||
self.size = 0
|
||||
self.driver = None
|
||||
self.driver = ''
|
||||
self.mdcontainer = ''
|
||||
self.subsystype = ''
|
||||
devnode = '/dev/{0}'.format(devname)
|
||||
qprop = subprocess.check_output(
|
||||
['udevadm', 'info', '--query=property', devnode])
|
||||
@@ -46,7 +48,9 @@ class DiskInfo(object):
|
||||
elif (k == 'DRIVERS' and not self.driver
|
||||
and v not in ('"sd"', '""')):
|
||||
self.driver = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer:
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
|
||||
@@ -65,7 +65,11 @@ def get_image_metadata(imgpath):
|
||||
continue
|
||||
yield md
|
||||
else:
|
||||
raise Exception('Installation from single part image not supported')
|
||||
# plausible filesystem structure to apply to a nominally "diskless" image
|
||||
yield {'mount': '/', 'filesystem': 'xfs', 'minsize': 39513563136, 'initsize': 954128662528, 'flags': 'rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota', 'device': '/dev/mapper/root', 'compressed_size': 27022069760}
|
||||
yield {'mount': '/boot', 'filesystem': 'xfs', 'minsize': 232316928, 'initsize': 1006632960, 'flags': 'rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota', 'device': '/dev/nvme1n1p2', 'compressed_size': 171462656}
|
||||
yield {'mount': '/boot/efi', 'filesystem': 'vfat', 'minsize': 7835648, 'initsize': 627900416, 'flags': 'rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=ascii,shortname=winnt,errors=remount-ro', 'device': '/dev/nvme1n1p1', 'compressed_size': 1576960}
|
||||
#raise Exception('Installation from single part image not supported')
|
||||
|
||||
class PartedRunner():
|
||||
def __init__(self, disk):
|
||||
@@ -84,8 +88,17 @@ def fixup(rootdir, vols):
|
||||
for vol in vols:
|
||||
devbymount[vol['mount']] = vol['targetdisk']
|
||||
fstabfile = os.path.join(rootdir, 'etc/fstab')
|
||||
with open(fstabfile) as tfile:
|
||||
fstab = tfile.read().split('\n')
|
||||
if os.path.exists(fstabfile):
|
||||
with open(fstabfile) as tfile:
|
||||
fstab = tfile.read().split('\n')
|
||||
else:
|
||||
#diskless image, need to invent fstab
|
||||
fstab = [
|
||||
"#ORIGFSTAB#/dev/mapper/root# / xfs defaults 0 0",
|
||||
"#ORIGFSTAB#UUID=aaf9e0f9-aa4d-4d74-9e75-3537620cfe23# /boot xfs defaults 0 0",
|
||||
"#ORIGFSTAB#UUID=C21D-B881# /boot/efi vfat umask=0077,shortname=winnt 0 2",
|
||||
"#ORIGFSTAB#/dev/mapper/swap# none swap defaults 0 0",
|
||||
]
|
||||
while not fstab[0]:
|
||||
fstab = fstab[1:]
|
||||
if os.path.exists(os.path.join(rootdir, '.autorelabel')):
|
||||
@@ -135,8 +148,10 @@ def fixup(rootdir, vols):
|
||||
newcfg = ifcfg.split('/')[-1]
|
||||
newcfg = os.path.join(rootdir, 'etc/NetworkManager/system-connections/{0}'.format(newcfg))
|
||||
shutil.copy2(ifcfg, newcfg)
|
||||
shutil.rmtree(os.path.join(rootdir, 'etc/confluent/'))
|
||||
shutil.copytree('/etc/confluent', os.path.join(rootdir, 'etc/confluent'))
|
||||
rootconfluentdir = os.path.join(rootdir, 'etc/confluent/')
|
||||
if os.path.exists(rootconfluentdir):
|
||||
shutil.rmtree(rootconfluentdir)
|
||||
shutil.copytree('/etc/confluent', rootconfluentdir)
|
||||
if policy:
|
||||
sys.stdout.write('Applying SELinux labeling...')
|
||||
sys.stdout.flush()
|
||||
@@ -191,8 +206,24 @@ def fixup(rootdir, vols):
|
||||
else:
|
||||
newcfgparts.append(cfgpart)
|
||||
loadentout.write(' '.join(newcfgparts) + '\n')
|
||||
with open(grubsyscfg) as defgrubin:
|
||||
defgrub = defgrubin.read().split('\n')
|
||||
if os.path.exists(grubsyscfg):
|
||||
with open(grubsyscfg) as defgrubin:
|
||||
defgrub = defgrubin.read().split('\n')
|
||||
else:
|
||||
defgrub = [
|
||||
'GRUB_TIMEOUT=5',
|
||||
'GRUB_DISTRIBUTOR="$(sed ' + "'s, release .*$,,g'" + ' /etc/system-release)"',
|
||||
'GRUB_DEFAULT=saved',
|
||||
'GRUB_DISABLE_SUBMENU=true',
|
||||
'GRUB_TERMINAL=""',
|
||||
'GRUB_SERIAL_COMMAND=""',
|
||||
'GRUB_CMDLINE_LINUX="crashkernel=1G-4G:192M,4G-64G:256M,64G-:512M rd.lvm.lv=vg/root rd.lvm.lv=vg/swap"',
|
||||
'GRUB_DISABLE_RECOVERY="true"',
|
||||
'GRUB_ENABLE_BLSCFG=true',
|
||||
]
|
||||
if not os.path.exists(os.path.join(rootdir, "etc/kernel/cmdline")):
|
||||
with open(os.path.join(rootdir, "etc/kernel/cmdline"), "w") as cmdlineout:
|
||||
cmdlineout.write("root=/dev/mapper/localstorage-root rd.lvm.lv=localstorage/root")
|
||||
with open(grubsyscfg, 'w') as defgrubout:
|
||||
for gline in defgrub:
|
||||
gline = gline.split()
|
||||
@@ -217,6 +248,12 @@ def fixup(rootdir, vols):
|
||||
grubcfg = grubcfg[:-1]
|
||||
if len(grubcfg) == 1:
|
||||
grubcfg = grubcfg[0]
|
||||
elif not grubcfg:
|
||||
grubcfg = '/boot/grub2/grub.cfg'
|
||||
paths = glob.glob(os.path.join(rootdir, 'boot/efi/EFI/*'))
|
||||
for path in paths:
|
||||
with open(os.path.join(path, 'grub.cfg'), 'w') as stubgrubout:
|
||||
stubgrubout.write("search --no-floppy --root-dev-only --fs-uuid --set=dev " + bootuuid + "\nset prefix=($dev)/grub2\nexport $prefix\nconfigfile $prefix/grub.cfg\n")
|
||||
else:
|
||||
for gcfg in grubcfg:
|
||||
rgcfg = os.path.join(rootdir, gcfg[1:]) # gcfg has a leading / to get rid of
|
||||
@@ -272,10 +309,19 @@ def fixup(rootdir, vols):
|
||||
shimpath = subprocess.check_output(['find', os.path.join(rootdir, 'boot/efi'), '-name', 'shimx64.efi']).decode('utf8').strip()
|
||||
shimpath = shimpath.replace(rootdir, '/').replace('/boot/efi', '').replace('//', '/').replace('/', '\\')
|
||||
subprocess.check_call(['efibootmgr', '-c', '-d', targblock, '-l', shimpath, '--part', partnum])
|
||||
|
||||
try:
|
||||
os.makedirs(os.path.join(rootdir, 'opt/confluent/bin'))
|
||||
except Exception:
|
||||
pass
|
||||
shutil.copy2('/opt/confluent/bin/apiclient', os.path.join(rootdir, 'opt/confluent/bin/apiclient'))
|
||||
#other network interfaces
|
||||
|
||||
|
||||
def had_swap():
|
||||
if not os.path.exists('/etc/fstab'):
|
||||
# diskless source, assume swap
|
||||
return True
|
||||
with open('/etc/fstab') as tabfile:
|
||||
tabs = tabfile.read().split('\n')
|
||||
for tab in tabs:
|
||||
@@ -440,6 +486,8 @@ def install_to_disk(imgpath):
|
||||
subprocess.check_call(['mount', vol['targetdisk'], '/run/imginst/targ'])
|
||||
source = vol['mount'].replace('/', '_')
|
||||
source = '/run/imginst/sources/' + source
|
||||
if not os.path.exists(source):
|
||||
source = '/run/imginst/sources/_' + vol['mount']
|
||||
blankfsstat = os.statvfs('/run/imginst/targ')
|
||||
blankused = (blankfsstat.f_blocks - blankfsstat.f_bfree) * blankfsstat.f_bsize
|
||||
sys.stdout.write('\nWriting {0}: '.format(vol['mount']))
|
||||
|
||||
@@ -41,6 +41,8 @@ echo "Port 22" >> /etc/ssh/sshd_config
|
||||
echo 'Match LocalPort 22' >> /etc/ssh/sshd_config
|
||||
echo ' ChrootDirectory /sysroot/run/imginst/targ' >> /etc/ssh/sshd_config
|
||||
kill -HUP $(cat /run/sshd.pid)
|
||||
cp /sysroot/etc/pki/ca-trust/source/anchors/* /sysroot/run/imginst/targ/etc/pki/ca-trust/source/anchors/
|
||||
chroot /sysroot/run/imginst/targ update-ca-trust
|
||||
|
||||
chroot /sysroot/run/imginst/targ bash -c "source /etc/confluent/functions; run_remote post.sh"
|
||||
chroot /sysroot bash -c "umount \$(tac /proc/mounts|awk '{print \$2}'|grep ^/run/imginst/targ)"
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
# This script is executed 'chrooted' into a cloned disk target before rebooting
|
||||
#
|
||||
|
||||
if [ -f /etc/dracut.conf.d/diskless.conf ]; then
|
||||
rm /etc/dracut.conf.d/diskless.conf
|
||||
fi
|
||||
for kver in /lib/modules/*; do kver=$(basename $kver); kernel-install add $kver /boot/vmlinuz-$kver; done
|
||||
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
|
||||
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
|
||||
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
|
||||
@@ -16,6 +19,7 @@ if [[ "$confluent_mgr" == *:* ]]; then
|
||||
fi
|
||||
export nodename confluent_mgr confluent_profile confluent_websrv
|
||||
. /etc/confluent/functions
|
||||
run_remote setupssh
|
||||
mkdir -p /var/log/confluent
|
||||
chmod 700 /var/log/confluent
|
||||
exec >> /var/log/confluent/confluent-post.log
|
||||
@@ -40,6 +44,9 @@ run_remote_parts post.d
|
||||
# Induce execution of remote configuration, e.g. ansible plays in ansible/post.d/
|
||||
run_remote_config post.d
|
||||
|
||||
# rebuild initrd, pick up new drivers if needed
|
||||
dracut -f /boot/initramfs-$(uname -r).img $(uname -r)
|
||||
|
||||
curl -sf -X POST -d 'status: staged' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_websrv/confluent-api/self/updatestatus
|
||||
|
||||
kill $logshowpid
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
label: Confluent installation of VMware ESXi %%VERSION%% Hypervisor
|
||||
label: VMware ESXi %%VERSION%% Hypervisor
|
||||
ostype: esxi
|
||||
kernelargs: runweasel
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/python3
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
@@ -10,8 +11,9 @@ class DiskInfo(object):
|
||||
self.path = None
|
||||
self.model = ''
|
||||
self.size = 0
|
||||
self.driver = None
|
||||
self.driver = ''
|
||||
self.mdcontainer = ''
|
||||
self.subsystype = ''
|
||||
devnode = '/dev/{0}'.format(devname)
|
||||
qprop = subprocess.check_output(
|
||||
['udevadm', 'info', '--query=property', devnode])
|
||||
@@ -46,7 +48,9 @@ class DiskInfo(object):
|
||||
elif (k == 'DRIVERS' and not self.driver
|
||||
and v not in ('"sd"', '""')):
|
||||
self.driver = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer:
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
|
||||
@@ -140,4 +140,5 @@ mv /lib/modules/$(uname -r) /lib/modules/$(uname -r)-ramfs
|
||||
ln -s /sysroot/lib/modules/$(uname -r) /lib/modules/
|
||||
mv /lib/firmware /lib/firmware-ramfs
|
||||
ln -s /sysroot/lib/firmware /lib/firmware
|
||||
chroot /sysroot chkstat --system --set --noheader > /dev/null
|
||||
exec /opt/confluent/bin/start_root
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/python3
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
@@ -10,8 +11,9 @@ class DiskInfo(object):
|
||||
self.path = None
|
||||
self.model = ''
|
||||
self.size = 0
|
||||
self.driver = None
|
||||
self.driver = ''
|
||||
self.mdcontainer = ''
|
||||
self.subsystype = ''
|
||||
devnode = '/dev/{0}'.format(devname)
|
||||
qprop = subprocess.check_output(
|
||||
['udevadm', 'info', '--query=property', devnode])
|
||||
@@ -46,7 +48,9 @@ class DiskInfo(object):
|
||||
elif (k == 'DRIVERS' and not self.driver
|
||||
and v not in ('"sd"', '""')):
|
||||
self.driver = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer:
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/python3
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
@@ -10,8 +11,9 @@ class DiskInfo(object):
|
||||
self.path = None
|
||||
self.model = ''
|
||||
self.size = 0
|
||||
self.driver = None
|
||||
self.driver = ''
|
||||
self.mdcontainer = ''
|
||||
self.subsystype = ''
|
||||
devnode = '/dev/{0}'.format(devname)
|
||||
qprop = subprocess.check_output(
|
||||
['udevadm', 'info', '--query=property', devnode])
|
||||
@@ -46,7 +48,9 @@ class DiskInfo(object):
|
||||
elif (k == 'DRIVERS' and not self.driver
|
||||
and v not in ('"sd"', '""')):
|
||||
self.driver = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer:
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/python3
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
@@ -10,8 +11,9 @@ class DiskInfo(object):
|
||||
self.path = None
|
||||
self.model = ''
|
||||
self.size = 0
|
||||
self.driver = None
|
||||
self.driver = ''
|
||||
self.mdcontainer = ''
|
||||
self.subsystype = ''
|
||||
devnode = '/dev/{0}'.format(devname)
|
||||
qprop = subprocess.check_output(
|
||||
['udevadm', 'info', '--query=property', devnode])
|
||||
@@ -46,7 +48,9 @@ class DiskInfo(object):
|
||||
elif (k == 'DRIVERS' and not self.driver
|
||||
and v not in ('"sd"', '""')):
|
||||
self.driver = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer:
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
|
||||
@@ -31,23 +31,26 @@ if [ -e /dev/disk/by-label/CNFLNT_IDNT ]; then
|
||||
MYGW=""
|
||||
fi
|
||||
MYNM=$(grep ^ipv4_netmask: $tcfg | awk '{print $2}')
|
||||
for NICGUESS in $(ip link|grep LOWER_UP|grep -v LOOPBACK|cut -d ' ' -f 2 | sed -e 's/:$//'); do
|
||||
ip addr add dev $NICGUESS $v4addr
|
||||
if [ ! -z "$MYGW" ]; then
|
||||
ip route add default via $MYGW
|
||||
fi
|
||||
for dsrv in $deploysrvs; do
|
||||
if openssl s_client -connect $dsrv:443 > /dev/null 2>&1; then
|
||||
deploysrvs=$dsrv
|
||||
NIC=$NICGUESS
|
||||
NIC=""
|
||||
while [ -z "$NIC" ]; do
|
||||
for NICGUESS in $(ip link|grep LOWER_UP|grep -v LOOPBACK|cut -d ' ' -f 2 | sed -e 's/:$//'); do
|
||||
ip addr add dev $NICGUESS $v4addr
|
||||
if [ ! -z "$MYGW" ]; then
|
||||
ip route add default via $MYGW
|
||||
fi
|
||||
for dsrv in $deploysrvs; do
|
||||
if openssl s_client -connect $dsrv:443 > /dev/null 2>&1; then
|
||||
deploysrvs=$dsrv
|
||||
NIC=$NICGUESS
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ -z "$NIC" ]; then
|
||||
ip -4 a flush dev $NICGUESS
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ -z "$NIC" ]; then
|
||||
ip -4 a flush dev $NICGUESS
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
ipconfig -d $MYIP::$MYGW:$MYNM::$NIC
|
||||
echo $NIC > /tmp/autodetectnic
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/python3
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
@@ -10,8 +11,9 @@ class DiskInfo(object):
|
||||
self.path = None
|
||||
self.model = ''
|
||||
self.size = 0
|
||||
self.driver = None
|
||||
self.driver = ''
|
||||
self.mdcontainer = ''
|
||||
self.subsystype = ''
|
||||
devnode = '/dev/{0}'.format(devname)
|
||||
qprop = subprocess.check_output(
|
||||
['udevadm', 'info', '--query=property', devnode])
|
||||
@@ -46,7 +48,9 @@ class DiskInfo(object):
|
||||
elif (k == 'DRIVERS' and not self.driver
|
||||
and v not in ('"sd"', '""')):
|
||||
self.driver = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer:
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
|
||||
@@ -31,23 +31,26 @@ if [ -e /dev/disk/by-label/CNFLNT_IDNT ]; then
|
||||
MYGW=""
|
||||
fi
|
||||
MYNM=$(grep ^ipv4_netmask: $tcfg | awk '{print $2}')
|
||||
for NICGUESS in $(ip link|grep LOWER_UP|grep -v LOOPBACK|cut -d ' ' -f 2 | sed -e 's/:$//'); do
|
||||
ip addr add dev $NICGUESS $v4addr
|
||||
if [ ! -z "$MYGW" ]; then
|
||||
ip route add default via $MYGW
|
||||
fi
|
||||
for dsrv in $deploysrvs; do
|
||||
if openssl s_client -connect $dsrv:443 > /dev/null 2>&1; then
|
||||
deploysrvs=$dsrv
|
||||
NIC=$NICGUESS
|
||||
NIC=""
|
||||
while [ -z "$NIC" ]; do
|
||||
for NICGUESS in $(ip link|grep LOWER_UP|grep -v LOOPBACK|cut -d ' ' -f 2 | sed -e 's/:$//'); do
|
||||
ip addr add dev $NICGUESS $v4addr
|
||||
if [ ! -z "$MYGW" ]; then
|
||||
ip route add default via $MYGW
|
||||
fi
|
||||
for dsrv in $deploysrvs; do
|
||||
if openssl s_client -connect $dsrv:443 > /dev/null 2>&1; then
|
||||
deploysrvs=$dsrv
|
||||
NIC=$NICGUESS
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ -z "$NIC" ]; then
|
||||
ip -4 a flush dev $NICGUESS
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ -z "$NIC" ]; then
|
||||
ip -4 a flush dev $NICGUESS
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
ipconfig -d $MYIP::$MYGW:$MYNM::$NIC
|
||||
echo $NIC > /tmp/autodetectnic
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/python3
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
@@ -10,8 +11,9 @@ class DiskInfo(object):
|
||||
self.path = None
|
||||
self.model = ''
|
||||
self.size = 0
|
||||
self.driver = None
|
||||
self.driver = ''
|
||||
self.mdcontainer = ''
|
||||
self.subsystype = ''
|
||||
devnode = '/dev/{0}'.format(devname)
|
||||
qprop = subprocess.check_output(
|
||||
['udevadm', 'info', '--query=property', devnode])
|
||||
@@ -46,7 +48,9 @@ class DiskInfo(object):
|
||||
elif (k == 'DRIVERS' and not self.driver
|
||||
and v not in ('"sd"', '""')):
|
||||
self.driver = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer:
|
||||
elif k == 'ATTRS{subsystype}':
|
||||
self.subsystype = v.replace('"', '')
|
||||
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
|
||||
raise Exception("No driver detected")
|
||||
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
|
||||
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
|
||||
|
||||
@@ -215,13 +215,19 @@ async def create_certificate(keyout=None, certout=None, csrout=None):
|
||||
longname = shortname # socket.getfqdn()
|
||||
if not csrout:
|
||||
await util.check_call(
|
||||
'openssl', 'ecparam', '-name', 'secp384r1', '-genkey', '-out',
|
||||
keyout)
|
||||
san = ['IP:{0}'.format(x) async for x in get_ip_addresses()]
|
||||
['openssl', 'ecparam', '-name', 'secp384r1', '-genkey', '-out',
|
||||
keyout])
|
||||
ipaddrs = list(get_ip_addresses())
|
||||
san = ['IP:{0}'.format(x) for x in ipaddrs]
|
||||
# It is incorrect to put IP addresses as DNS type. However
|
||||
# there exists non-compliant clients that fail with them as IP
|
||||
san.extend(['DNS:{0}'.format(x) async for x in get_ip_addresses()])
|
||||
san.append('DNS:{0}'.format(shortname))
|
||||
# san.extend(['DNS:{0}'.format(x) for x in ipaddrs])
|
||||
dnsnames = set(ipaddrs)
|
||||
dnsnames.add(shortname)
|
||||
for currip in ipaddrs:
|
||||
dnsnames.add(socket.getnameinfo((currip, 0), 0)[0])
|
||||
for currname in dnsnames:
|
||||
san.append('DNS:{0}'.format(currname))
|
||||
#san.append('DNS:{0}'.format(longname))
|
||||
san = ','.join(san)
|
||||
sslcfg = get_openssl_conf_location()
|
||||
|
||||
@@ -94,8 +94,8 @@ node = {
|
||||
'considered a member'),
|
||||
},
|
||||
'type': {
|
||||
'description': ('Classification of node as server or switch. By default a node is presumed to be a server.'),
|
||||
'validvalues': ('switch', 'server'),
|
||||
'description': ('The type of node. This may be switch, server, rackmount, dense, enclosure or not set to be generic.'),
|
||||
'validvalues': ('switch', 'server', 'rackmount', 'dense', 'enclosure', ''),
|
||||
},
|
||||
'crypted.rootpassword': {
|
||||
'description': 'The password of the local root password. '
|
||||
|
||||
@@ -2367,6 +2367,9 @@ class ConfigManager(object):
|
||||
lidx = self._cfgstore['nodes'][node]['groups'].index(name)
|
||||
self._cfgstore['nodes'][node]['groups'][lidx] = renamemap[name]
|
||||
_mark_dirtykey('nodes', node, self.tenant)
|
||||
for node in self._cfgstore['nodegroups'][renamemap[name]].get('nodes', []):
|
||||
self._node_removed_from_group(node, name, {})
|
||||
self._node_added_to_group(node, renamemap[name], {})
|
||||
self._bg_sync_to_file()
|
||||
|
||||
|
||||
|
||||
@@ -704,10 +704,11 @@ class ProxyConsole(object):
|
||||
try:
|
||||
remote = await collective.connect_to_collective(None, self.managerinfo['address'])
|
||||
except Exception as e:
|
||||
print(repr(e))
|
||||
if _tracelog:
|
||||
_tracelog.log(traceback.format_exc(), ltype=log.DataTypes.event, event=log.Events.stacktrace)
|
||||
await asyncio.sleep(3)
|
||||
if self.clisession:
|
||||
await self.clisession.detach()
|
||||
await self.clisession.detach(False)
|
||||
await self.detachsession(None)
|
||||
return
|
||||
await tlvdata.recv(remote)
|
||||
@@ -836,7 +837,7 @@ class ConsoleSession(object):
|
||||
self._evt = None
|
||||
self.reghdl = None
|
||||
|
||||
async def detach(self):
|
||||
async def detach(self, reattach=True):
|
||||
"""Handler for the console handler to detach so it can reattach,
|
||||
currently to facilitate changing from one collective.manager to
|
||||
another
|
||||
@@ -844,9 +845,10 @@ class ConsoleSession(object):
|
||||
:return:
|
||||
"""
|
||||
await self.conshdl.detachsession(self)
|
||||
self.connect_session()
|
||||
await self.conshdl.attachsession(self)
|
||||
self.write = self.conshdl.write
|
||||
if reattach:
|
||||
self.connect_session()
|
||||
await self.conshdl.attachsession(self)
|
||||
self.write = self.conshdl.write
|
||||
|
||||
def got_data(self, data):
|
||||
"""Receive data from console and buffer
|
||||
|
||||
@@ -63,7 +63,10 @@ import msgpack
|
||||
import os
|
||||
import struct
|
||||
import sys
|
||||
import uuid
|
||||
import yaml
|
||||
import shutil
|
||||
|
||||
|
||||
pluginmap = {}
|
||||
dispatch_plugins = (b'ipmi', u'ipmi', b'redfish', u'redfish', b'tsmsol', u'tsmsol', b'geist', u'geist', b'deltapdu', u'deltapdu', b'eatonpdu', u'eatonpdu', b'affluent', u'affluent', b'cnos', u'cnos', b'enos', u'enos')
|
||||
@@ -180,8 +183,9 @@ def _merge_dict(original, custom):
|
||||
|
||||
|
||||
rootcollections = ['deployment/', 'discovery/', 'events/', 'networking/',
|
||||
'noderange/', 'nodes/', 'nodegroups/', 'storage/', 'usergroups/' ,
|
||||
'users/', 'uuid', 'version']
|
||||
'noderange/', 'nodes/', 'nodegroups/', 'storage/', 'usergroups/',
|
||||
'users/', 'uuid', 'version', 'staging/']
|
||||
|
||||
|
||||
|
||||
class PluginRoute(object):
|
||||
@@ -1294,6 +1298,98 @@ def handle_discovery(pathcomponents, operation, configmanager, inputdata):
|
||||
if pathcomponents[0] == 'detected':
|
||||
pass
|
||||
|
||||
|
||||
class Staging:
|
||||
def __init__(self, user, uuid):
|
||||
self.uuid_str = uuid
|
||||
self.storage_folder = '/var/lib/confluent/client_assets/' + self.uuid_str
|
||||
self.filename = None
|
||||
self.user = user
|
||||
self.base_folder = os.path.exists('/var/lib/confluent/client_assets/')
|
||||
|
||||
if not self.base_folder:
|
||||
try:
|
||||
os.mkdir('/var/lib/confluent/client_assets/')
|
||||
except Exception as e:
|
||||
raise OSError(str(e))
|
||||
|
||||
def getUUID(self):
|
||||
return self.uuid_str
|
||||
|
||||
def get_push_url(self):
|
||||
return 'staging/{0}/{1}'.format(self.user,self.uuid_str)
|
||||
|
||||
def create_directory(self):
|
||||
try:
|
||||
os.mkdir(self.storage_folder)
|
||||
return True
|
||||
except OSError as e:
|
||||
raise exc.InvalidArgumentException(str(e))
|
||||
|
||||
def get_file_name(self):
|
||||
stage_file = '{}/filename.txt'.format(self.storage_folder)
|
||||
try:
|
||||
with open(stage_file, 'r') as f:
|
||||
filename = f.readline()
|
||||
os.remove(stage_file)
|
||||
return self.storage_folder + '/{}'.format(filename)
|
||||
except FileNotFoundError:
|
||||
file = None
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def remove_directory(directory):
|
||||
storage_folder = '/var/lib/confluent/client_assets/' + directory
|
||||
if os.path.exists(storage_folder):
|
||||
shutil.rmtree(storage_folder)
|
||||
else:
|
||||
raise FileNotFoundError
|
||||
return directory
|
||||
|
||||
def handle_staging(pathcomponents, operation, configmanager, inputdata):
|
||||
'''
|
||||
e.g push_url: /confluent-api/staging/user/<unique_id>
|
||||
'''
|
||||
if operation == 'create':
|
||||
if len(pathcomponents) == 1:
|
||||
stage = Staging(inputdata['user'],str(uuid.uuid1()))
|
||||
if stage.create_directory():
|
||||
if 'filename' in inputdata:
|
||||
data_file = stage.storage_folder + '/filename.txt'
|
||||
with open(data_file, 'w') as f:
|
||||
f.write(inputdata['filename'])
|
||||
else:
|
||||
raise Exception('Error: Missing filename arg')
|
||||
push_url = stage.get_push_url()
|
||||
yield msg.CreatedResource(push_url)
|
||||
|
||||
elif len(pathcomponents) == 3:
|
||||
stage = Staging(pathcomponents[1], pathcomponents[2])
|
||||
file = stage.get_file_name()
|
||||
if 'filedata' in inputdata and file:
|
||||
content_length = inputdata['content_length']
|
||||
remaining_length = content_length
|
||||
filedata = inputdata['filedata']
|
||||
chunk_size = 16384
|
||||
progress = 0.0
|
||||
with open(file, 'wb') as f:
|
||||
while remaining_length > 0:
|
||||
progress = (1 - (remaining_length/content_length)) * 100
|
||||
datachunk = filedata['wsgi.input'].read(min(chunk_size, remaining_length))
|
||||
f.write(datachunk)
|
||||
remaining_length -= len(datachunk)
|
||||
eventlet.sleep(0)
|
||||
yield msg.FileUploadProgress(progress)
|
||||
yield msg.FileUploadProgress(100)
|
||||
|
||||
|
||||
elif operation == 'delete':
|
||||
if len(pathcomponents) == 3:
|
||||
asset = Staging.remove_directory(pathcomponents[2])
|
||||
yield msg.DeletedResource(asset)
|
||||
else:
|
||||
raise Exception("Invalid url")
|
||||
|
||||
async def handle_path(path, operation, configmanager, inputdata=None, autostrip=True):
|
||||
"""Given a full path request, return an object.
|
||||
|
||||
@@ -1402,5 +1498,7 @@ async def handle_path(path, operation, configmanager, inputdata=None, autostrip=
|
||||
elif pathcomponents[0] == 'discovery':
|
||||
return handle_discovery(pathcomponents[1:], operation, configmanager,
|
||||
inputdata)
|
||||
elif pathcomponents[0] == 'staging':
|
||||
return handle_staging(pathcomponents, operation, configmanager, inputdata)
|
||||
else:
|
||||
raise exc.NotFoundException()
|
||||
|
||||
@@ -76,6 +76,7 @@ import confluent.discovery.handlers.pxe as pxeh
|
||||
import confluent.discovery.handlers.smm as smm
|
||||
import confluent.discovery.handlers.xcc as xcc
|
||||
import confluent.discovery.handlers.xcc3 as xcc3
|
||||
import confluent.discovery.handlers.smm3 as smm3
|
||||
import confluent.discovery.handlers.megarac as megarac
|
||||
import confluent.exceptions as exc
|
||||
import confluent.log as log
|
||||
@@ -110,6 +111,7 @@ class nesteddict(dict):
|
||||
nodehandlers = {
|
||||
'service:lenovo-smm': smm,
|
||||
'service:lenovo-smm2': smm,
|
||||
'lenovo-smm3': smm3,
|
||||
'lenovo-xcc': xcc,
|
||||
'lenovo-xcc3': xcc3,
|
||||
'megarac-bmc': megarac,
|
||||
@@ -130,6 +132,7 @@ servicenames = {
|
||||
'cumulus-switch': 'cumulus-switch',
|
||||
'service:lenovo-smm': 'lenovo-smm',
|
||||
'service:lenovo-smm2': 'lenovo-smm2',
|
||||
'lenovo-smm3': 'lenovo-smm3',
|
||||
'affluent-switch': 'affluent-switch',
|
||||
'lenovo-xcc': 'lenovo-xcc',
|
||||
'lenovo-xcc3': 'lenovo-xcc3',
|
||||
@@ -147,6 +150,7 @@ servicebyname = {
|
||||
'cumulus-switch': 'cumulus-switch',
|
||||
'lenovo-smm': 'service:lenovo-smm',
|
||||
'lenovo-smm2': 'service:lenovo-smm2',
|
||||
'lenovo-smm3': 'lenovo-smm3',
|
||||
'affluent-switch': 'affluent-switch',
|
||||
'lenovo-xcc': 'lenovo-xcc',
|
||||
'lenovo-xcc3': 'lenovo-xcc3',
|
||||
|
||||
@@ -31,7 +31,7 @@ async def get_host_interface_urls(wc, mginfo):
|
||||
returls = []
|
||||
hifurl = mginfo.get('HostInterfaces', {}).get('@odata.id', None)
|
||||
if not hifurl:
|
||||
return None
|
||||
return []
|
||||
hifinfo = await wc.grab_json_response(hifurl)
|
||||
hifurls = hifinfo.get('Members', [])
|
||||
for hifurl in hifurls:
|
||||
@@ -111,7 +111,7 @@ class NodeHandler(generic.NodeHandler):
|
||||
await self.target_account_url(wc))
|
||||
acctinfo = acctinfo[0]
|
||||
actypes = acctinfo['AccountTypes']
|
||||
candidates = acctinfo['AccountTypes@Redfish.AllowableValues']
|
||||
candidates = acctinfo.get('AccountTypes@Redfish.AllowableValues', [])
|
||||
if 'IPMI' not in actypes and 'IPMI' in candidates:
|
||||
actypes.append('IPMI')
|
||||
acctupd = {
|
||||
@@ -137,7 +137,14 @@ class NodeHandler(generic.NodeHandler):
|
||||
rsp = json.loads(rsp)
|
||||
currerr = rsp.get('error', {})
|
||||
ecode = currerr.get('code', None)
|
||||
if ecode.endswith('PasswordChangeRequired'):
|
||||
if not ecode:
|
||||
for msg in rsp['@Message.ExtendedInfo']:
|
||||
if 'PasswordChangeRequired' in msg['MessageId']:
|
||||
chgurl = msg['MessageArgs'][0]
|
||||
break
|
||||
else:
|
||||
raise Exception("Failed to ascertain login failure reason")
|
||||
elif ecode.endswith('PasswordChangeRequired'):
|
||||
for einfo in currerr.get('@Message.ExtendedInfo', []):
|
||||
if einfo.get('MessageId', None).endswith('PasswordChangeRequired'):
|
||||
for msgarg in einfo.get('MessageArgs'):
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
# Copyright 2024 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.
|
||||
|
||||
import confluent.discovery.handlers.redfishbmc as redfishbmc
|
||||
import eventlet.support.greendns
|
||||
import confluent.util as util
|
||||
|
||||
webclient = eventlet.import_patched('pyghmi.util.webclient')
|
||||
|
||||
|
||||
|
||||
getaddrinfo = eventlet.support.greendns.getaddrinfo
|
||||
|
||||
|
||||
class NodeHandler(redfishbmc.NodeHandler):
|
||||
devname = 'SMM3'
|
||||
|
||||
def scan(self):
|
||||
attrs = self.info.get('attributes', {})
|
||||
mtm = attrs.get('enclosure-machinetype-model', None)
|
||||
if mtm:
|
||||
self.info['modelnumber'] = mtm.strip()
|
||||
sn = attrs.get('enclosure-serial-number', None)
|
||||
if sn:
|
||||
self.info['serialnumber'] = sn.strip()
|
||||
modelname = attrs.get('enclosure-component-name', None)
|
||||
if modelname:
|
||||
modelname = modelname.split(' MT:')[0]
|
||||
self.info['modelname'] = modelname
|
||||
|
||||
def get_firmware_default_account_info(self):
|
||||
return ('USERID', 'PASSW0RD')
|
||||
|
||||
|
||||
def remote_nodecfg(nodename, cfm):
|
||||
cfg = cfm.get_node_attributes(
|
||||
nodename, 'hardwaremanagement.manager')
|
||||
ipaddr = cfg.get(nodename, {}).get('hardwaremanagement.manager', {}).get(
|
||||
'value', None)
|
||||
ipaddr = ipaddr.split('/', 1)[0]
|
||||
ipaddr = getaddrinfo(ipaddr, 0)[0][-1]
|
||||
if not ipaddr:
|
||||
raise Exception('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)
|
||||
import sys
|
||||
info = {'addresses': [[sys.argv[1]]]}
|
||||
print(repr(info))
|
||||
testr = NodeHandler(info, c)
|
||||
testr.config(sys.argv[2])
|
||||
|
||||
@@ -602,6 +602,7 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
'/redfish/v1/AccountService/Accounts/1',
|
||||
updateinf, method='PATCH')
|
||||
if targbmc and not targbmc.startswith('fe80::'):
|
||||
attribsuffix = ''
|
||||
newip = targbmc.split('/', 1)[0]
|
||||
newipinfo = socket.getaddrinfo(newip, 0)[0]
|
||||
newip = newipinfo[-1][0]
|
||||
@@ -611,6 +612,25 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
newmask = netutil.cidr_to_mask(netconfig['prefix'])
|
||||
currinfo = await wc.grab_json_response('/api/providers/logoninfo')
|
||||
currip = currinfo.get('items', [{}])[0].get('ipv4_address', '')
|
||||
curreth1 = wc.grab_json_response('/api/dataset/imm_ethernet')
|
||||
if curreth1:
|
||||
if self.ipaddr.startswith('fe80::'):
|
||||
ipkey = 'ipv6_link_local_address'
|
||||
elif '.' in self.ipaddr:
|
||||
ipkey = 'ipv4_address'
|
||||
else:
|
||||
raise Exception('Non-Link-Local IPv6 TODO')
|
||||
nic1ip = curreth1.get('items', [{}])[0].get(ipkey, None)
|
||||
if nic1ip != self.ipaddr:
|
||||
# check second nic instead
|
||||
curreth2 = wc.grab_json_response('/api/dataset/imm_ethernet_2')
|
||||
if curreth2 and curreth2.get('items', [{}])[0].get('if_second_port_exist', 0):
|
||||
nic2ip = curreth2.get('items', [{}])[0].get(ipkey + '_2', None)
|
||||
if nic2ip != self.ipaddr:
|
||||
raise Exception("Unable to determine which NIC is active")
|
||||
# ok, second nic is active, target it
|
||||
currip = curreth2.get('items', [{}])[0].get("ipv4_address", None)
|
||||
attribsuffix = '_2'
|
||||
# do not change the ipv4_config if the current config looks right already
|
||||
if currip != newip:
|
||||
statargs = {
|
||||
@@ -621,6 +641,10 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
statargs['ENET_IPv4GatewayIPAddr'] = netconfig['ipv4_gateway']
|
||||
elif not netutil.address_is_local(newip):
|
||||
raise exc.InvalidArgumentException('Will not remotely configure a device with no gateway')
|
||||
if attribsuffix:
|
||||
for currkey in list(statargs):
|
||||
statargs[currkey + attribsuffix] = statargs[currkey]
|
||||
del statargs[currkey]
|
||||
netset, status = await wc.grab_json_response_with_status('/api/dataset', statargs)
|
||||
print(repr(netset))
|
||||
print(repr(status))
|
||||
|
||||
@@ -57,7 +57,7 @@ smsg = ('M-SEARCH * HTTP/1.1\r\n'
|
||||
|
||||
async def active_scan(handler, protocol=None):
|
||||
known_peers = set([])
|
||||
async for scanned in scan(['urn:dmtf-org:service:redfish-rest:1', 'urn::service:affluent']):
|
||||
async for scanned in scan(['urn:dmtf-org:service:redfish-rest:1', 'urn::dmtf-org:service:redfish-rest:', 'urn::service:affluent']):
|
||||
for addr in scanned['addresses']:
|
||||
addr = addr[0:1] + addr[2:]
|
||||
if addr in known_peers:
|
||||
@@ -452,10 +452,10 @@ async def _find_service(service, target):
|
||||
mya['enclosure-machinetype-model'] = [val]
|
||||
yield peerdata[nid]
|
||||
continue
|
||||
if '/redfish/v1/' not in peerdata[nid].get('urls', ()) and '/redfish/v1' not in peerdata[nid].get('urls', ()):
|
||||
continue
|
||||
if '/DeviceDescription.json' in peerdata[nid]['urls']:
|
||||
pooltargs.append(('/DeviceDescription.json', peerdata[nid], 'lenovo-xcc'))
|
||||
elif '/redfish/v1/' not in peerdata[nid].get('urls', ()) and '/redfish/v1' not in peerdata[nid].get('urls', ()):
|
||||
continue
|
||||
else:
|
||||
for targurl in peerdata[nid]['urls']:
|
||||
if '/eth' in targurl and targurl.endswith('.xml'):
|
||||
@@ -493,6 +493,12 @@ async def check_fish(urldata, port=443, verifycallback=None):
|
||||
return None
|
||||
if url == '/DeviceDescription.json':
|
||||
if not peerinfo:
|
||||
if data.get('services', None) == ['urn::dmtf-org:service:redfish-rest:']:
|
||||
peerinfo = wc.grab_json_response('/redfish/v1/')
|
||||
if peerinfo:
|
||||
data['services'] = ['lenovo-smm3']
|
||||
data['uuid'] = peerinfo['UUID'].lower()
|
||||
return data
|
||||
return None
|
||||
try:
|
||||
peerinfo = peerinfo[0]
|
||||
@@ -509,6 +515,12 @@ async def check_fish(urldata, port=443, verifycallback=None):
|
||||
data['services'] = ['lenovo-xcc'] if 'xcc-variant' not in peerinfo else ['lenovo-xcc' + peerinfo['xcc-variant']]
|
||||
return data
|
||||
except (IndexError, KeyError):
|
||||
if 'type' in peerinfo and peerinfo['type'].lower() == 'lenovo-smm3':
|
||||
del peerinfo['xcc-variant']
|
||||
data['uuid'] = peerinfo['enclosure-uuid']
|
||||
data['services'] = ['lenovo-smm3']
|
||||
data['attributes'] = peerinfo
|
||||
return data
|
||||
return None
|
||||
url = '/redfish/v1/'
|
||||
peerinfo = await wc.grab_json_response('/redfish/v1/')
|
||||
|
||||
@@ -44,6 +44,7 @@ import confluent.asynctlvdata as tlvdata
|
||||
import confluent.util as util
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
import traceback
|
||||
@@ -435,6 +436,8 @@ def websockify_data(data):
|
||||
data = data.decode('utf8')
|
||||
except UnicodeDecodeError:
|
||||
data = data.decode('cp437')
|
||||
except AttributeError: # already str
|
||||
pass
|
||||
data = u' ' + data
|
||||
return data
|
||||
|
||||
@@ -641,7 +644,6 @@ async def resourcehandler(request):
|
||||
rsp.content_type = mimetype
|
||||
await rsp.prepare(request)
|
||||
return rsp
|
||||
|
||||
try:
|
||||
if 'Sec-WebSocket-Version' in request.headers:
|
||||
return await wsock_handler(request)
|
||||
@@ -667,6 +669,11 @@ async def resourcehandler_backend(req, make_response):
|
||||
reqpath = req.rel_url.path
|
||||
if reqpath.startswith('/self/'):
|
||||
return await selfservice.handle_request(req, make_response, mimetype)
|
||||
if reqpath == '/httpapi_initialized':
|
||||
if (len(configmanager.ConfigManager(None).list_usergroups()) > 0
|
||||
or len(configmanager.ConfigManager(None).list_users()) > 0):
|
||||
return await make_response(mimetype, 200, "OK")
|
||||
return await make_response(mimetype, 500, "No authorized users")
|
||||
if reqpath.startswith('/boot/'):
|
||||
request = reqpath.split('/')
|
||||
if not request[0]:
|
||||
@@ -688,14 +695,14 @@ async def resourcehandler_backend(req, make_response):
|
||||
if not pprofile:
|
||||
return await make_response(mimetype, 404, 'Not Found')
|
||||
redir = '/confluent-public/os/{0}/boot.{1}'.format(pprofile, bootfile)
|
||||
rsp = make_response(mimetype, 302, 'Found', {'Location': redir})
|
||||
rsp = await make_response(mimetype, 302, 'Found', {'Location': redir})
|
||||
return
|
||||
if req.content_length:
|
||||
if req.content_length and nat '/staging' in reqpath:
|
||||
reqbody = await req.read()
|
||||
reqtype = req.content_type
|
||||
operation = opmap.get(req.method, None)
|
||||
if not operation:
|
||||
rsp = make_response(mimetype, 400, 'Bad Request')
|
||||
rsp = await make_response(mimetype, 400, 'Bad Request')
|
||||
await rsp.write(b'Unsupported method')
|
||||
return rsp
|
||||
querydict = _get_query_dict(req, reqbody, reqtype)
|
||||
@@ -893,6 +900,67 @@ async def resourcehandler_backend(req, make_response):
|
||||
return rsp
|
||||
else: # no keys, but a session, means it's hooking to receive data
|
||||
raise Exception("long polling console sessions are discontinued")
|
||||
======
|
||||
elif (operation == 'create' and ('/firmware/updates/active' in reqpath)):
|
||||
if 'application/json' in reqtype:
|
||||
if not isinstance(reqbody, str):
|
||||
reqbody = reqbody.decode('utf8')
|
||||
pbody = json.loads(reqbody)
|
||||
args = pbody['args']
|
||||
file_directory = '/var/lib/confluent/client_assets/{}'.format(args.split('/')[-1])
|
||||
filepath = '{0}/{1}'.format(file_directory, os.listdir(file_directory)[0]) # TODO find a way to validate that the file is found and its the expected one
|
||||
args_dict = {'filename': filepath}
|
||||
noderrs = {}
|
||||
nodeurls = {}
|
||||
hdlr = pluginapi.handle_path(reqpath, operation, cfgmgr, args_dict)
|
||||
for res in hdlr:
|
||||
if isinstance(res, confluent.messages.CreatedResource):
|
||||
watchurl = res.kvpairs['created']
|
||||
currnode = watchurl.split('/')[1]
|
||||
nodeurls[currnode] = '/' + watchurl
|
||||
|
||||
rsp = await make_response(mimetype, 200, 'OK', headers=headers)
|
||||
await rsp.write(json.dumps({'data': nodeurls}))
|
||||
return
|
||||
elif (operation == 'create' and ('/staging' in reqpath)):
|
||||
url = reqpath
|
||||
args_dict = {}
|
||||
content_length = int(req.content_length)
|
||||
if content_length > 0 and (len(url.split('/')) > 2):
|
||||
# check if the user and the url defined user are the same
|
||||
if authorized['username'] == url.split('/')[2]:
|
||||
args_dict.update({'filedata':env, 'content_length': content_length}) # TODO: replace env
|
||||
hdlr = pluginapi.handle_path(url, operation, cfgmgr, args_dict)
|
||||
for resp in hdlr:
|
||||
if isinstance(resp, confluent.messages.FileUploadProgress):
|
||||
if resp.kvpairs['progress']['value'] == 100:
|
||||
progress = resp.kvpairs['progress']['value']
|
||||
rsp = await make_response(mimetype, 200, 'OK', headers=headers)
|
||||
await rsp.write(json.dumps({'data': 'done'}))
|
||||
return
|
||||
else:
|
||||
rsp = await make_response(mimetype, 401 'Unauthorized', headers=headers)
|
||||
await rsp.write(json.dumps({'data': 'You do not have permission to write to file'}))
|
||||
return
|
||||
elif len(url.split('/')) == 2:
|
||||
reqbody = env['wsgi.input'].read(int(env['CONTENT_LENGTH'])) # TODO: replace env
|
||||
reqtype = env['CONTENT_TYPE']
|
||||
if not isinstance(reqbody, str):
|
||||
reqbody = reqbody.decode('utf8')
|
||||
pbody = json.loads(reqbody)
|
||||
args = pbody['args']
|
||||
args_dict.update({'filename': args, 'user': authorized['username']})
|
||||
try:
|
||||
args_dict.update({'bank': pbody['bank']})
|
||||
except KeyError:
|
||||
pass
|
||||
hdlr = pluginapi.handle_path(url, operation, cfgmgr, args_dict)
|
||||
for res in hdlr:
|
||||
if isinstance(res, confluent.messages.CreatedResource):
|
||||
stageurl = res.kvpairs['created']
|
||||
rsp = await make_response(mimetype, 200, 'OK', headers=headers)
|
||||
await rsp.write(json.dumps({'data': stageurl}))
|
||||
return
|
||||
else:
|
||||
# normal request
|
||||
url = reqpath
|
||||
|
||||
@@ -645,6 +645,18 @@ class SavedFile(ConfluentMessage):
|
||||
self.myargs = (node, file)
|
||||
self.kvpairs = {node: {'filename': file}}
|
||||
|
||||
class FileUploadProgress(ConfluentMessage):
|
||||
readonly = True
|
||||
|
||||
def __init__(self, progress, name=None):
|
||||
self.myargs = (progress)
|
||||
self.stripped = False
|
||||
self.notnode = name is None
|
||||
if self.notnode:
|
||||
self.kvpairs = {'progress': {'value': progress}}
|
||||
else:
|
||||
self.kvpairs = {name: {'progress': {'value': progress}}}
|
||||
|
||||
class InputAlertData(ConfluentMessage):
|
||||
|
||||
def __init__(self, path, inputdata, nodes=None):
|
||||
|
||||
@@ -91,10 +91,13 @@ async def update_boot_esxi(profiledir, profile, label):
|
||||
newbootcfg = ''
|
||||
efibootcfg = ''
|
||||
filesneeded = []
|
||||
localabel = label
|
||||
if 'installation of' not in localabel:
|
||||
localabel = 'Confluent installation of {}'.format(localabel)
|
||||
for cfgline in bootcfg:
|
||||
if cfgline.startswith('title='):
|
||||
newbootcfg += 'title={0}\n'.format(label)
|
||||
efibootcfg += 'title={0}\n'.format(label)
|
||||
newbootcfg += 'title={0}\n'.format(localabel)
|
||||
efibootcfg += 'title={0}\n'.format(localabel)
|
||||
elif cfgline.startswith('kernelopt='):
|
||||
newbootcfg += 'kernelopt={0}\n'.format(kernelargs)
|
||||
efibootcfg += 'kernelopt={0}\n'.format(kernelargs)
|
||||
@@ -312,6 +315,7 @@ def check_alma(isoinfo):
|
||||
ver = None
|
||||
arch = None
|
||||
cat = None
|
||||
suffix = ""
|
||||
for entry in isoinfo[0]:
|
||||
if 'almalinux-release-8' in entry:
|
||||
ver = entry.split('-')[2]
|
||||
@@ -323,6 +327,12 @@ def check_alma(isoinfo):
|
||||
arch = entry.split('.')[-2]
|
||||
cat = 'el9'
|
||||
break
|
||||
elif 'almalinux-kitten-release-10' in entry:
|
||||
ver = entry.split('-')[3]
|
||||
arch = entry.split('.')[-2]
|
||||
cat = 'el10'
|
||||
suffix = '_kitten'
|
||||
break
|
||||
else:
|
||||
return None
|
||||
if arch == 'noarch' and '.discinfo' in isoinfo[1]:
|
||||
@@ -330,7 +340,7 @@ def check_alma(isoinfo):
|
||||
arch = prodinfo.split(b'\n')[2]
|
||||
if not isinstance(arch, str):
|
||||
arch = arch.decode('utf-8')
|
||||
return {'name': 'alma-{0}-{1}'.format(ver, arch), 'method': EXTRACT, 'category': cat}
|
||||
return {'name': 'alma{0}-{1}-{2}'.format(suffix, ver, arch), 'method': EXTRACT, 'category': cat}
|
||||
|
||||
|
||||
def check_centos(isoinfo):
|
||||
@@ -362,6 +372,12 @@ def check_centos(isoinfo):
|
||||
cat = 'el9'
|
||||
isstream = '_stream'
|
||||
break
|
||||
elif 'centos-stream-release-10' in entry:
|
||||
ver = entry.split('-')[3]
|
||||
arch = entry.split('.')[-2]
|
||||
cat = 'el10'
|
||||
isstream = '_stream'
|
||||
break
|
||||
elif 'centos-linux-release-8' in entry:
|
||||
ver = entry.split('-')[3]
|
||||
arch = entry.split('.')[-2]
|
||||
|
||||
@@ -104,6 +104,17 @@ class OpenBmcConsole(conapi.Console):
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
def recvdata(self):
|
||||
while self.connected:
|
||||
try:
|
||||
pendingdata = self.ws.recv()
|
||||
except websocket.WebSocketConnectionClosedException:
|
||||
pendingdata = ''
|
||||
if pendingdata == '':
|
||||
self.datacallback(conapi.ConsoleEvent.Disconnect)
|
||||
return
|
||||
self.datacallback(pendingdata)
|
||||
|
||||
|
||||
async def connect(self, callback):
|
||||
self.datacallback = callback
|
||||
@@ -134,7 +145,11 @@ class OpenBmcConsole(conapi.Console):
|
||||
return
|
||||
|
||||
async def write(self, data):
|
||||
await self.ws.send_str(data.decode())
|
||||
try:
|
||||
await self.ws.send_str(data.decode())
|
||||
except Exception as e:
|
||||
print(repr(e))
|
||||
await self.datacallback(conapi.ConsoleEvent.Disconnect)
|
||||
|
||||
async def close(self):
|
||||
if self.recvr:
|
||||
|
||||
@@ -0,0 +1,279 @@
|
||||
# Copyright 2022 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.
|
||||
|
||||
import pyghmi.util.webclient as wc
|
||||
import confluent.util as util
|
||||
import confluent.messages as msg
|
||||
import confluent.exceptions as exc
|
||||
import eventlet.green.time as time
|
||||
import eventlet
|
||||
import eventlet.greenpool as greenpool
|
||||
|
||||
|
||||
|
||||
def simplify_name(name):
|
||||
return name.lower().replace(' ', '_').replace('/', '-').replace('_-_', '-')
|
||||
|
||||
|
||||
pdupool = greenpool.GreenPool(128)
|
||||
|
||||
_pduclients = {}
|
||||
|
||||
|
||||
class EnlogicClient(object):
|
||||
def __init__(self, pdu, configmanager):
|
||||
self.node = pdu
|
||||
self.configmanager = configmanager
|
||||
self._token = None
|
||||
self._wc = None
|
||||
self.username = None
|
||||
|
||||
@property
|
||||
def token(self):
|
||||
if not self._token:
|
||||
self._token = self.login(self.configmanager)
|
||||
return self._token
|
||||
|
||||
@property
|
||||
def wc(self):
|
||||
if self._wc:
|
||||
print("done cache")
|
||||
return self._wc
|
||||
print("loggin")
|
||||
targcfg = self.configmanager.get_node_attributes(
|
||||
self.node, ['hardwaremanagement.manager'], decrypt=True
|
||||
)
|
||||
targcfg = targcfg.get(self.node, {})
|
||||
target = targcfg.get('hardwaremanagement.manager', {}).get('value', None)
|
||||
if not target:
|
||||
target = self.node
|
||||
target = target.split('/', 1)[0]
|
||||
cv = util.TLSCertVerifier(
|
||||
self.configmanager, self.node, 'pubkeys.tls_hardwaremanager'
|
||||
).verify_cert
|
||||
self._wc = wc.SecureHTTPConnection(target, port=443, verifycallback=cv)
|
||||
return self._wc
|
||||
|
||||
def grab_json_response(self, url, body=None):
|
||||
rsp, status = self.wc.grab_json_response_with_status(url, body)
|
||||
if status == 401:
|
||||
self._token = None
|
||||
if body and 'cookie' in body:
|
||||
body['cookie'] = self.token
|
||||
rsp, status = self.wc.grab_json_response_with_status(url, body)
|
||||
if status < 300:
|
||||
return rsp
|
||||
return {}
|
||||
|
||||
def login(self, configmanager):
|
||||
credcfg = configmanager.get_node_attributes(
|
||||
self.node,
|
||||
['secret.hardwaremanagementuser', 'secret.hardwaremanagementpassword'],
|
||||
decrypt=True,
|
||||
)
|
||||
credcfg = credcfg.get(self.node, {})
|
||||
username = credcfg.get('secret.hardwaremanagementuser', {}).get('value', None)
|
||||
passwd = credcfg.get('secret.hardwaremanagementpassword', {}).get('value', None)
|
||||
|
||||
if not isinstance(username, str):
|
||||
username = username.decode('utf8')
|
||||
if not isinstance(passwd, str):
|
||||
passwd = passwd.decode('utf8')
|
||||
if not username or not passwd:
|
||||
raise Exception('Missing username or password')
|
||||
self.username = username
|
||||
rsp = self.wc.grab_json_response(
|
||||
'/xhrlogin.jsp',
|
||||
{'username': username, 'password': passwd, 'cookie': 0}
|
||||
)
|
||||
print(repr(rsp))
|
||||
self.authtoken = rsp['cookie']
|
||||
self.wc.set_header('Authorization', self.authtoken)
|
||||
return self.authtoken
|
||||
|
||||
def logout(self):
|
||||
if self._token:
|
||||
self.wc.grab_json_response(
|
||||
'/xhrlogout.jsp',
|
||||
{'timeout': 0, 'cookie': self._token},
|
||||
)
|
||||
self._token = None
|
||||
|
||||
def get_outlet(self, outlet):
|
||||
rsp = self.grab_json_response(
|
||||
'/xhroutgetgrid.jsp', {
|
||||
'cookie': self.token,
|
||||
'pduid': 1
|
||||
})
|
||||
outlets = rsp['outlet']
|
||||
for olet in outlets:
|
||||
if olet['id'] == int(outlet):
|
||||
state = "on" if olet['powstat'] == 1 else "off"
|
||||
return state
|
||||
|
||||
def set_outlet(self, outlet, state):
|
||||
bitflags = 2**(int(outlet) - 1)
|
||||
outlet1 = bitflags & (2**24-1)
|
||||
outlet2 = bitflags >> 24
|
||||
if state == 'off':
|
||||
state = 0
|
||||
elif state == 'on':
|
||||
state = 1
|
||||
else:
|
||||
raise Exception("Unrecognized state " + repr(state))
|
||||
request = {
|
||||
'cookie': self.token,
|
||||
'outlet1': outlet1,
|
||||
'outlet2': outlet2,
|
||||
'pduid': 1,
|
||||
'powstat': state
|
||||
}
|
||||
rsp = self.grab_json_response('/xhroutpowstatset.jsp', request)
|
||||
|
||||
|
||||
_sensors_by_node = {}
|
||||
|
||||
|
||||
def read_sensors(element, node, configmanager):
|
||||
category, name = element[-2:]
|
||||
justnames = False
|
||||
if len(element) == 3:
|
||||
# just get names
|
||||
category = name
|
||||
name = 'all'
|
||||
justnames = True
|
||||
if category in ('leds, fans', 'temperature'):
|
||||
return
|
||||
if justnames:
|
||||
yield msg.ChildCollection('total_energy')
|
||||
yield msg.ChildCollection('total_apparent_power')
|
||||
yield msg.ChildCollection('total_real_power')
|
||||
return
|
||||
sn = _sensors_by_node.get(node, None)
|
||||
if not sn or sn[1] < time.time():
|
||||
gc = get_client(node, configmanager)
|
||||
adev = gc.grab_json_response('/energy_get', {'cookie': gc.token, 'end': 1, 'start': 1})
|
||||
_sensors_by_node[node] = (adev, time.time() + 1)
|
||||
sn = _sensors_by_node.get(node, None)
|
||||
if sn:
|
||||
sn = sn[0]
|
||||
readings = [
|
||||
{
|
||||
'name': 'Total Energy',
|
||||
'value': float(sn[0]['total_energy']) * 0.001,
|
||||
'units': 'kWh',
|
||||
'type': 'Energy',
|
||||
},
|
||||
{
|
||||
'name': 'Total Real Power',
|
||||
'value': float(sn[0]['active_power']),
|
||||
'units': 'W',
|
||||
'type': 'Power',
|
||||
},
|
||||
{
|
||||
'name': 'Total Apparent Power',
|
||||
'value': float(sn[0]['apparent_power']),
|
||||
'units': 'W',
|
||||
'type': 'Power',
|
||||
},
|
||||
]
|
||||
yield msg.SensorReadings(readings, name=node)
|
||||
return
|
||||
|
||||
def get_client(node, configmanager):
|
||||
if node not in _pduclients:
|
||||
_pduclients[node] = EnlogicClient(node, configmanager)
|
||||
return _pduclients[node]
|
||||
|
||||
def get_outlet(element, node, configmanager):
|
||||
gc = get_client(node, configmanager)
|
||||
state = gc.get_outlet(element[-1])
|
||||
return msg.PowerState(node=node, state=state)
|
||||
|
||||
|
||||
def read_firmware(node, configmanager):
|
||||
gc = get_client(node, configmanager)
|
||||
adev = gc.grab_json_response('/xhrgetuserlist.jsp')
|
||||
myversion = adev[0]['fwver']
|
||||
yield msg.Firmware([{'PDU Firmware': {'version': myversion}}], node)
|
||||
|
||||
|
||||
def read_inventory(element, node, configmanager):
|
||||
_inventory = {}
|
||||
inventory = {}
|
||||
gc = get_client(node, configmanager)
|
||||
adev = gc.grab_json_response('/sys_info_get', {
|
||||
'cookie': gc.token, 'pduid': 1
|
||||
})
|
||||
inventory['present'] = True
|
||||
inventory['name'] = 'PDU'
|
||||
info = {}
|
||||
info['Serial Number'] = adev['pdu'][0]['serial_number']
|
||||
info['Product Name'] = adev['pdu'][0]['model']
|
||||
info['Model'] = adev['pdu'][0]['part_number']
|
||||
inventory['information'] = info
|
||||
yield msg.KeyValueData({'inventory': [inventory]}, node)
|
||||
|
||||
def retrieve(nodes, element, configmanager, inputdata):
|
||||
|
||||
if 'outlets' in element:
|
||||
gp = greenpool.GreenPile(pdupool)
|
||||
for node in nodes:
|
||||
|
||||
gp.spawn(get_outlet, element, node, configmanager)
|
||||
for res in gp:
|
||||
yield res
|
||||
|
||||
return
|
||||
elif element[0] == 'sensors':
|
||||
gp = greenpool.GreenPile(pdupool)
|
||||
for node in nodes:
|
||||
gp.spawn(read_sensors, element, node, configmanager)
|
||||
for rsp in gp:
|
||||
for datum in rsp:
|
||||
yield datum
|
||||
return
|
||||
elif '/'.join(element).startswith('inventory/firmware/all'):
|
||||
gp = greenpool.GreenPile(pdupool)
|
||||
for node in nodes:
|
||||
gp.spawn(read_firmware, node, configmanager)
|
||||
for rsp in gp:
|
||||
for datum in rsp:
|
||||
yield datum
|
||||
|
||||
elif '/'.join(element).startswith('inventory/hardware/all'):
|
||||
gp = greenpool.GreenPile(pdupool)
|
||||
for node in nodes:
|
||||
gp.spawn(read_inventory, element, node, configmanager)
|
||||
for rsp in gp:
|
||||
for datum in rsp:
|
||||
yield datum
|
||||
else:
|
||||
for node in nodes:
|
||||
yield msg.ConfluentResourceUnavailable(node, 'Not implemented')
|
||||
return
|
||||
|
||||
|
||||
def update(nodes, element, configmanager, inputdata):
|
||||
if 'outlets' not in element:
|
||||
for node in nodes:
|
||||
yield msg.ConfluentResourceUnavailable(node, 'Not implemented')
|
||||
return
|
||||
for node in nodes:
|
||||
gc = get_client(node, configmanager)
|
||||
newstate = inputdata.powerstate(node)
|
||||
gc.set_outlet(element[-1], newstate)
|
||||
eventlet.sleep(1)
|
||||
for res in retrieve(nodes, element, configmanager, inputdata):
|
||||
yield res
|
||||
@@ -1371,10 +1371,8 @@ class IpmiHandler:
|
||||
def identify(self):
|
||||
if 'update' == self.op:
|
||||
identifystate = self.inputdata.inputbynode[self.node] == 'on'
|
||||
if self.inputdata.inputbynode[self.node] == 'blink':
|
||||
raise exc.InvalidArgumentException(
|
||||
'"blink" is not supported with ipmi')
|
||||
self.ipmicmd.set_identify(on=identifystate)
|
||||
blinkstate = self.inputdata.inputbynode[self.node] == 'blink'
|
||||
self.ipmicmd.set_identify(on=identifystate, blink=blinkstate)
|
||||
self.output.put(msg.IdentifyState(
|
||||
node=self.node, state=self.inputdata.inputbynode[self.node]))
|
||||
return
|
||||
|
||||
@@ -52,7 +52,7 @@ def listdump(input):
|
||||
|
||||
|
||||
def get_extra_names(nodename, cfg, myip=None):
|
||||
names = set([])
|
||||
names = set(['127.0.0.1', '::1', 'localhost', 'localhost.localdomain'])
|
||||
dnsinfo = cfg.get_node_attributes(nodename, ('dns.*', 'net.*hostname'))
|
||||
dnsinfo = dnsinfo.get(nodename, {})
|
||||
domain = dnsinfo.get('dns.domain', {}).get('value', None)
|
||||
@@ -625,4 +625,8 @@ def get_cluster_list(nodename=None, cfg=None):
|
||||
nodes.add(myname)
|
||||
if domain and domain not in myname:
|
||||
nodes.add('{0}.{1}'.format(myname, domain))
|
||||
nodes.add('::1')
|
||||
nodes.add('127.0.0.1')
|
||||
nodes.add('localhost')
|
||||
nodes.add('localhost.domain')
|
||||
return nodes, domain
|
||||
|
||||
@@ -16,26 +16,36 @@ _vinztoken = None
|
||||
import socket
|
||||
import aiohmi.util.webclient as webclient
|
||||
|
||||
startingup = False
|
||||
|
||||
|
||||
# Handle the vinz VNC session
|
||||
async def assure_vinz():
|
||||
global _vinzfd
|
||||
global _vinztoken
|
||||
if _vinzfd is None:
|
||||
_vinztoken = base64.b64encode(os.urandom(33), altchars=b'_-').decode()
|
||||
os.environ['VINZ_TOKEN'] = _vinztoken
|
||||
os.makedirs('/var/run/confluent/vinz/sessions', exist_ok=True)
|
||||
|
||||
_vinzfd = await asyncio.subprocess.create_subprocess_exec(
|
||||
global startingup
|
||||
while startingup:
|
||||
await asyncio.sleep(0.5)
|
||||
try:
|
||||
startingup = True
|
||||
if _vinzfd is None:
|
||||
_vinztoken = base64.b64encode(os.urandom(33), altchars=b'_-').decode()
|
||||
os.environ['VINZ_TOKEN'] = _vinztoken
|
||||
os.makedirs('/var/run/confluent/vinz/sessions', exist_ok=True)
|
||||
os.chmod('/var/run/confluent/vinz', 0o711)
|
||||
os.chmod('/var/run/confluent/vinz/sessions', 0o711)
|
||||
_vinzfd = await asyncio.subprocess.create_subprocess_exec(
|
||||
'/opt/confluent/bin/vinz',
|
||||
'-c', '/var/run/confluent/vinz/control',
|
||||
'-w', '127.0.0.1:4007',
|
||||
'-a', '/var/run/confluent/vinz/approval',
|
||||
# vinz supports unix domain websocket, however apache reverse proxy is dicey that way in some versions
|
||||
'-d', '/var/run/confluent/vinz/sessions')
|
||||
while not os.path.exists('/var/run/confluent/vinz/control'):
|
||||
await asyncio.sleep(0.5)
|
||||
util.spawn(monitor_requests())
|
||||
while not os.path.exists('/var/run/confluent/vinz/control'):
|
||||
await asyncio.sleep(0.5)
|
||||
util.spawn(monitor_requests)
|
||||
finally:
|
||||
startingup = False
|
||||
|
||||
_unix_by_nodename = {}
|
||||
async def get_url(nodename, inputdata):
|
||||
|
||||
@@ -1,110 +1,323 @@
|
||||
import base64
|
||||
import confluent.tlvdata as tlvdata
|
||||
import confluent.util as util
|
||||
import json
|
||||
import pywarp
|
||||
import pywarp.backends
|
||||
import pywarp.credentials
|
||||
import copy
|
||||
import base64
|
||||
import secrets, time
|
||||
from typing import Any, Optional
|
||||
from webauthn import (
|
||||
generate_registration_options,
|
||||
options_to_json,
|
||||
generate_authentication_options,
|
||||
)
|
||||
from webauthn.helpers.structs import (
|
||||
AuthenticatorSelectionCriteria,
|
||||
UserVerificationRequirement,
|
||||
)
|
||||
|
||||
from webauthn import verify_registration_response
|
||||
from webauthn import verify_authentication_response
|
||||
|
||||
|
||||
challenges = {}
|
||||
|
||||
class ConfluentBackend(pywarp.backends.CredentialStorageBackend):
|
||||
def __init__(self, cfg):
|
||||
self.cfg = cfg
|
||||
CONFIG_MANAGER = None
|
||||
|
||||
def get_credential_ids_by_email(self, email):
|
||||
if not isinstance(email, str):
|
||||
email = email.decode('utf8')
|
||||
authenticators = self.cfg.get_user(email).get('authenticators', {})
|
||||
if not authenticators:
|
||||
raise Exception('No authenticators found')
|
||||
for cid in authenticators:
|
||||
yield base64.b64decode(cid)
|
||||
class Credential():
|
||||
def __init__(self, id, signature_count, public_key):
|
||||
self.id = id
|
||||
self.signature_count = signature_count
|
||||
self.credential_public_key = public_key
|
||||
|
||||
def get_credential_by_email_id(self, email, id):
|
||||
if not isinstance(email, str):
|
||||
email = email.decode('utf8')
|
||||
authenticators = self.cfg.get_user(email).get('authenticators', {})
|
||||
cid = base64.b64encode(id).decode('utf8')
|
||||
pk = authenticators[cid]['cpk']
|
||||
pk = base64.b64decode(pk)
|
||||
return pywarp.credentials.Credential(credential_id=id, credential_public_key=pk)
|
||||
class Challenge():
|
||||
def __init__(self, request, id=None) -> None:
|
||||
if id is None:
|
||||
self.id = util.randomstring(16)
|
||||
else:
|
||||
self.id = id
|
||||
self.request = request
|
||||
|
||||
def get_credential_by_email(self, email):
|
||||
if not isinstance(email, str):
|
||||
email = email.decode('utf8')
|
||||
authenticators = self.cfg.get_user(email)
|
||||
cid = list(authenticators)[0]
|
||||
cred = authenticators[cid]
|
||||
cid = base64.b64decode(cred['cid'])
|
||||
cpk = base64.b64decode(cred['cpk'])
|
||||
return pywarp.credentials.Credential(credential_id=cid, credential_public_key=cpk)
|
||||
def _load_credentials(creds):
|
||||
if creds is None:
|
||||
return None
|
||||
ret = copy.deepcopy(creds)
|
||||
ret['credential_public_key'] = base64.b64decode(creds['credential_public_key'])
|
||||
ret['id'] = base64.b64decode(creds['id'])
|
||||
return ret
|
||||
|
||||
def save_credential_for_user(self, email, credential):
|
||||
if not isinstance(email, str):
|
||||
email = email.decode('utf8')
|
||||
cid = base64.b64encode(credential.id).decode('utf8')
|
||||
credential = {'cid': cid, 'cpk': base64.b64encode(bytes(credential.public_key)).decode('utf8')}
|
||||
authenticators = self.cfg.get_user(email).get('authenticators', {})
|
||||
authenticators[cid] = credential
|
||||
self.cfg.set_user(email, {'authenticators': authenticators})
|
||||
def _load_authenticators(authenticators):
|
||||
ret = authenticators
|
||||
if 'challenges' in ret:
|
||||
if not ret['challenges'] is None:
|
||||
ret['challenges']['request'] = base64.b64decode(ret['challenges']['request'])
|
||||
if 'credentials' in ret:
|
||||
ret['credentials'] = _load_credentials(ret['credentials'])
|
||||
return ret
|
||||
|
||||
def save_challenge_for_user(self, email, challenge, type):
|
||||
if not isinstance(email, str):
|
||||
email = email.decode('utf8')
|
||||
challenges[email] = challenge
|
||||
class User():
|
||||
def __init__(self, id, username, user_handle, challenge: Challenge = None, credential: Credential = None):
|
||||
self.id = id
|
||||
self.username = username
|
||||
self.user_handle = user_handle
|
||||
self.challenges = challenge
|
||||
self.credentials = credential
|
||||
|
||||
def get_challenge_for_user(self, email, type):
|
||||
if not isinstance(email, str):
|
||||
email = email.decode('utf8')
|
||||
return challenges[email]
|
||||
def __parse_credentials(self):
|
||||
if self.credentials:
|
||||
credid = base64.b64encode(self.credentials.id).decode()
|
||||
pubkey = base64.b64encode(self.credentials.credential_public_key).decode()
|
||||
return {"id": credid, "signature_count": self.credentials.signature_count, "credential_public_key": pubkey}
|
||||
|
||||
|
||||
def __parse_challenges(self):
|
||||
if self.challenges:
|
||||
request = base64.b64encode(self.challenges.request).decode()
|
||||
return {"id": self.challenges.id, 'request': request}
|
||||
|
||||
|
||||
@staticmethod
|
||||
def seek_credential_by_id(credential_id):
|
||||
"""
|
||||
There certainly is a better way to do this but for now lets try the wrong way that works
|
||||
"""
|
||||
for username in CONFIG_MANAGER.list_users():
|
||||
authenticators = CONFIG_MANAGER.get_user(username).get('authenticators', {})
|
||||
authenticators = _load_authenticators(authenticators)
|
||||
try:
|
||||
credential = authenticators['credentials']
|
||||
except KeyError:
|
||||
continue
|
||||
if "id" in credential.keys() and credential["id"] == credential_id:
|
||||
#for now leaving signature count as None
|
||||
return (Credential(id=credential["id"], signature_count=None, public_key=credential["credential_public_key"]), username)
|
||||
return None
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_credential(credential_id, username):
|
||||
if not isinstance(username, str):
|
||||
username = username.decode('utf8')
|
||||
authenticators = CONFIG_MANAGER.get_user(username).get('authenticators', {})
|
||||
authenticators = _load_authenticators(authenticators)
|
||||
credential = authenticators.get('credentials', None)
|
||||
if credential is None:
|
||||
return None
|
||||
|
||||
if credential_id is None:
|
||||
return Credential(id=credential["id"], signature_count=credential["signature_count"], public_key=credential["credential_public_key"])
|
||||
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_challenge(username):
|
||||
if not isinstance(username, str):
|
||||
username = username.decode('utf8')
|
||||
authuser = CONFIG_MANAGER.get_user(username)
|
||||
if not authuser:
|
||||
return None
|
||||
authenticators = authuser.get('authenticators', {})
|
||||
authenticators = _load_authenticators(authenticators)
|
||||
challenge = authenticators['challenges']
|
||||
return Challenge(request=challenge["request"], id=challenge["id"])
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get(username):
|
||||
challenges_return = None
|
||||
credentials_return = None
|
||||
if not CONFIG_MANAGER:
|
||||
raise Exception('config manager is not set up')
|
||||
if not isinstance(username, str):
|
||||
username = username.decode('utf8')
|
||||
userinfo = CONFIG_MANAGER.get_user(username)
|
||||
try:
|
||||
authenticators = CONFIG_MANAGER.get_user(username).get('authenticators', {})
|
||||
except AttributeError:
|
||||
return None
|
||||
if userinfo is None:
|
||||
return None
|
||||
authenticators = _load_authenticators(authenticators)
|
||||
b64authid = userinfo.get('webauthid', None)
|
||||
if b64authid is None:
|
||||
authid = None
|
||||
else:
|
||||
authid = base64.b64decode(b64authid)
|
||||
challenge = authenticators.get("challenges", None)
|
||||
if challenge:
|
||||
challenges_return = Challenge(challenge['request'], id=challenge["id"])
|
||||
|
||||
credential = authenticators.get("credentials", None)
|
||||
if credential:
|
||||
credentials_return = (Credential(credential['id'], credential['signature_count'], credential["credential_public_key"]))
|
||||
|
||||
return User(id=None, username=username, user_handle=authid, challenge=challenges_return, credential=credentials_return)
|
||||
|
||||
def save(self):
|
||||
authenticators = CONFIG_MANAGER.get_user(self.username).get('authenticators', {})
|
||||
authenticators = _load_authenticators(authenticators)
|
||||
authenticators['challenges'] = self.__parse_challenges() # Looks like the bigger the array we encounter problems changing to just save one challenge
|
||||
authenticators['credentials'] = self.__parse_credentials()
|
||||
|
||||
CONFIG_MANAGER.set_user(self.username, {'authenticators': authenticators})
|
||||
|
||||
|
||||
def add(self, item):
|
||||
if isinstance(item, Challenge):
|
||||
self.challenges = item
|
||||
elif isinstance(item, Credential):
|
||||
self.credentials = item
|
||||
|
||||
def update(self, item):
|
||||
if isinstance(item, Challenge):
|
||||
self.challenges = item
|
||||
elif isinstance(item, Credential):
|
||||
self.credentials = item
|
||||
return
|
||||
#raise Exception("Credential item not found")
|
||||
|
||||
|
||||
def registration_request(username, cfg, APP_RELYING_PARTY):
|
||||
user_model = User.get(username)
|
||||
if user_model is None:
|
||||
raise Exception("User not foud")
|
||||
|
||||
options = generate_registration_options(
|
||||
rp_name=APP_RELYING_PARTY.name,
|
||||
rp_id=APP_RELYING_PARTY.id,
|
||||
user_id=user_model.user_handle,
|
||||
user_name=username,
|
||||
authenticator_selection=AuthenticatorSelectionCriteria(
|
||||
user_verification=UserVerificationRequirement.REQUIRED,
|
||||
),
|
||||
)
|
||||
|
||||
challenge = Challenge(options.challenge)
|
||||
user_model.add(challenge)
|
||||
user_model.save()
|
||||
options_json = options_to_json(options)
|
||||
return options_json
|
||||
|
||||
|
||||
def registration_response(request, username, APP_RELYING_PARTY, APP_ORIGIN):
|
||||
challenge_model = User.get_challenge(username)
|
||||
if not challenge_model:
|
||||
raise Exception("Could not find challenge matching given id")
|
||||
|
||||
user_model = User.get(username)
|
||||
if not user_model:
|
||||
raise Exception("Invalid Username")
|
||||
|
||||
try:
|
||||
registration_verification = verify_registration_response(
|
||||
credential=request,
|
||||
expected_challenge=challenge_model.request,
|
||||
expected_rp_id=APP_RELYING_PARTY.id,
|
||||
expected_origin=APP_ORIGIN,
|
||||
require_user_verification=True,
|
||||
)
|
||||
except Exception as err:
|
||||
raise Exception("Could not handle credential attestation")
|
||||
|
||||
credential = Credential(id=registration_verification.credential_id, signature_count=registration_verification.sign_count, public_key=registration_verification.credential_public_key)
|
||||
user_model.add(credential)
|
||||
user_model.save()
|
||||
|
||||
return {"verified": True}
|
||||
|
||||
|
||||
def authentication_request(username, APP_RELYING_PARTY):
|
||||
user_model = User.get(username)
|
||||
if not user_model:
|
||||
raise Exception("Invalid Username")
|
||||
|
||||
options = generate_authentication_options(
|
||||
rp_id=APP_RELYING_PARTY.id,
|
||||
user_verification=UserVerificationRequirement.REQUIRED,
|
||||
)
|
||||
|
||||
challenge = Challenge(options.challenge)
|
||||
user_model.add(challenge)
|
||||
user_model.save()
|
||||
opts = options_to_json(options)
|
||||
return opts
|
||||
|
||||
def authentication_response(request, username, APP_RELYING_PARTY, APP_ORIGIN):
|
||||
user_model = User.get(username)
|
||||
if not user_model:
|
||||
raise Exception("Invalid Username")
|
||||
|
||||
challenge_model = User.get_challenge(username)
|
||||
if not challenge_model:
|
||||
raise Exception("Could not find challenge matching given id")
|
||||
|
||||
credential_model = User.get_credential(credential_id=None, username=username)
|
||||
if not credential_model:
|
||||
raise Exception("No credential for user")
|
||||
|
||||
verification = verify_authentication_response(
|
||||
credential=request,
|
||||
expected_challenge=challenge_model.request,
|
||||
expected_rp_id=APP_RELYING_PARTY.id,
|
||||
expected_origin=APP_ORIGIN,
|
||||
credential_public_key = credential_model.credential_public_key,
|
||||
credential_current_sign_count = 0,
|
||||
require_user_verification = True
|
||||
|
||||
)
|
||||
|
||||
return {"verified": True}
|
||||
|
||||
class RpEntity(object):
|
||||
def __init__(self, name, id):
|
||||
self.name = name
|
||||
self.id = id
|
||||
|
||||
def handle_api_request(url, env, start_response, username, cfm, headers, reqbody, authorized):
|
||||
"""
|
||||
For now webauth is going to be limited to just one passkey per user
|
||||
If you try to register a new passkey this will just clear the old one and register the new passkey
|
||||
"""
|
||||
global CONFIG_MANAGER
|
||||
CONFIG_MANAGER = cfm
|
||||
|
||||
APP_ORIGIN = 'https://' + env['HTTP_X_FORWARDED_HOST']
|
||||
HOST = env['HTTP_X_FORWARDED_HOST']
|
||||
APP_RELYING_PARTY = RpEntity(name='Confluent Web UI', id=HOST)
|
||||
|
||||
if env['REQUEST_METHOD'] != 'POST':
|
||||
raise Exception('Only POST supported for webauthn operations')
|
||||
url = url.replace('/sessions/current/webauthn', '')
|
||||
if url == '/registration_options':
|
||||
rp = pywarp.RelyingPartyManager('Confluent Web UI', credential_storage_backend=ConfluentBackend(cfm), require_attestation=False)
|
||||
userinfo = cfm.get_user(username)
|
||||
if not userinfo:
|
||||
cfm.create_user(username, role='Stub')
|
||||
userinfo = cfm.get_user(username)
|
||||
authid = userinfo.get('authid', None)
|
||||
authid = userinfo.get('webauthid', None)
|
||||
if not authid:
|
||||
authid = util.randomstring(64)
|
||||
cfm.set_user(username, {'authid': authid})
|
||||
opts = rp.get_registration_options(username)
|
||||
# pywarp generates an id derived
|
||||
# from username, which is a 'must not' in the spec
|
||||
# we replace that with a complying approach
|
||||
opts['user']['id'] = authid
|
||||
if 'icon' in opts['user']:
|
||||
del opts['user']['icon']
|
||||
if 'id' in opts['rp']:
|
||||
del opts['rp']['id']
|
||||
authid = secrets.token_bytes(64)
|
||||
b64authid = base64.b64encode(authid).decode()
|
||||
cfm.set_user(username, {'webauthid': b64authid})
|
||||
opts = registration_request(username, cfm, APP_RELYING_PARTY)
|
||||
start_response('200 OK', headers)
|
||||
yield json.dumps(opts)
|
||||
yield opts
|
||||
elif url.startswith('/registered_credentials/'):
|
||||
username = url.rsplit('/', 1)[-1]
|
||||
rp = pywarp.RelyingPartyManager('Confluent Web UI', credential_storage_backend=ConfluentBackend(cfm))
|
||||
userinfo = cfm.get_user(username)
|
||||
if not isinstance(username, bytes):
|
||||
username = username.encode('utf8')
|
||||
opts = rp.get_authentication_options(username)
|
||||
opts['challenge'] = base64.b64encode(opts['challenge']).decode('utf8')
|
||||
opts = authentication_request(username, APP_RELYING_PARTY)
|
||||
start_response('200 OK', headers)
|
||||
yield json.dumps(opts)
|
||||
yield opts
|
||||
elif url.startswith('/validate/'):
|
||||
username = url.rsplit('/', 1)[-1]
|
||||
userinfo = cfm.get_user(username)
|
||||
if not isinstance(username, bytes):
|
||||
username = username.encode('utf8')
|
||||
rp = pywarp.RelyingPartyManager('Confluent Web UI', credential_storage_backend=ConfluentBackend(cfm))
|
||||
req = json.loads(reqbody)
|
||||
for x in req:
|
||||
req[x] = base64.b64decode(req[x].replace('-', '+').replace('_', '/'))
|
||||
req['email'] = username
|
||||
rsp = rp.verify(**req)
|
||||
if start_response:
|
||||
rsp = authentication_response(req, username, APP_RELYING_PARTY, APP_ORIGIN)
|
||||
if rsp == 'Timeout':
|
||||
start_response('408 Timeout', headers)
|
||||
elif rsp['verified'] and start_response:
|
||||
start_response('200 OK', headers)
|
||||
sessinfo = {'username': username}
|
||||
if 'authtoken' in authorized:
|
||||
@@ -116,13 +329,13 @@ def handle_api_request(url, env, start_response, username, cfm, headers, reqbody
|
||||
else:
|
||||
yield rsp
|
||||
elif url == '/register_credential':
|
||||
rp = pywarp.RelyingPartyManager('Confluent Web UI', credential_storage_backend=ConfluentBackend(cfm), require_attestation=False)
|
||||
req = json.loads(reqbody)
|
||||
for x in req:
|
||||
req[x] = base64.b64decode(req[x].replace('-', '+').replace('_', '/'))
|
||||
userinfo = cfm.get_user(username)
|
||||
if not isinstance(username, bytes):
|
||||
username = username.encode('utf8')
|
||||
req['email'] = username
|
||||
rsp = rp.register(**req)
|
||||
start_response('200 OK', headers)
|
||||
yield json.dumps(rsp)
|
||||
rsp = registration_response(req, username, APP_RELYING_PARTY, APP_ORIGIN)
|
||||
if rsp.get('verified', False):
|
||||
start_response('200 OK', headers)
|
||||
yield json.dumps({'status': 'Success'})
|
||||
|
||||
|
||||
|
||||
@@ -18,15 +18,17 @@ Prefix: %{_prefix}
|
||||
BuildArch: noarch
|
||||
Requires: confluent_vtbufferd
|
||||
%if "%{dist}" == ".el7"
|
||||
Requires: python-pyghmi >= 1.5.71, python-eventlet, python-greenlet, python-pycryptodomex >= 3.4.7, confluent_client == %{version}, python-pyparsing, python-paramiko, python-dnspython, python-netifaces, python2-pyasn1 >= 0.2.3, python-pysnmp >= 4.3.4, python-lxml, python-eficompressor, python-setuptools, python-dateutil, python-websocket-client python2-msgpack python-libarchive-c python-yaml python-monotonic cpio
|
||||
|
||||
Requires: python-pyghmi >= 1.5.71, python-eventlet, python-greenlet, python-pycryptodomex >= 3.4.7, confluent_client == %{version}, python-pyparsing, python-paramiko, python-dnspython, python-netifaces, python2-pyasn1 >= 0.2.3, python-pysnmp >= 4.3.4, python-lxml, python-eficompressor, python-setuptools, python-dateutil, python-websocket-client python2-msgpack python-libarchive-c python-yaml python-monotonic
|
||||
%else
|
||||
%if "%{dist}" == ".el8"
|
||||
Requires: python3-pyghmi >= 1.5.71, python3-eventlet, python3-greenlet, python3-pycryptodomex >= 3.4.7, confluent_client == %{version}, python3-pyparsing, python3-paramiko, python3-dns, python3-netifaces, python3-pyasn1 >= 0.2.3, python3-pysnmp >= 4.3.4, python3-lxml, python3-eficompressor, python3-setuptools, python3-dateutil, python3-enum34, python3-asn1crypto, python3-cffi, python3-pyOpenSSL, python3-websocket-client python3-msgpack python3-libarchive-c python3-yaml openssl iproute cpio
|
||||
Requires: python3-pyghmi >= 1.5.71, python3-eventlet, python3-greenlet, python3-pycryptodomex >= 3.4.7, confluent_client == %{version}, python3-pyparsing, python3-paramiko, python3-dns, python3-netifaces, python3-pyasn1 >= 0.2.3, python3-pysnmp >= 4.3.4, python3-lxml, python3-eficompressor, python3-setuptools, python3-dateutil, python3-enum34, python3-asn1crypto, python3-cffi, python3-pyOpenSSL, python3-websocket-client python3-msgpack python3-libarchive-c python3-yaml openssl iproute
|
||||
%else
|
||||
%if "%{dist}" == ".el9"
|
||||
Requires: python3-pyghmi >= 1.5.71, python3-eventlet, python3-greenlet, python3-pycryptodomex >= 3.4.7, confluent_client == %{version}, python3-pyparsing, python3-paramiko, python3-dns, python3-netifaces, python3-pyasn1 >= 0.2.3, python3-pysnmp >= 4.3.4, python3-lxml, python3-eficompressor, python3-setuptools, python3-dateutil, python3-cffi, python3-pyOpenSSL, python3-websocket-client python3-msgpack python3-libarchive-c python3-yaml openssl iproute cpio
|
||||
Requires: python3-pyghmi >= 1.5.71, python3-eventlet, python3-greenlet, python3-pycryptodomex >= 3.4.7, confluent_client == %{version}, python3-pyparsing, python3-paramiko, python3-dns, python3-webauthn, python3-netifaces, python3-pyasn1 >= 0.2.3, python3-pysnmp >= 4.3.4, python3-lxml, python3-eficompressor, python3-setuptools, python3-dateutil, python3-cffi, python3-pyOpenSSL, python3-websocket-client python3-msgpack python3-libarchive-c python3-yaml openssl iproute
|
||||
%else
|
||||
Requires: python3-dbm,python3-pyghmi >= 1.5.71, python3-eventlet, python3-greenlet, python3-pycryptodome >= 3.4.7, confluent_client == %{version}, python3-pyparsing, python3-paramiko, python3-dnspython, python3-netifaces, python3-pyasn1 >= 0.2.3, python3-pysnmp >= 4.3.4, python3-lxml, python3-eficompressor, python3-setuptools, python3-dateutil, python3-cffi, python3-pyOpenSSL, python3-websocket-client python3-msgpack python3-libarchive-c python3-PyYAML openssl iproute
|
||||
|
||||
%endif
|
||||
%endif
|
||||
%endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
%define arch x86_64
|
||||
Version: 3.10.0
|
||||
Version: 3.12.0
|
||||
Release: 1
|
||||
Name: confluent-genesis-%{arch}
|
||||
BuildArch: noarch
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
Copyright and licensing for additional files included in the bash package:
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Files: lib/sh/inet_aton.c
|
||||
Copyright: 1983, 1990, 1993 The Regents of the University of California. All rights reserved.
|
||||
License: BSD-4-UC AND HPND
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
* -
|
||||
* Portions Copyright (c) 1993 by Digital Equipment Corporation.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies, and that
|
||||
* the name of Digital Equipment Corporation not be used in advertising or
|
||||
* publicity pertaining to distribution of the document or software without
|
||||
* specific, written prior permission.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
|
||||
* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
|
||||
* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
||||
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||
* SOFTWARE.
|
||||
* -
|
||||
* --Copyright--
|
||||
@@ -0,0 +1,157 @@
|
||||
Copyright and licensing for additional files included in the dhcp package:
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Files:
|
||||
server/ldap_krb_helper.c
|
||||
omapip/inet_addr.c
|
||||
Copyright: Copyright (c) 2015 by Internet Systems Consortium, Inc. ("ISC") All rights reserved.
|
||||
License: BSD-3
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of The Internet Software Consortium nor the names
|
||||
* of its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
|
||||
* CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* This helper was written by William Brown <william@adelaide.edu.au>,
|
||||
* inspired by krb5_helper.c from bind-dyndb-ldap by Simo Sorce (Redhat)
|
||||
|
||||
Files: server/ldap_casa.c
|
||||
Copyright: Copyright (c) 2006 Novell, Inc.
|
||||
License: BSD-3 AND ISC
|
||||
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1.Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2.Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3.Neither the name of ISC, ISC DHCP, nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
|
||||
* THIS SOFTWARE IS PROVIDED BY INTERNET SYSTEMS CONSORTIUM AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ISC OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
* This file was written by S Kalyanasundaram <skalyanasundaram@novell.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC")
|
||||
* Copyright (c) 1995-2003 by Internet Software Consortium
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* Internet Systems Consortium, Inc.
|
||||
* 950 Charter Street
|
||||
* Redwood City, CA 94063
|
||||
* <info@isc.org>
|
||||
* https://www.isc.org/
|
||||
|
||||
Files: server/ldap.c.cloexec
|
||||
server/ldap.c
|
||||
Copyright:
|
||||
Copyright (c) 2010,2015-2016 by Internet Systems Consortium, Inc. ("ISC")
|
||||
Copyright (c) 2003-2006 Ntelos, Inc.
|
||||
All rights reserved.
|
||||
License: BSD-3
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of The Internet Software Consortium nor the names
|
||||
* of its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
|
||||
* CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* This LDAP module was written by Brian Masney <masneyb@ntelos.net>. Its
|
||||
* development was sponsored by Ntelos, Inc. (www.ntelos.com).
|
||||
|
||||
Files: omapip/inet_addr.c
|
||||
Copyright: Copyright (c) 1983, 1990, 1993 The Regents of the University of California. All rights reserved.
|
||||
License: BSD-3-UC
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
@@ -0,0 +1,238 @@
|
||||
Additional license notices for Libgcrypt. -*- org -*-
|
||||
|
||||
This file contains the copying permission notices for various files in
|
||||
the Libgcrypt distribution which are not covered by the GNU Lesser
|
||||
General Public License (LGPL) or the GNU General Public License (GPL).
|
||||
|
||||
These notices all require that a copy of the notice be included
|
||||
in the accompanying documentation and be distributed with binary
|
||||
distributions of the code, so be sure to include this file along
|
||||
with any binary distributions derived from the GNU C Library.
|
||||
|
||||
* BSD_3Clause
|
||||
|
||||
For files:
|
||||
- cipher/sha256-avx-amd64.S
|
||||
- cipher/sha256-avx2-bmi2-amd64.S
|
||||
- cipher/sha256-ssse3-amd64.S
|
||||
- cipher/sha512-avx-amd64.S
|
||||
- cipher/sha512-avx2-bmi2-amd64.S
|
||||
- cipher/sha512-ssse3-amd64.S
|
||||
|
||||
#+begin_quote
|
||||
Copyright (c) 2012, Intel Corporation
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of the Intel Corporation nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#+end_quote
|
||||
|
||||
|
||||
For files:
|
||||
- random/jitterentropy-base.c
|
||||
- random/jitterentropy.h
|
||||
- random/rndjent.c (plus common Libgcrypt copyright holders)
|
||||
|
||||
#+begin_quote
|
||||
* Copyright Stephan Mueller <smueller@chronox.de>, 2013
|
||||
*
|
||||
* License
|
||||
* =======
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, and the entire permission notice in its entirety,
|
||||
* including the disclaimer of warranties.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior
|
||||
* written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this product may be distributed under the terms of
|
||||
* the GNU General Public License, in which case the provisions of the GPL are
|
||||
* required INSTEAD OF the above restrictions. (This clause is
|
||||
* necessary due to a potential bad interaction between the GPL and
|
||||
* the restrictions contained in a BSD-style copyright.)
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
|
||||
* WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
#+end_quote
|
||||
|
||||
* X License
|
||||
|
||||
For files:
|
||||
- install.sh
|
||||
|
||||
#+begin_quote
|
||||
Copyright (C) 1994 X Consortium
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
|
||||
TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of the X Consortium shall not
|
||||
be used in advertising or otherwise to promote the sale, use or other deal-
|
||||
ings in this Software without prior written authorization from the X Consor-
|
||||
tium.
|
||||
#+end_quote
|
||||
|
||||
* Public domain
|
||||
|
||||
For files:
|
||||
- cipher/arcfour-amd64.S
|
||||
|
||||
#+begin_quote
|
||||
Author: Marc Bevand <bevand_m (at) epita.fr>
|
||||
Licence: I hereby disclaim the copyright on this code and place it
|
||||
in the public domain.
|
||||
#+end_quote
|
||||
|
||||
* OCB license 1
|
||||
|
||||
For files:
|
||||
- cipher/cipher-ocb.c
|
||||
|
||||
#+begin_quote
|
||||
OCB is covered by several patents but may be used freely by most
|
||||
software. See http://web.cs.ucdavis.edu/~rogaway/ocb/license.htm .
|
||||
In particular license 1 is suitable for Libgcrypt: See
|
||||
http://web.cs.ucdavis.edu/~rogaway/ocb/license1.pdf for the full
|
||||
license document; it basically says:
|
||||
|
||||
License 1 — License for Open-Source Software Implementations of OCB
|
||||
(Jan 9, 2013)
|
||||
|
||||
Under this license, you are authorized to make, use, and
|
||||
distribute open-source software implementations of OCB. This
|
||||
license terminates for you if you sue someone over their
|
||||
open-source software implementation of OCB claiming that you have
|
||||
a patent covering their implementation.
|
||||
|
||||
|
||||
|
||||
License for Open Source Software Implementations of OCB
|
||||
January 9, 2013
|
||||
|
||||
1 Definitions
|
||||
|
||||
1.1 “Licensor” means Phillip Rogaway.
|
||||
|
||||
1.2 “Licensed Patents” means any patent that claims priority to United
|
||||
States Patent Application No. 09/918,615 entitled “Method and Apparatus
|
||||
for Facilitating Efficient Authenticated Encryption,” and any utility,
|
||||
divisional, provisional, continuation, continuations-in-part, reexamination,
|
||||
reissue, or foreign counterpart patents that may issue with respect to the
|
||||
aforesaid patent application. This includes, but is not limited to, United
|
||||
States Patent No. 7,046,802; United States Patent No. 7,200,227; United
|
||||
States Patent No. 7,949,129; United States Patent No. 8,321,675 ; and any
|
||||
patent that issues out of United States Patent Application No. 13/669,114.
|
||||
|
||||
1.3 “Use” means any practice of any invention claimed in the Licensed Patents.
|
||||
|
||||
1.4 “Software Implementation” means any practice of any invention
|
||||
claimed in the Licensed Patents that takes the form of software executing on
|
||||
a user-programmable, general-purpose computer or that takes the form of a
|
||||
computer-readable medium storing such software. Software Implementation does
|
||||
not include, for example, application-specific integrated circuits (ASICs),
|
||||
field-programmable gate arrays (FPGAs), embedded systems, or IP cores.
|
||||
|
||||
1.5 “Open Source Software” means software whose source code is published
|
||||
and made available for inspection and use by anyone because either (a) the
|
||||
source code is subject to a license that permits recipients to copy, modify,
|
||||
and distribute the source code without payment of fees or royalties, or
|
||||
(b) the source code is in the public domain, including code released for
|
||||
public use through a CC0 waiver. All licenses certified by the Open Source
|
||||
Initiative at opensource.org as of January 9, 2013 and all Creative Commons
|
||||
licenses identified on the creativecommons.org website as of January 9,
|
||||
2013, including the Public License Fallback of the CC0 waiver, satisfy these
|
||||
requirements for the purposes of this license.
|
||||
|
||||
1.6 “Open Source Software Implementation” means a Software
|
||||
Implementation in which the software implicating the Licensed Patents is
|
||||
Open Source Software. Open Source Software Implementation does not include
|
||||
any Software Implementation in which the software implicating the Licensed
|
||||
Patents is combined, so as to form a larger program, with software that is
|
||||
not Open Source Software.
|
||||
|
||||
2 License Grant
|
||||
|
||||
2.1 License. Subject to your compliance with the term s of this license,
|
||||
including the restriction set forth in Section 2.2, Licensor hereby
|
||||
grants to you a perpetual, worldwide, non-exclusive, non-transferable,
|
||||
non-sublicenseable, no-charge, royalty-free, irrevocable license to practice
|
||||
any invention claimed in the Licensed Patents in any Open Source Software
|
||||
Implementation.
|
||||
|
||||
2.2 Restriction. If you or your affiliates institute patent litigation
|
||||
(including, but not limited to, a cross-claim or counterclaim in a lawsuit)
|
||||
against any entity alleging that any Use authorized by this license
|
||||
infringes another patent, then any rights granted to you under this license
|
||||
automatically terminate as of the date such litigation is filed.
|
||||
|
||||
3 Disclaimer
|
||||
YOUR USE OF THE LICENSED PATENTS IS AT YOUR OWN RISK AND UNLESS REQUIRED
|
||||
BY APPLICABLE LAW, LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
|
||||
KIND CONCERNING THE LICENSED PATENTS OR ANY PRODUCT EMBODYING ANY LICENSED
|
||||
PATENT, EXPRESS OR IMPLIED, STATUT ORY OR OTHERWISE, INCLUDING, WITHOUT
|
||||
LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE, OR NONINFRINGEMENT. IN NO EVENT WILL LICENSOR BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM OR RELATED TO ANY USE OF THE LICENSED PATENTS, INCLUDING,
|
||||
WITHOUT LIMITATION, DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE
|
||||
OR SPECIAL DAMAGES, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES PRIOR TO SUCH AN OCCURRENCE.
|
||||
#+end_quote
|
||||
@@ -0,0 +1,54 @@
|
||||
Copyright and licensing for additional files included in the libsepol package:
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Files: cil/*
|
||||
Copyright: Copyright 2011, 2014 Tresys Technology, LLC. All rights reserved.
|
||||
License: BSD-2
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY TRESYS TECHNOLOGY, LLC ``AS IS'' AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL TRESYS TECHNOLOGY, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those
|
||||
* of the authors and should not be interpreted as representing official policies,
|
||||
* either expressed or implied, of Tresys Technology, LLC.
|
||||
|
||||
Files: cil/test/unit/CuTest.c
|
||||
Copyright: Copyright (c) 2003 Asim Jalis
|
||||
License: Zlib
|
||||
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in
|
||||
* a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not
|
||||
* be misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source
|
||||
* distribution.
|
||||
@@ -76,7 +76,7 @@ manuallicenses = [
|
||||
'/usr/share/licenses/lz4/LICENSE.BSD',
|
||||
'/usr/share/licenses/nss/LICENSE.APACHE', # http://www.apache.org/licenses/LICENSE-2.0
|
||||
'/usr/share/licenses/openssh/COPYING.blowfish', # from header of blowfish file in bsd-compat
|
||||
'/usr/share/licenses/bc/COPYING.GPLv2',
|
||||
'/usr/share/licenses/bc/COPYING.GPLv2', # generic copy of GPLv2
|
||||
'/usr/share/licenses/bind-license/LICENSE', # MPLv2 from the source code
|
||||
'/usr/share/licenses/procps-ng/COPYING.LIBv2.1', # fetched internet
|
||||
# cp /usr/share/doc/lz4-libs/LICENSE /usr/share/licenses/lz4/LICENSE.BSD
|
||||
@@ -87,7 +87,7 @@ manuallicenses = [
|
||||
'/usr/share/licenses/pcre/LICENSE.BSD2', # stack-less just in time compiler, Zoltan Herzeg
|
||||
'/usr/share/licenses/sqlite/LICENSE.md', # https://raw.githubusercontent.com/sqlite/sqlite/master/LICENSE.md
|
||||
'/usr/share/licenses/pcre2/LICENSE.BSD2',
|
||||
'/usr/share/licenses/dhcp-common/NOTICE',
|
||||
'/usr/share/licenses/dhcp-common/NOTICE', # from exlicenses
|
||||
'/usr/share/licenses/xz/COPYING.GPLv3', # manually extracted from xz source
|
||||
'/usr/share/licenses/bash/NOTICE',
|
||||
'/usr/share/licenses/libsepol/NOTICE',
|
||||
@@ -111,6 +111,7 @@ manuallicenses = [
|
||||
'/usr/share/licenses/tmux/NOTICE', # built by extracttmuxlicenses.py
|
||||
'/usr/share/licenses/tmux/COPYING', # extracted from source
|
||||
'/usr/share/licenses/tmux/README', # extracted from source
|
||||
# yum download kernel soruce, cp -a from LICENSES to kernel-extra
|
||||
'/usr/share/licenses/kernel-extra/preferred/BSD-2-Clause',
|
||||
'/usr/share/licenses/kernel-extra/preferred/BSD-3-Clause',
|
||||
'/usr/share/licenses/kernel-extra/preferred/BSD-3-Clause-Clear',
|
||||
@@ -132,10 +133,10 @@ manuallicenses = [
|
||||
'/usr/share/licenses/kernel-extra/exceptions/GCC-exception-2.0',
|
||||
'/usr/share/licenses/kernel-extra/exceptions/Linux-syscall-note',
|
||||
'/usr/share/licenses/util-linux/COPYING.GPLv3', # extract from parse-date.c, from srpm
|
||||
'/usr/share/licenses/kmod/COPYING', # GPL not LGPL, must extract from kmod srpm
|
||||
'/usr/share/licenses/kmod/COPYING', # GPL not LGPL, must extract from kmod srpm, tools subdir
|
||||
'/usr/share/licenses/krb5-libs/NOTICE', # copy it verbatim from LICENSE, exact same file
|
||||
'/usr/share/doc/less/README',
|
||||
'/usr/share/centos-release/EULA',
|
||||
'/usr/share/almalinux-release/EULA',
|
||||
#'/usr/share/doc/almalinux-release/GPL',
|
||||
'/usr/share/licenses/libcap-ng-utils/COPYING',
|
||||
'/usr/share/licenses/libdb/copyright', # from libdb, db-5.3.28, lang/sql/odbc/debian/copyright
|
||||
|
||||
@@ -612,6 +612,22 @@ class SuseHandler(OsHandler):
|
||||
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'))
|
||||
with open(os.path.join(self.targpath, 'etc/permissions.local'), 'a') as permout:
|
||||
permout.write(
|
||||
'/usr/lib/ssh/ssh-keysign root:ssh_keys 2711\n'
|
||||
'/etc/ssh/ssh_host_dsa_key root:ssh_keys 640\n'
|
||||
'/etc/ssh/ssh_host_ecdsa_key root:ssh_keys 640\n'
|
||||
'/etc/ssh/ssh_host_ed25519_key root:ssh_keys 640\n'
|
||||
'/etc/ssh/ssh_host_rsa_key root:ssh_keys 640\n'
|
||||
)
|
||||
args.cmd = ['groupadd', 'ssh_keys']
|
||||
run_constrainedx(fancy_chroot, (args,
|
||||
self.targpath))
|
||||
args.cmd = ['chkstat', '--system', '--set'],
|
||||
run_constrainedx(fancy_chroot, (args,
|
||||
self.targpath))
|
||||
|
||||
|
||||
if os.path.exists(os.path.join(self.targpath, 'sbin/mkinitrd')):
|
||||
args.cmd = ['mkinitrd']
|
||||
else:
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import base64
|
||||
import pyghmi.redfish.command as ic
|
||||
import pyghmi.util.webclient as webclient
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
def iterm_draw(databuf):
|
||||
datalen = len(databuf)
|
||||
data = base64.b64encode(databuf).decode('utf8')
|
||||
sys.stdout.write(
|
||||
'\x1b]1337;File=inline=1;size={}:'.format(datalen))
|
||||
sys.stdout.write(data)
|
||||
sys.stdout.write('\a')
|
||||
sys.stdout.write('\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
i = ic.Command(sys.argv[1], os.environ['XCCUSER'], os.environ['XCCPASS'], verifycallback=lambda x: True)
|
||||
i.get_health()
|
||||
#url = '/download/Mini_ScreenShot.png?t={}'.format(int(time.time()*1000))
|
||||
i.oem.wc.grab_json_response('/api/providers/rp_screenshot')
|
||||
url = '/download/HostScreenShot.png'
|
||||
fd = webclient.FileDownloader(i.oem.wc, url, sys.argv[2])
|
||||
fd.start()
|
||||
fd.join()
|
||||
if sys.argv[3]:
|
||||
imgdata = open(sys.argv[2], 'rb').read()
|
||||
iterm_draw(imgdata)
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
# This creates a locked down variant of a confluent CA
|
||||
|
||||
# The confluent CA naturally doesn't have any name constraints, which
|
||||
# is great for flexibility.
|
||||
|
||||
# However, if actually being imported into a web browser, it is likely
|
||||
# to want to limit the CA to apply to cluster resources rather
|
||||
# than having a blank check to vouch for any and all subjects.
|
||||
|
||||
# This particular approach causes a certificate authority that
|
||||
# can be imported and vouch for the subset of certificates
|
||||
# that were issued by the normal CA that match name constraints
|
||||
|
||||
# Unfortunately, *ALL* names are checked, whether they are relevant
|
||||
# to the conversation or not, so we reproduce the logic and the resultant
|
||||
# CA is fragile to IP addresses.
|
||||
|
||||
# The better approach would be to issue a separate, full name only certificate
|
||||
# and have a simpler alt CA. The same CA can be used to sign both certificates,
|
||||
# and still import the limited one
|
||||
|
||||
|
||||
import subprocess
|
||||
import socket
|
||||
import confluent.certutil as certutil
|
||||
import sys
|
||||
def create_alt_ca(certout, permitdomains):
|
||||
# This is to create a constrained variant of the existing authority
|
||||
# this will allow a client browser to only trust it for select domains
|
||||
# while the nodes can more broadly trust it (e.g. to vouch for IP addresses)
|
||||
sslcfg = certutil.get_openssl_conf_location()
|
||||
newcfg = '/etc/confluent/tls/ca-alt/openssl-alt.cfg'
|
||||
subj = subprocess.check_output(
|
||||
['openssl', 'x509', '-subject', '-noout', '-in', '/etc/confluent/tls/ca/cacert.pem']
|
||||
).decode().replace('subject=', '')
|
||||
serial = subprocess.check_output(
|
||||
['openssl', 'x509', '-serial', '-noout', '-in', '/etc/confluent/tls/ca/cacert.pem']
|
||||
).decode().replace('serial=', '')
|
||||
with open('/etc/confluent/tls/ca-alt/serial', 'w') as srl:
|
||||
srl.write(serial)
|
||||
settings = {
|
||||
'dir': '/etc/confluent/tls/ca-alt',
|
||||
'certificate': '$dir/cacert.pem',
|
||||
'private_key': '$dir/private/cakey.pem',
|
||||
'countryName': 'optional',
|
||||
'stateOrProvinceName': 'optional',
|
||||
'organizationName': 'optional',
|
||||
}
|
||||
keyin = '/etc/confluent/tls/ca/private/cakey.pem'
|
||||
csrin = '/etc/confluent/tls/ca/ca.csr'
|
||||
|
||||
shortname = 'r3u20'
|
||||
|
||||
|
||||
ipaddrs = list(certutil.get_ip_addresses())
|
||||
san = [] # 'IP:{0}'.format(x) for x in ipaddrs]
|
||||
# It is incorrect to put IP addresses as DNS type. However
|
||||
# there exists non-compliant clients that fail with them as IP
|
||||
# san.extend(['DNS:{0}'.format(x) for x in ipaddrs])
|
||||
dnsnames = set(ipaddrs)
|
||||
dnsnames.add(shortname)
|
||||
for currip in ipaddrs:
|
||||
dnsnames.add(socket.getnameinfo((currip, 0), 0)[0])
|
||||
for currname in dnsnames:
|
||||
san.append('DNS:{0}'.format(currname))
|
||||
|
||||
|
||||
|
||||
if permitdomains[0] == '':
|
||||
permitdomains = []
|
||||
nameconstraints = ['permitted;DNS:{}'.format(x) for x in permitdomains]
|
||||
nameconstraints.extend(['permitted;{}'.format(x) for x in san])
|
||||
nameconstraints = ','.join(nameconstraints)
|
||||
if nameconstraints:
|
||||
nameconstraints = f'nameConstraints = critical,{nameconstraints}\n'
|
||||
certutil.mkdirp('/etc/confluent/tls/ca-alt/newcerts')
|
||||
with open('/etc/confluent/tls/ca-alt/index.txt', 'w') as idx:
|
||||
pass
|
||||
|
||||
with open(sslcfg, 'r') as cfgin:
|
||||
with open(newcfg, 'w') as cfgfile:
|
||||
for line in cfgin.readlines():
|
||||
cfg = line.split('#')[0]
|
||||
if '=' in cfg:
|
||||
key, val = cfg.split('=', 1)
|
||||
for stg in settings:
|
||||
if certutil.substitute_cfg(stg, key, val, settings[stg], cfgfile, line):
|
||||
break
|
||||
else:
|
||||
cfgfile.write(line.strip() + '\n')
|
||||
continue
|
||||
cfgfile.write(line.strip() + '\n')
|
||||
cfgfile.write(f'\n[CACert]\nbasicConstraints = CA:true\n{nameconstraints}[ca_confluent]\n')
|
||||
subprocess.check_call(
|
||||
['openssl', 'ca', '-config', newcfg, '-batch', '-selfsign',
|
||||
'-extensions', 'CACert', '-extfile', newcfg,
|
||||
'-notext', '-startdate',
|
||||
'19700101010101Z', '-enddate', '21000101010101Z', '-keyfile',
|
||||
keyin, '-out', certout, '-in', csrin]
|
||||
)
|
||||
if __name__ == '__main__':
|
||||
create_alt_ca(sys.argv[1], sys.argv[2].split(','))
|
||||
|
||||
Reference in New Issue
Block a user