diff --git a/confluent_osdeploy/el7/profiles/default/ansible/firstboot.d/README.txt b/confluent_osdeploy/el7/profiles/default/ansible/firstboot.d/README.txt new file mode 100644 index 00000000..97e5f506 --- /dev/null +++ b/confluent_osdeploy/el7/profiles/default/ansible/firstboot.d/README.txt @@ -0,0 +1,25 @@ +Ansible playbooks ending in .yml or .yaml that are placed into this directory will be executed at the +appropriate phase of the install process. + +The 'hosts' may be omitted, and if included will be ignored, replaced with the host that is specifically +requesting the playbooks be executed. + +Also, the playbooks will be executed on the deployment server. Hence it may be slower in aggregate than +running content under scripts/ which ask much less of the deployment server + +Here is an example of what a playbook would look like broadly: + +- name: Example + gather_facts: no + tasks: + - name: Example1 + lineinfile: + path: /etc/hosts + line: 1.2.3.4 test1 + create: yes + - name: Example2 + lineinfile: + path: /etc/hosts + line: 1.2.3.5 test2 + create: yes + diff --git a/confluent_osdeploy/el7/profiles/default/ansible/post.d/README.txt b/confluent_osdeploy/el7/profiles/default/ansible/post.d/README.txt new file mode 100644 index 00000000..97e5f506 --- /dev/null +++ b/confluent_osdeploy/el7/profiles/default/ansible/post.d/README.txt @@ -0,0 +1,25 @@ +Ansible playbooks ending in .yml or .yaml that are placed into this directory will be executed at the +appropriate phase of the install process. + +The 'hosts' may be omitted, and if included will be ignored, replaced with the host that is specifically +requesting the playbooks be executed. + +Also, the playbooks will be executed on the deployment server. Hence it may be slower in aggregate than +running content under scripts/ which ask much less of the deployment server + +Here is an example of what a playbook would look like broadly: + +- name: Example + gather_facts: no + tasks: + - name: Example1 + lineinfile: + path: /etc/hosts + line: 1.2.3.4 test1 + create: yes + - name: Example2 + lineinfile: + path: /etc/hosts + line: 1.2.3.5 test2 + create: yes + diff --git a/confluent_osdeploy/el7/profiles/default/initprofile.sh b/confluent_osdeploy/el7/profiles/default/initprofile.sh index fdea9df3..d0125588 100644 --- a/confluent_osdeploy/el7/profiles/default/initprofile.sh +++ b/confluent_osdeploy/el7/profiles/default/initprofile.sh @@ -1,5 +1,5 @@ #!/bin/sh -sed -i 's/centos/CentOS/; s/rhel/Red Hat Enterprise Linux/' $2/profile.yaml +sed -i 's/centos/CentOS/; s/rhel/Red Hat Enterprise Linux/; s/oraclelinux/Oracle Linux/; s/alma/AlmaLinux/' $2/profile.yaml ln -s $1/images/pxeboot/vmlinuz $2/boot/kernel && \ ln -s $1/images/pxeboot/initrd.img $2/boot/initramfs/distribution mkdir -p $2/boot/efi/boot && \ diff --git a/confluent_osdeploy/el7/profiles/default/kickstart b/confluent_osdeploy/el7/profiles/default/kickstart index 47cf393f..4371b721 100644 --- a/confluent_osdeploy/el7/profiles/default/kickstart +++ b/confluent_osdeploy/el7/profiles/default/kickstart @@ -33,14 +33,15 @@ reboot %packages +#-kernel-uek # This can opt out of the UEK for the relevant distribution chrony rsync python +tar pciutils %include /tmp/addonpackages %end - # A kickstart.custom file is provided to enable easily adding # kickstart content without modifying the stock file. # While the stock file may be safely modified, using the .custom @@ -64,15 +65,16 @@ curl -f https://$mgr/confluent-public/os/$profile/scripts/prechroot.sh > /tmp/po # Hook firstboot.sh curl -f https://$mgr/confluent-public/os/$profile/scripts/firstboot.service > /mnt/sysimage/etc/systemd/system/firstboot.service -curl -f https://$mgr/confluent-public/os/$profile/scripts/firstboot.sh > /mnt/sysimage/etc/confluent/firstboot.sh -chmod +x /mnt/sysimage/etc/confluent/firstboot.sh +mkdir -p /mnt/sysimage/opt/confluent/bin +curl -f https://$mgr/confluent-public/os/$profile/scripts/firstboot.sh > /mnt/sysimage/opt/confluent/bin/firstboot.sh +chmod +x /mnt/sysimage/opt/confluent/bin/firstboot.sh %end %post cat /etc/confluent/tls/*.pem >> /etc/pki/tls/certs/ca-bundle.crt systemctl enable firstboot chgrp ssh_keys /etc/ssh/ssh*key -restorecon /etc/ssh/ssh*key /root/.shosts /etc/ssh/shosts.equiv /etc/ssh/ssh_config.d/* /etc/confluent/firstboot.sh +restorecon /etc/ssh/ssh*key /root/.shosts /etc/ssh/shosts.equiv /etc/ssh/ssh_config.d/* /opt/confluent/bin/firstboot.sh profile=$(grep ^profile: /etc/confluent/confluent.deploycfg |awk '{print $2}') mgr=$(grep deploy_server /etc/confluent/confluent.deploycfg |awk '{print $2}') curl -f https://$mgr/confluent-public/os/$profile/scripts/post.sh > /tmp/postinst.sh diff --git a/confluent_osdeploy/el7/profiles/default/profile.yaml b/confluent_osdeploy/el7/profiles/default/profile.yaml index d0d79ec4..265237a5 100644 --- a/confluent_osdeploy/el7/profiles/default/profile.yaml +++ b/confluent_osdeploy/el7/profiles/default/profile.yaml @@ -1,3 +1,3 @@ label: %%DISTRO%% %%VERSION%% %%ARCH%% (Default Profile) -kernelargs: quiet +kernelargs: quiet # These arguments are passed to the installer #installedargs: example # These arguments would be added to the installed system diff --git a/confluent_osdeploy/el7/profiles/default/scripts/configbmc b/confluent_osdeploy/el7/profiles/default/scripts/configbmc new file mode 100644 index 00000000..7176c12c --- /dev/null +++ b/confluent_osdeploy/el7/profiles/default/scripts/configbmc @@ -0,0 +1,414 @@ +# Copyright 2017 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 argparse +import ctypes +import fcntl +import json +from select import select +import socket +import struct +import os +import subprocess +import sys +import time + +if os.path.exists('/opt/confluent/bin/apiclient'): + apiclient = '/opt/confluent/bin/apiclient' +elif os.path.exists('/etc/confluent/apiclient'): + apiclient = '/etc/confluent/apiclient' + +class IpmiMsg(ctypes.Structure): + _fields_ = [('netfn', ctypes.c_ubyte), + ('cmd', ctypes.c_ubyte), + ('data_len', ctypes.c_short), + ('data', ctypes.POINTER(ctypes.c_ubyte))] + + +class IpmiSystemInterfaceAddr(ctypes.Structure): + _fields_ = [('addr_type', ctypes.c_int), + ('channel', ctypes.c_short), + ('lun', ctypes.c_ubyte)] + + +class IpmiRecv(ctypes.Structure): + _fields_ = [('recv_type', ctypes.c_int), + ('addr', ctypes.POINTER(IpmiSystemInterfaceAddr)), + ('addr_len', ctypes.c_uint), + ('msgid', ctypes.c_long), + ('msg', IpmiMsg)] + + +class IpmiReq(ctypes.Structure): + _fields_ = [('addr', ctypes.POINTER(IpmiSystemInterfaceAddr)), + ('addr_len', ctypes.c_uint), + ('msgid', ctypes.c_long), + ('msg', IpmiMsg)] + + +_IONONE = 0 +_IOWRITE = 1 +_IOREAD = 2 +IPMICTL_SET_MY_ADDRESS_CMD = ( + _IOREAD << 30 | ctypes.sizeof(ctypes.c_uint) << 16 + | ord('i') << 8 | 17) # from ipmi.h +IPMICTL_SEND_COMMAND = ( + _IOREAD << 30 | ctypes.sizeof(IpmiReq) << 16 + | ord('i') << 8 | 13) # from ipmi.h +# next is really IPMICTL_RECEIVE_MSG_TRUNC, but will only use that +IPMICTL_RECV = ( + (_IOWRITE | _IOREAD) << 30 | ctypes.sizeof(IpmiRecv) << 16 + | ord('i') << 8 | 11) # from ipmi.h +BMC_SLAVE_ADDR = ctypes.c_uint(0x20) +CURRCHAN = 0xf +ADDRTYPE = 0xc + + +class Session(object): + def __init__(self, devnode='/dev/ipmi0'): + """Create a local session inband + + :param: devnode: The path to the ipmi device + """ + self.ipmidev = open(devnode, 'r+') + fcntl.ioctl(self.ipmidev, IPMICTL_SET_MY_ADDRESS_CMD, BMC_SLAVE_ADDR) + # the interface is initted, create some reusable memory for our session + self.databuffer = ctypes.create_string_buffer(4096) + self.req = IpmiReq() + self.rsp = IpmiRecv() + self.addr = IpmiSystemInterfaceAddr() + self.req.msg.data = ctypes.cast( + ctypes.addressof(self.databuffer), + ctypes.POINTER(ctypes.c_ubyte)) + self.rsp.msg.data = self.req.msg.data + self.userid = None + self.password = None + + def await_reply(self): + rd, _, _ = select((self.ipmidev,), (), (), 1) + while not rd: + rd, _, _ = select((self.ipmidev,), (), (), 1) + + def pause(self, seconds): + time.sleep(seconds) + + @property + def parsed_rsp(self): + response = {'netfn': self.rsp.msg.netfn, 'command': self.rsp.msg.cmd, + 'code': bytearray(self.databuffer.raw)[0], + 'data': bytearray( + self.databuffer.raw[1:self.rsp.msg.data_len])} + return response + + def await_config(s, bmccfg, channel): + vlan = bmccfg.get('bmcvlan', None) + ipv4 = bmccfg.get('bmcipv4', None) + prefix = bmccfg.get('prefixv4', None) + gw = bmccfg.get('bmcgw', None) + + + + def raw_command(self, + netfn, + command, + data=(), + bridge_request=None, + retry=True, + delay_xmit=None, + timeout=None, + waitall=False, rslun=0): + self.addr.channel = CURRCHAN + self.addr.addr_type = ADDRTYPE + self.addr.lun = rslun + self.req.addr_len = ctypes.sizeof(IpmiSystemInterfaceAddr) + self.req.addr = ctypes.pointer(self.addr) + self.req.msg.netfn = netfn + self.req.msg.cmd = command + if data: + data = memoryview(bytearray(data)) + try: + self.databuffer[:len(data)] = data[:len(data)] + except ValueError: + self.databuffer[:len(data)] = data[:len(data)].tobytes() + self.req.msg.data_len = len(data) + fcntl.ioctl(self.ipmidev, IPMICTL_SEND_COMMAND, self.req) + self.await_reply() + self.rsp.msg.data_len = 4096 + self.rsp.addr = ctypes.pointer(self.addr) + self.rsp.addr_len = ctypes.sizeof(IpmiSystemInterfaceAddr) + fcntl.ioctl(self.ipmidev, IPMICTL_RECV, self.rsp) + return self.parsed_rsp + + +def _is_tsm(model): + return model in ('7y00', '7z01', '7y98', '7y99') + +def set_port(s, port, vendor, model): + oport = port + if vendor not in ('IBM', 'Lenovo'): + raise Exception('{0} not implemented'.format(vendor)) + if _is_tsm(model): + return set_port_tsm(s, port, model) + else: + set_port_xcc(s, port, model) + return 1 + + +def set_port_tsm(s, port, model): + oport = port + sys.stdout.write('Setting TSM port to "{}"...'.format(oport)) + sys.stdout.flush() + if port == 'ocp': + s.raw_command(0x32, 0x71, b'\x00\x01\x00') + elif port == 'dedicated': + s.raw_command(0x32, 0x71, b'\x00\x00\x00') + else: + raise Exception("Unsupported port for TSM") + timer = 15 + while timer: + timer = timer - 1 + time.sleep(1.0) + sys.stdout.write('.') + sys.stdout.flush() + if port == 'ocp': + iface = 0 + s.raw_command(0x32, 0x71, b'\x00\x00\x03') + elif port == 'dedicated': + iface = 1 + s.raw_command(0x32, 0x71, b'\x00\x01\x03') + rsp = s.raw_command(0x32, 0x72, bytearray([4, iface, 0])) + print('Complete') + return int(rsp['data'][0]) + + +def set_port_xcc(s, port, model): + oport = port + if port.lower() == 'dedicated': + port = b'\x01' + elif port.lower() in ('ml2', 'ocp'): + port = b'\x02\x00' + elif port.lower() == 'lom': + if model == '7x58': + port = b'\x00\x02' + else: + port = b'\x00\x00' + else: + port = port.split(' ') + port = bytes(bytearray([int(x) for x in port])) + currport = bytes(s.raw_command(0xc, 2, b'\x01\xc0\x00\x00')['data'][1:]) + if port == currport: + sys.stdout.write('XCC port already set to "{}"\n'.format(oport)) + return + sys.stdout.write('Setting XCC port to "{}"...'.format(oport)) + sys.stdout.flush() + s.raw_command(0xc, 1, b'\x01\xc0' + port) + tries = 60 + while currport != port and tries: + tries -= 1 + time.sleep(0.5) + sys.stdout.write('.') + sys.stdout.flush() + currport = bytes(s.raw_command(0xc, 2, b'\x01\xc0\x00\x00')['data'][1:]) + if not tries: + raise Exception('Timeout attempting to set port') + sys.stdout.write('Complete\n') + + +def check_vlan(s, vlan, channel): + if vlan == 'off': + vlan = b'\x00\x00' + else: + vlan = int(vlan) + if vlan: + vlan = vlan | 32768 + vlan = struct.pack(' /dev/null return $retcode } + +run_remote_config() { + echo + set_confluent_vars + apiclient=/opt/confluent/bin/apiclient + if [ -f /etc/confluent/apiclient ]; then + apiclient=/etc/confluent/apiclient + fi + echo '---------------------------------------------------------------------------' + echo Requesting to run remote configuration for "'$*'" from $mgr under profile $profile + if [ -x /usr/libexec/platform-python ]; then + /usr/libexec/platform-python $apiclient /confluent-api/self/remoteconfig/"$*" -d {} + /usr/libexec/platform-python $apiclient /confluent-api/self/remoteconfig/status -w 204 + else + /usr/bin/python $apiclient /confluent-api/self/remoteconfig/"$*" -d {} + /usr/bin/python $apiclient /confluent-api/self/remoteconfig/status -w 204 + fi + echo + echo 'Completed remote configuration' + echo '---------------------------------------------------------------------------' + return +} + diff --git a/confluent_osdeploy/el7/profiles/default/scripts/post.custom b/confluent_osdeploy/el7/profiles/default/scripts/post.custom index 29676d2b..4530569e 100644 --- a/confluent_osdeploy/el7/profiles/default/scripts/post.custom +++ b/confluent_osdeploy/el7/profiles/default/scripts/post.custom @@ -2,6 +2,6 @@ # This is a convenient place to keep customizations separate from modifying the stock scripts # While modification of the stock scripts is fine, it may be easier to rebase to a newer # stock profile if the '.custom' files are used. + # An example for installing OFED for infiniband follows (see the file for more detail): #run_remote infiniband/mofed.post - diff --git a/confluent_osdeploy/el7/profiles/default/scripts/post.d/.gitignore b/confluent_osdeploy/el7/profiles/default/scripts/post.d/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/confluent_osdeploy/el7/profiles/default/scripts/post.sh b/confluent_osdeploy/el7/profiles/default/scripts/post.sh index e5b2c137..98f3b0c1 100644 --- a/confluent_osdeploy/el7/profiles/default/scripts/post.sh +++ b/confluent_osdeploy/el7/profiles/default/scripts/post.sh @@ -5,16 +5,14 @@ apikey=$(cat /etc/confluent/confluent.apikey) chmod 700 /etc/confluent chmod og-rwx /etc/confluent/* - export mgr profile nodename . /etc/confluent/functions -curl -X POST -d 'status: staged' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" https://$mgr/confluent-api/self/updatestatus - if [ -f /tmp/cryptboot ]; then run_remote tpm_luks.sh fi + # By default, the install repository is ignored, change # this by manually adding local repositories @@ -37,3 +35,10 @@ run_remote_python syncfileclient # run_remote example.sh # run_remote_python example.py run_remote post.custom + +# Also, scripts may be placed into 'post.d', e.g. post.d/01-runfirst.sh, post.d/02-runsecond.sh +run_remote_parts post + +# Induce execution of remote configuration, e.g. ansible plays in ansible/post.d/ +run_remote_config post +curl -X POST -d 'status: staged' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $apikey" https://$mgr/confluent-api/self/updatestatus diff --git a/confluent_osdeploy/el7/profiles/default/scripts/pre.custom b/confluent_osdeploy/el7/profiles/default/scripts/pre.custom index 38b55e4c..adb3866c 100644 --- a/confluent_osdeploy/el7/profiles/default/scripts/pre.custom +++ b/confluent_osdeploy/el7/profiles/default/scripts/pre.custom @@ -11,4 +11,3 @@ #Some addons improve efficiency by adding dependencies during install #here is an example for adding OFED install prereqs to the install #run_remote infiniband/mofed.pre - diff --git a/confluent_osdeploy/el7/profiles/default/scripts/pre.d/.gitignore b/confluent_osdeploy/el7/profiles/default/scripts/pre.d/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/confluent_osdeploy/el7/profiles/default/scripts/pre.sh b/confluent_osdeploy/el7/profiles/default/scripts/pre.sh index 3bf26e94..c6239aa2 100644 --- a/confluent_osdeploy/el7/profiles/default/scripts/pre.sh +++ b/confluent_osdeploy/el7/profiles/default/scripts/pre.sh @@ -43,7 +43,7 @@ if [ ! -z "$blargs" ]; then echo "bootloader $blargs" > /tmp/grubpw fi ssh-keygen -A -for pubkey in /etc/ssh/ssh_host_*_key.pub; do +for pubkey in /etc/ssh/ssh_host*key.pub; do certfile=${pubkey/.pub/-cert.pub} curl -f -X POST -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $(cat /etc/confluent/confluent.apikey)" -d @$pubkey https://$mgr/confluent-api/self/sshcert > $certfile echo HostCertificate $certfile >> /etc/ssh/sshd_config.anaconda @@ -53,9 +53,9 @@ if [ -f "/run/install/cmdline.d/01-autocons.conf" ]; then consoledev=$(cat /run/install/cmdline.d/01-autocons.conf | sed -e 's!console=!/dev/!' -e 's/,.*//') TMUX= tmux a <> $consoledev >&0 2>&1 & fi -touch /tmp/addonpackages cryptboot=$(grep ^encryptboot: /etc/confluent/confluent.deploycfg | awk '{print $2}') LUKSPARTY='' +touch /tmp/addonpackages if [ "$cryptboot" == "tpm2" ]; then LUKSPARTY="--encrypted --passphrase=$(cat /etc/confluent/confluent.apikey)" echo $cryptboot >> /tmp/cryptboot @@ -65,11 +65,14 @@ fi export mgr profile nodename curl -f https://$mgr/confluent-public/os/$profile/scripts/functions > /tmp/functions . /tmp/functions -run_remote_python getinstalldisk -if [ -e /tmp/installdisk ]; then +run_remote pre.custom +run_remote_parts pre +if [ ! -e /tmp/installdisk ]; then + run_remote_python getinstalldisk +fi +if [ -e /tmp/installdisk -a ! -e /tmp/partitioning ]; then echo clearpart --all --initlabel >> /tmp/partitioning echo ignoredisk --only-use $(cat /tmp/installdisk) >> /tmp/partitioning echo autopart --nohome $LUKSPARTY >> /tmp/partitioning fi python /etc/confluent/apiclient /confluent-public/os/$profile/kickstart.custom -o /tmp/kickstart.custom -run_remote pre.custom diff --git a/confluent_osdeploy/el7/profiles/default/scripts/prechroot.sh b/confluent_osdeploy/el7/profiles/default/scripts/prechroot.sh index f7972b7b..44d42402 100644 --- a/confluent_osdeploy/el7/profiles/default/scripts/prechroot.sh +++ b/confluent_osdeploy/el7/profiles/default/scripts/prechroot.sh @@ -10,9 +10,14 @@ nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}') export mgr profile nodename cp -a /etc/confluent /mnt/sysimage/etc +chmod -R og-rwx /mnt/sysimage/etc/confluent cp /tmp/functions /mnt/sysimage/etc/confluent/ . /tmp/functions cp /tmp/cryptboot /mnt/sysimage/tmp/ +echo Port 2222 >> /etc/ssh/sshd_config.anaconda +echo Match LocalPort 22 >> /etc/ssh/sshd_config.anaconda +echo " ChrootDirectory /mnt/sysimage" >> /etc/ssh/sshd_config.anaconda +kill -HUP $(cat /run/sshd.pid) # Preserve the ssh setup work done for the installer # by copying into the target system and setting up diff --git a/confluent_osdeploy/el8/profiles/default/scripts/infiniband/mofed.post b/confluent_osdeploy/el8/profiles/default/scripts/infiniband/mofed.post index 6d371bc3..a650d8f0 100644 --- a/confluent_osdeploy/el8/profiles/default/scripts/infiniband/mofed.post +++ b/confluent_osdeploy/el8/profiles/default/scripts/infiniband/mofed.post @@ -9,12 +9,11 @@ if lspci -d 15b3:: -n |grep 15b3 > /dev/null; then # two lines to use the .iso instead of the tgz packaging #fetch_remote infiniband/mofed.iso #mkdir MLNX_OFED - #mount -o loop ofed.iso MLNX_OFED + #mount -o loop infiniband/mofed.iso MLNX_OFED fetch_remote infiniband/mofed.tgz tar xf infiniband/mofed.tgz # The rest is common between tar and iso cd MLNX_OFED* - mount -o loop ofed ./mlnxofedinstall --force fi diff --git a/confluent_osdeploy/el8/profiles/default/scripts/pre.sh b/confluent_osdeploy/el8/profiles/default/scripts/pre.sh index 828374f0..82680788 100644 --- a/confluent_osdeploy/el8/profiles/default/scripts/pre.sh +++ b/confluent_osdeploy/el8/profiles/default/scripts/pre.sh @@ -74,7 +74,7 @@ curl -f https://$mgr/confluent-public/os/$profile/scripts/functions > /tmp/funct . /tmp/functions run_remote pre.custom run_remote_parts pre -if [ -e /tmp/installdisk ]; then +if [ ! -e /tmp/installdisk ]; then run_remote_python getinstalldisk fi if [ -e /tmp/installdisk -a ! -e /tmp/partitioning ]; then