#!/bin/bash # # lib/ironic # Functions to control the configuration and operation of the **Ironic** service # Dependencies: # # - ``functions`` file # - ``DEST``, ``DATA_DIR``, ``STACK_USER`` must be defined # - ``SERVICE_{TENANT_NAME|PASSWORD}`` must be defined # - ``SERVICE_HOST`` # - ``KEYSTONE_TOKEN_FORMAT`` must be defined # ``stack.sh`` calls the entry points in this order: # # - install_ironic # - install_ironicclient # - init_ironic # - start_ironic # - stop_ironic # - cleanup_ironic # ensure we don't re-source this in the same environment [[ -z "$_IRONIC_DEVSTACK_LIB" ]] || return 0 declare -r -g _IRONIC_DEVSTACK_LIB=1 # Save xtrace and pipefail settings _XTRACE_IRONIC=$(set +o | grep xtrace) _PIPEFAIL_IRONIC=$(set +o | grep pipefail) set -o xtrace set +o pipefail # Defaults # -------- # Set up default directories GITDIR["python-ironicclient"]=$DEST/python-ironicclient GITDIR["ironic-lib"]=$DEST/ironic-lib GITREPO["pyghmi"]=${PYGHMI_REPO:-${GIT_BASE}/x/pyghmi} GITBRANCH["pyghmi"]=${PYGHMI_BRANCH:-master} GITDIR["pyghmi"]=$DEST/pyghmi GITREPO["virtualbmc"]=${VIRTUALBMC_REPO:-${GIT_BASE}/openstack/virtualbmc.git} GITBRANCH["virtualbmc"]=${VIRTUALBMC_BRANCH:-master} GITDIR["virtualbmc"]=$DEST/virtualbmc GITREPO["virtualpdu"]=${VIRTUALPDU_REPO:-${GIT_BASE}/openstack/virtualpdu.git} GITBRANCH["virtualpdu"]=${VIRTUALPDU_BRANCH:-master} GITDIR["virtualpdu"]=$DEST/virtualpdu GITREPO["sushy"]=${SUSHY_REPO:-${GIT_BASE}/openstack/sushy.git} GITBRANCH["sushy"]=${SUSHY_BRANCH:-master} GITDIR["sushy"]=$DEST/sushy GITREPO["sushy-tools"]=${SUSHY_TOOLS_REPO:-${GIT_BASE}/openstack/sushy-tools.git} GITBRANCH["sushy-tools"]=${SUSHY_TOOLS_BRANCH:-master} GITDIR["sushy-tools"]=$DEST/sushy-tools IRONIC_DIR=$DEST/ironic IRONIC_DEVSTACK_DIR=$IRONIC_DIR/devstack IRONIC_DEVSTACK_FILES_DIR=$IRONIC_DEVSTACK_DIR/files # TODO(dtantsur): delete these three when we migrate image building to # ironic-python-agent-builder completely IRONIC_PYTHON_AGENT_REPO=${IRONIC_PYTHON_AGENT_REPO:-${GIT_BASE}/openstack/ironic-python-agent.git} IRONIC_PYTHON_AGENT_BRANCH=${IRONIC_PYTHON_AGENT_BRANCH:-$TARGET_BRANCH} IRONIC_PYTHON_AGENT_DIR=$DEST/ironic-python-agent IRONIC_PYTHON_AGENT_BUILDER_REPO=${IRONIC_PYTHON_AGENT_BUILDER_REPO:-${GIT_BASE}/openstack/ironic-python-agent-builder.git} IRONIC_PYTHON_AGENT_BUILDER_BRANCH=${IRONIC_PYTHON_AGENT_BUILDER_BRANCH:-$BRANCHLESS_TARGET_BRANCH} IRONIC_PYTHON_AGENT_BUILDER_DIR=$DEST/ironic-python-agent-builder IRONIC_DIB_BINDEP_FILE=https://opendev.org/openstack/diskimage-builder/raw/branch/master/bindep.txt IRONIC_DATA_DIR=$DATA_DIR/ironic IRONIC_STATE_PATH=/var/lib/ironic IRONIC_AUTH_CACHE_DIR=${IRONIC_AUTH_CACHE_DIR:-/var/cache/ironic} IRONIC_CONF_DIR=${IRONIC_CONF_DIR:-/etc/ironic} IRONIC_CONF_FILE=$IRONIC_CONF_DIR/ironic.conf IRONIC_ROOTWRAP_CONF=$IRONIC_CONF_DIR/rootwrap.conf # Deploy Ironic API under uwsgi (NOT mod_wsgi) server. # Devstack aims to remove mod_wsgi support, so ironic shouldn't use it too. # If set to False that will fall back to use the eventlet server that # can happen on grenade runs. # The (confusing) name IRONIC_USE_MOD_WSGI is left for backward compatibility, # for example during grenade runs # TODO(pas-ha) remove IRONIC_USE_MOD_WSGI var after oldest supported # stable branch is stable/rocky IRONIC_USE_MOD_WSGI=$(trueorfalse $ENABLE_HTTPD_MOD_WSGI_SERVICES IRONIC_USE_MOD_WSGI) # If True, will deploy Ironic API under WSGI server, currently supported one # is uwsgi. # Defaults to the (now confusingly named) IRONIC_USE_MOD_WSGI for backward compat IRONIC_USE_WSGI=$(trueorfalse $IRONIC_USE_MOD_WSGI IRONIC_USE_WSGI) # Whether DevStack will be setup for bare metal or VMs IRONIC_IS_HARDWARE=$(trueorfalse False IRONIC_IS_HARDWARE) # Deploy callback timeout can be changed from its default (1800), if required. IRONIC_CALLBACK_TIMEOUT=${IRONIC_CALLBACK_TIMEOUT:-} # Timeout before retrying PXE boot. Set low to help the CI. if [[ "$IRONIC_IS_HARDWARE" == False ]]; then IRONIC_PXE_BOOT_RETRY_TIMEOUT=${IRONIC_PXE_BOOT_RETRY_TIMEOUT:-600} else IRONIC_PXE_BOOT_RETRY_TIMEOUT=${IRONIC_PXE_BOOT_RETRY_TIMEOUT:-} fi # Ping timeout after the node becomes active IRONIC_PING_TIMEOUT=${IRONIC_PING_TIMEOUT:-} # Deploy to hardware platform IRONIC_HW_NODE_CPU=${IRONIC_HW_NODE_CPU:-1} IRONIC_HW_NODE_RAM=${IRONIC_HW_NODE_RAM:-512} IRONIC_HW_NODE_DISK=${IRONIC_HW_NODE_DISK:-10} IRONIC_HW_EPHEMERAL_DISK=${IRONIC_HW_EPHEMERAL_DISK:-0} IRONIC_HW_ARCH=${IRONIC_HW_ARCH:-x86_64} # The file is composed of multiple lines, each line includes fields # separated by white space, in the format: # # [] # # For example: # # 192.168.110.107 00:1e:67:57:50:4c root otc123 # # Supported IRONIC_DEPLOY_DRIVERs: # ipmi: # # # idrac: # # # irmc: # # IRONIC_HWINFO_FILE=${IRONIC_HWINFO_FILE:-$IRONIC_DATA_DIR/hardware_info} # Set up defaults for functional / integration testing IRONIC_NODE_UUID=${IRONIC_NODE_UUID:-`uuidgen`} IRONIC_SCRIPTS_DIR=${IRONIC_SCRIPTS_DIR:-$IRONIC_DEVSTACK_DIR/tools/ironic/scripts} IRONIC_TEMPLATES_DIR=${IRONIC_TEMPLATES_DIR:-$IRONIC_DEVSTACK_DIR/tools/ironic/templates} IRONIC_BAREMETAL_BASIC_OPS=$(trueorfalse False IRONIC_BAREMETAL_BASIC_OPS) IRONIC_TFTPBOOT_DIR=${IRONIC_TFTPBOOT_DIR:-$IRONIC_DATA_DIR/tftpboot} IRONIC_TFTPSERVER_IP=${IRONIC_TFTPSERVER_IP:-$HOST_IP} IRONIC_TFTP_BLOCKSIZE=${IRONIC_TFTP_BLOCKSIZE:-$((PUBLIC_BRIDGE_MTU-50))} IRONIC_VM_COUNT=${IRONIC_VM_COUNT:-1} IRONIC_VM_SPECS_CPU=${IRONIC_VM_SPECS_CPU:-1} IRONIC_VM_SPECS_RAM=${IRONIC_VM_SPECS_RAM:-1280} IRONIC_VM_SPECS_CPU_ARCH=${IRONIC_VM_SPECS_CPU_ARCH:-'x86_64'} IRONIC_VM_SPECS_DISK=${IRONIC_VM_SPECS_DISK:-10} IRONIC_VM_SPECS_DISK_FORMAT=${IRONIC_VM_SPECS_DISK_FORMAT:-qcow2} IRONIC_VM_EPHEMERAL_DISK=${IRONIC_VM_EPHEMERAL_DISK:-0} IRONIC_VM_EMULATOR=${IRONIC_VM_EMULATOR:-'/usr/bin/qemu-system-x86_64'} IRONIC_VM_ENGINE=${IRONIC_VM_ENGINE:-qemu} IRONIC_VM_NETWORK_BRIDGE=${IRONIC_VM_NETWORK_BRIDGE:-brbm} IRONIC_VM_INTERFACE_COUNT=${IRONIC_VM_INTERFACE_COUNT:-2} IRONIC_VM_VOLUME_COUNT=${IRONIC_VM_VOLUME_COUNT:-1} IRONIC_VM_MACS_CSV_FILE=${IRONIC_VM_MACS_CSV_FILE:-$IRONIC_DATA_DIR/ironic_macs.csv} IRONIC_CLEAN_NET_NAME=${IRONIC_CLEAN_NET_NAME:-${IRONIC_PROVISION_NETWORK_NAME:-${PRIVATE_NETWORK_NAME}}} IRONIC_RESCUE_NET_NAME=${IRONIC_RESCUE_NET_NAME:-${IRONIC_CLEAN_NET_NAME}} IRONIC_EXTRA_PXE_PARAMS=${IRONIC_EXTRA_PXE_PARAMS:-} IRONIC_TTY_DEV=${IRONIC_TTY_DEV:-ttyS0,115200} IRONIC_TEMPEST_BUILD_TIMEOUT=${IRONIC_TEMPEST_BUILD_TIMEOUT:-${BUILD_TIMEOUT:-}} if [[ -n "$BUILD_TIMEOUT" ]]; then echo "WARNING: BUILD_TIMEOUT variable is renamed to IRONIC_TEMPEST_BUILD_TIMEOUT and will be deprecated in Pike." fi hostdomain=$(hostname) if [[ "$hostdomain" =~ "rax" ]]; then echo "WARNING: Auto-increasing the requested build timeout by 1.5 as the detected hostname suggests a cloud host where VMs are software emulated." # NOTE(TheJulia): Rax hosts are entirely qemu emulated, not CPU enabled # virtualization. As such, the ramdisk decompression is known to take an # eceptional amount of time and we need to afford a little more time to # these hosts for jobs to complete without issues. new_timeout=$(echo "$IRONIC_TEMPEST_BUILD_TIMEOUT * 1.5 / 1" | bc) IRONIC_TEMPEST_BUILD_TIMEOUT=$new_timeout fi IRONIC_DEFAULT_API_VERSION=${IRONIC_DEFAULT_API_VERSION:-} IRONIC_CMD="openstack baremetal" if [[ -n "$IRONIC_DEFAULT_API_VERSION" ]]; then IRONIC_CMD="$IRONIC_CMD --os-baremetal-api-version $IRONIC_DEFAULT_API_VERSION" fi IRONIC_ENABLED_HARDWARE_TYPES=${IRONIC_ENABLED_HARDWARE_TYPES:-"ipmi,fake-hardware"} # list of all available driver interfaces types IRONIC_DRIVER_INTERFACE_TYPES="bios boot power management deploy console inspect raid rescue storage network vendor" IRONIC_ENABLED_BIOS_INTERFACES=${IRONIC_ENABLED_BIOS_INTERFACES:-"fake,no-bios"} IRONIC_ENABLED_BOOT_INTERFACES=${IRONIC_ENABLED_BOOT_INTERFACES:-"fake,ipxe"} IRONIC_ENABLED_CONSOLE_INTERFACES=${IRONIC_ENABLED_CONSOLE_INTERFACES:-"fake,no-console"} IRONIC_ENABLED_DEPLOY_INTERFACES=${IRONIC_ENABLED_DEPLOY_INTERFACES:-"fake,iscsi,direct"} IRONIC_ENABLED_INSPECT_INTERFACES=${IRONIC_ENABLED_INSPECT_INTERFACES:-"fake,no-inspect"} IRONIC_ENABLED_MANAGEMENT_INTERFACES=${IRONIC_ENABLED_MANAGEMENT_INTERFACES:-"fake,ipmitool,noop"} IRONIC_ENABLED_NETWORK_INTERFACES=${IRONIC_ENABLED_NETWORK_INTERFACES:-"flat,noop"} IRONIC_ENABLED_POWER_INTERFACES=${IRONIC_ENABLED_POWER_INTERFACES:-"fake,ipmitool"} IRONIC_ENABLED_RAID_INTERFACES=${IRONIC_ENABLED_RAID_INTERFACES:-"fake,agent,no-raid"} IRONIC_ENABLED_RESCUE_INTERFACES=${IRONIC_ENABLED_RESCUE_INTERFACES:-"fake,no-rescue"} IRONIC_ENABLED_STORAGE_INTERFACES=${IRONIC_ENABLED_STORAGE_INTERFACES:-"fake,cinder,noop"} IRONIC_ENABLED_VENDOR_INTERFACES=${IRONIC_ENABLED_VENDOR_INTERFACES:-"fake,ipmitool,no-vendor"} # for usage with hardware types IRONIC_DEFAULT_BIOS_INTERFACE=${IRONIC_DEFAULT_BIOS_INTERFACE:-} IRONIC_DEFAULT_BOOT_INTERFACE=${IRONIC_DEFAULT_BOOT_INTERFACE:-} IRONIC_DEFAULT_CONSOLE_INTERFACE=${IRONIC_DEFAULT_CONSOLE_INTERFACE:-} IRONIC_DEFAULT_DEPLOY_INTERFACE=${IRONIC_DEFAULT_DEPLOY_INTERFACE:-} IRONIC_DEFAULT_INSPECT_INTERFACE=${IRONIC_DEFAULT_INSPECT_INTERFACE:-} IRONIC_DEFAULT_MANAGEMENT_INTERFACE=${IRONIC_DEFAULT_MANAGEMENT_INTERFACE:-} IRONIC_DEFAULT_NETWORK_INTERFACE=${IRONIC_DEFAULT_NETWORK_INTERFACE:-} IRONIC_DEFAULT_POWER_INTERFACE=${IRONIC_DEFAULT_POWER_INTERFACE:-} IRONIC_DEFAULT_RAID_INTERFACE=${IRONIC_DEFAULT_RAID_INTERFACE:-} IRONIC_DEFAULT_RESCUE_INTERFACE=${IRONIC_DEFAULT_RESCUE_INTERFACE:-} IRONIC_DEFAULT_STORAGE_INTERFACE=${IRONIC_DEFAULT_STORAGE_INTERFACE:-} IRONIC_DEFAULT_VENDOR_INTERFACE=${IRONIC_DEFAULT_VENDOR_INTERFACE:-} # If IRONIC_VM_ENGINE is explicitly set to "auto" or "kvm", # devstack will attempt to use hardware virtualization # (aka nested kvm). We do not enable it in the infra gates # because it is not consistently supported/working across # all gate infrastructure providers. if [[ "$IRONIC_VM_ENGINE" == "auto" ]]; then sudo modprobe kvm || true if [ ! -e /dev/kvm ]; then echo "WARNING: Switching to QEMU" IRONIC_VM_ENGINE=qemu if [[ -z "$IRONIC_VM_EMULATOR" ]]; then IRONIC_VM_EMULATOR='/usr/bin/qemu-system-x86_64' fi else IRONIC_VM_ENGINE=kvm fi fi if [[ "$IRONIC_VM_ENGINE" == "kvm" ]]; then # Set this to empty, so configure-vm.py can autodetect location # of KVM binary IRONIC_VM_EMULATOR="" fi # By default, baremetal VMs will console output to file. IRONIC_VM_LOG_CONSOLE=$(trueorfalse True IRONIC_VM_LOG_CONSOLE) IRONIC_VM_LOG_DIR=${IRONIC_VM_LOG_DIR:-$IRONIC_DATA_DIR/logs/} IRONIC_VM_LOG_ROTATE=$(trueorfalse True IRONIC_VM_LOG_ROTATE) # Set resource_classes for nodes to use Nova's placement engine IRONIC_DEFAULT_RESOURCE_CLASS=${IRONIC_DEFAULT_RESOURCE_CLASS:-baremetal} # Set traits for nodes. Traits should be separated by whitespace. IRONIC_DEFAULT_TRAITS=${IRONIC_DEFAULT_TRAITS-CUSTOM_GOLD} # Whether to build the ramdisk or download a prebuilt one. IRONIC_BUILD_DEPLOY_RAMDISK=$(trueorfalse True IRONIC_BUILD_DEPLOY_RAMDISK) # Ironic IPA ramdisk type, supported types are: IRONIC_SUPPORTED_RAMDISK_TYPES_RE="^(tinyipa|dib)$" IRONIC_RAMDISK_TYPE=${IRONIC_RAMDISK_TYPE:-dib} # Confirm we have a supported ramdisk type or fail early. if [[ ! "$IRONIC_RAMDISK_TYPE" =~ $IRONIC_SUPPORTED_RAMDISK_TYPES_RE ]]; then die $LINENO "Unrecognized IRONIC_RAMDISK_TYPE: $IRONIC_RAMDISK_TYPE. Expected 'tinyipa' or 'dib'" fi # Prevent a case that will likely result in a failure. if [[ "$hostdomain" =~ "rax" ]]; then if [[ "$IRONIC_RAMDISK_TYPE" == "dib" ]]; then echo "** WARNING ** - DIB based IPA images have been defined, however we are running devstack on RAX VM. Due to virtualization constraints, we are automatically falling back to TinyIPA to ensure CI job passage." IRONIC_RAMDISK_TYPE="tinyipa" fi fi # If present, these files are used as deploy ramdisk/kernel. # (The value must be an absolute path) IRONIC_DEPLOY_RAMDISK=${IRONIC_DEPLOY_RAMDISK:-$TOP_DIR/files/ir-deploy-$IRONIC_DEPLOY_DRIVER.initramfs} IRONIC_DEPLOY_KERNEL=${IRONIC_DEPLOY_KERNEL:-$TOP_DIR/files/ir-deploy-$IRONIC_DEPLOY_DRIVER.kernel} IRONIC_DEPLOY_ISO=${IRONIC_DEPLOY_ISO:-$TOP_DIR/files/ir-deploy-$IRONIC_DEPLOY_DRIVER.iso} # If present, this file is used to deploy/boot nodes over virtual media # (The value must be an absolute path) IRONIC_EFIBOOT=${IRONIC_EFIBOOT:-$TOP_DIR/files/ir-deploy-$IRONIC_DEPLOY_DRIVER.efiboot} # NOTE(jroll) this needs to be updated when stable branches are cut IPA_DOWNLOAD_BRANCH=${IPA_DOWNLOAD_BRANCH:-stable/ussuri} IPA_DOWNLOAD_BRANCH=$(echo $IPA_DOWNLOAD_BRANCH | tr / -) # OS for using with DIB images IRONIC_DIB_RAMDISK_OS=${IRONIC_DIB_RAMDISK_OS:-centos8} IRONIC_DIB_RAMDISK_RELEASE=${IRONIC_DIB_RAMDISK_RELEASE:-} # Configure URLs required to download ramdisk if we're not building it, and # IRONIC_DEPLOY_RAMDISK/KERNEL or the RAMDISK/KERNEL_URLs have not been # preconfigured. if [[ "$IRONIC_BUILD_DEPLOY_RAMDISK" == "False" && \ ! (-e "$IRONIC_DEPLOY_RAMDISK" && -e "$IRONIC_DEPLOY_KERNEL") && \ (-z "$IRONIC_AGENT_KERNEL_URL" || -z "$IRONIC_AGENT_RAMDISK_URL") ]]; then case $IRONIC_RAMDISK_TYPE in tinyipa) IRONIC_AGENT_KERNEL_FILE=tinyipa-${IPA_DOWNLOAD_BRANCH}.vmlinuz IRONIC_AGENT_RAMDISK_FILE=tinyipa-${IPA_DOWNLOAD_BRANCH}.gz ;; dib) IRONIC_AGENT_KERNEL_FILE=ipa-${IRONIC_DIB_RAMDISK_OS}-${IPA_DOWNLOAD_BRANCH}.kernel IRONIC_AGENT_RAMDISK_FILE=ipa-${IRONIC_DIB_RAMDISK_OS}-${IPA_DOWNLOAD_BRANCH}.initramfs ;; esac IRONIC_AGENT_KERNEL_URL=https://tarballs.openstack.org/ironic-python-agent/${IRONIC_RAMDISK_TYPE}/files/${IRONIC_AGENT_KERNEL_FILE} IRONIC_AGENT_RAMDISK_URL=https://tarballs.openstack.org/ironic-python-agent/${IRONIC_RAMDISK_TYPE}/files/${IRONIC_AGENT_RAMDISK_FILE} fi # This refers the options for disk-image-create and the platform on which # to build the dib based ironic-python-agent ramdisk. IRONIC_DIB_RAMDISK_OPTIONS=${IRONIC_DIB_RAMDISK_OPTIONS:-} if [[ -z "$IRONIC_DIB_RAMDISK_OPTIONS" ]]; then if [[ "$IRONIC_DIB_RAMDISK_OS" == "centos8" ]]; then # Adapt for DIB naming change IRONIC_DIB_RAMDISK_OS=centos-minimal IRONIC_DIB_RAMDISK_RELEASE=8 fi IRONIC_DIB_RAMDISK_OPTIONS="$IRONIC_DIB_RAMDISK_OS" fi # DHCP timeout for the dhcp-all-interfaces element. IRONIC_DIB_DHCP_TIMEOUT=${IRONIC_DIB_DHCP_TIMEOUT:-60} # Some drivers in Ironic require deploy ramdisk in bootable ISO format. # Set this variable to "true" to build an ISO for deploy ramdisk and # upload to Glance. IRONIC_DEPLOY_ISO_REQUIRED=$(trueorfalse False IRONIC_DEPLOY_ISO_REQUIRED) if [[ "$IRONIC_DEPLOY_ISO_REQUIRED" = "True" \ && "$IRONIC_BUILD_DEPLOY_RAMDISK" = "False" \ && ! -e "$IRONIC_DEPLOY_ISO" ]]; then die "Prebuilt ISOs are not available, provide an ISO via IRONIC_DEPLOY_ISO \ or set IRONIC_BUILD_DEPLOY_RAMDISK=True to use ISOs" fi # Which deploy driver to use - valid choices right now # are ``ipmi``, ``snmp`` and ``redfish``. # # Additional valid choices if IRONIC_IS_HARDWARE == true are: # ``idrac`` and ``irmc``. IRONIC_DEPLOY_DRIVER=${IRONIC_DEPLOY_DRIVER:-ipmi} # If the requested driver is not yet enable, enable it, if it is not it will fail anyway if [[ -z "$(echo ${IRONIC_ENABLED_HARDWARE_TYPES} | grep -w ${IRONIC_DEPLOY_DRIVER})" ]]; then die "The deploy driver $IRONIC_DEPLOY_DRIVER is not in the list of enabled \ hardware types $IRONIC_ENABLED_HARDWARE_TYPES" fi # Support entry points installation of console scripts IRONIC_BIN_DIR=$(get_python_exec_prefix) IRONIC_UWSGI_CONF=$IRONIC_CONF_DIR/ironic-uwsgi.ini IRONIC_UWSGI=$IRONIC_BIN_DIR/ironic-api-wsgi # Lets support IPv6 testing! IRONIC_IP_VERSION=${IRONIC_IP_VERSION:-${IP_VERSION:-4}} # Ironic connection info. Note the port must be specified. if is_service_enabled tls-proxy; then IRONIC_SERVICE_PROTOCOL=https fi IRONIC_SERVICE_PROTOCOL=${IRONIC_SERVICE_PROTOCOL:-$SERVICE_PROTOCOL} IRONIC_SERVICE_PORT=${IRONIC_SERVICE_PORT:-6385} IRONIC_SERVICE_PORT_INT=${IRONIC_SERVICE_PORT_INT:-16385} # If ironic api running under apache or UWSGI we use the path rather than port if [[ "$IRONIC_USE_WSGI" == "True" ]]; then IRONIC_HOSTPORT=${IRONIC_HOSTPORT:-$SERVICE_HOST/baremetal} else IRONIC_HOSTPORT=${IRONIC_HOSTPORT:-$SERVICE_HOST:$IRONIC_SERVICE_PORT} fi # Enable iPXE IRONIC_IPXE_ENABLED=$(trueorfalse True IRONIC_IPXE_ENABLED) # Options below are only applied when IRONIC_IPXE_ENABLED is True IRONIC_IPXE_USE_SWIFT=$(trueorfalse False IRONIC_IPXE_USE_SWIFT) IRONIC_HTTP_DIR=${IRONIC_HTTP_DIR:-$IRONIC_DATA_DIR/httpboot} IRONIC_HTTP_PORT=${IRONIC_HTTP_PORT:-3928} # Allow using JSON RPC instead of oslo.messaging IRONIC_RPC_TRANSPORT=${IRONIC_RPC_TRANSPORT:-oslo} IRONIC_JSON_RPC_PORT=${IRONIC_JSON_RPC_PORT:-8089} # The first port in the range to bind the Virtual BMCs. The number of # ports that will be used depends on $IRONIC_VM_COUNT variable, e.g if # $IRONIC_VM_COUNT=3 the ports 6230, 6231 and 6232 will be used for the # Virtual BMCs, one for each VM. IRONIC_VBMC_PORT_RANGE_START=${IRONIC_VBMC_PORT_RANGE_START:-6230} IRONIC_VBMC_CONFIG_FILE=${IRONIC_VBMC_CONFIG_FILE:-$IRONIC_CONF_DIR/virtualbmc/virtualbmc.conf} IRONIC_VBMC_LOGFILE=${IRONIC_VBMC_LOGFILE:-$IRONIC_VM_LOG_DIR/virtualbmc.log} IRONIC_VBMC_SYSTEMD_SERVICE=devstack@virtualbmc.service # Virtual PDU configs IRONIC_VPDU_CONFIG_FILE=${IRONIC_VPDU_CONFIG_FILE:-$IRONIC_CONF_DIR/virtualpdu/virtualpdu.conf} IRONIC_VPDU_PORT_RANGE_START=${IRONIC_VPDU_PORT_RANGE_START:-1} IRONIC_VPDU_LISTEN_PORT=${IRONIC_VPDU_LISTEN_PORT:-1161} IRONIC_VPDU_COMMUNITY=${IRONIC_VPDU_COMMUNITY:-private} IRONIC_VPDU_SNMPDRIVER=${IRONIC_VPDU_SNMPDRIVER:-apc_rackpdu} IRONIC_VPDU_SYSTEMD_SERVICE=devstack@virtualpdu.service # Redfish configs IRONIC_REDFISH_EMULATOR_PORT=${IRONIC_REDFISH_EMULATOR_PORT:-9132} IRONIC_REDFISH_EMULATOR_SYSTEMD_SERVICE="devstack@redfish-emulator.service" IRONIC_REDFISH_EMULATOR_CONFIG=${IRONIC_REDFISH_EMULATOR_CONFIG:-$IRONIC_CONF_DIR/redfish/emulator.conf} # To explicitly enable configuration of Glance with Swift # (which is required by some vendor drivers), set this # variable to true. IRONIC_CONFIGURE_GLANCE_WITH_SWIFT=$(trueorfalse False IRONIC_CONFIGURE_GLANCE_WITH_SWIFT) # The path to the libvirt hooks directory, used if IRONIC_VM_LOG_ROTATE is True IRONIC_LIBVIRT_HOOKS_PATH=${IRONIC_LIBVIRT_HOOKS_PATH:-/etc/libvirt/hooks/} LIBVIRT_STORAGE_POOL=${LIBVIRT_STORAGE_POOL:-"default"} LIBVIRT_STORAGE_POOL_PATH=${LIBVIRT_STORAGE_POOL_PATH:-/var/lib/libvirt/images} # The authentication strategy used by ironic-api. Valid values are: # keystone and noauth. IRONIC_AUTH_STRATEGY=${IRONIC_AUTH_STRATEGY:-keystone} # By default, terminal SSL certificate is disabled. IRONIC_TERMINAL_SSL=$(trueorfalse False IRONIC_TERMINAL_SSL) IRONIC_TERMINAL_CERT_DIR=${IRONIC_TERMINAL_CERT_DIR:-$IRONIC_DATA_DIR/terminal_cert/} # This flag is used to allow adding Link-Local-Connection info # to ironic port-create command. LLC info is obtained from # IRONIC_{VM,HW}_NODES_FILE IRONIC_USE_LINK_LOCAL=$(trueorfalse False IRONIC_USE_LINK_LOCAL) # Allow selecting dhcp provider IRONIC_DHCP_PROVIDER=${IRONIC_DHCP_PROVIDER:-neutron} # This is the network interface to use for a node IRONIC_NETWORK_INTERFACE=${IRONIC_NETWORK_INTERFACE:-} # Ironic provision network name, if this value is set it means we are using # multi-tenant networking. If not set, then we are not using multi-tenant # networking and are therefore using a 'flat' network. IRONIC_PROVISION_NETWORK_NAME=${IRONIC_PROVISION_NETWORK_NAME:-} # Provision network provider type. Can be flat or vlan. # This is only used if IRONIC_PROVISION_NETWORK_NAME has been set. IRONIC_PROVISION_PROVIDER_NETWORK_TYPE=${IRONIC_PROVISION_PROVIDER_NETWORK_TYPE:-'vlan'} # If IRONIC_PROVISION_PROVIDER_NETWORK_TYPE is vlan. VLAN_ID may be specified. If it is not set, # vlan will be allocated dynamically. # This is only used if IRONIC_PROVISION_NETWORK_NAME has been set. IRONIC_PROVISION_SEGMENTATION_ID=${IRONIC_PROVISION_SEGMENTATION_ID:-} if [[ "$IRONIC_IP_VERSION" != '6' ]]; then # NOTE(TheJulia): Lets not try and support mixed mode since the conductor # can't support mixed mode operation. We are either IPv4 OR IPv6. IRONIC_IP_VERSION='4' # Allocation network pool for provision network # Example: IRONIC_PROVISION_ALLOCATION_POOL=start=10.0.5.10,end=10.0.5.100 # This is only used if IRONIC_PROVISION_NETWORK_NAME has been set. IRONIC_PROVISION_ALLOCATION_POOL=${IRONIC_PROVISION_ALLOCATION_POOL:-'start=10.0.5.10,end=10.0.5.100'} # With multinode case all ironic-conductors should have IP from provisioning network. # IRONIC_PROVISION_SUBNET_GATEWAY - is configured on primary node. # Ironic provision subnet gateway. IRONIC_PROVISION_SUBNET_GATEWAY=${IRONIC_PROVISION_SUBNET_GATEWAY:-'10.0.5.1'} IRONIC_PROVISION_SUBNET_SUBNODE_IP=${IRONIC_PROVISION_SUBNET_SUBNODE_IP:-'10.0.5.2'} # Ironic provision subnet prefix # Example: IRONIC_PROVISION_SUBNET_PREFIX=10.0.5.0/24 IRONIC_PROVISION_SUBNET_PREFIX=${IRONIC_PROVISION_SUBNET_PREFIX:-'10.0.5.0/24'} else IRONIC_IP_VERSION='6' # NOTE(TheJulia): The IPv6 address devstack has identified is the # local loopback. This does not really serve our purposes very # well, so we need to setup something that will work. if [[ "$HOST_IPV6" == '::1' ]] || [[ ! $HOST_IPV6 =~ "::" ]]; then # We setup an address elsewhere because the service address of # loopback cannot be used for v6 testing. IRONIC_HOST_IPV6='fc00::1' else IRONIC_HOST_IPV6=$SERVICE_HOST fi IRONIC_PROVISION_SUBNET_GATEWAY=${IRONIC_PROVISION_SUBNET_GATEWAY:-'fc01::1'} IRONIC_PROVISION_SUBNET_SUBNODE_IP=${IRONIC_PROVISION_SUBNET_SUBNODE_IP:-'fc01::2'} IRONIC_PROVISION_SUBNET_PREFIX=${IRONIC_PROVISION_SUBNET_PREFIX:-'fc01::/64'} IRONIC_TFTPSERVER_IP=$IRONIC_HOST_IPV6 fi IRONIC_ROUTER_NAME=${Q_ROUTER_NAME:-router1} # Ironic provision subnet name. # This is only used if IRONIC_PROVISION_NETWORK_NAME has been set. IRONIC_PROVISION_PROVIDER_SUBNET_NAME=${IRONIC_PROVISION_PROVIDER_SUBNET_NAME:-${IRONIC_PROVISION_NETWORK_NAME}-subnet} # When enabled this will set the physical_network attribute for ironic ports # and subnet-to-segment association on provisioning network will be configured. # NOTE: The neutron segments service_plugin must be loaded for this. IRONIC_USE_NEUTRON_SEGMENTS=$(trueorfalse False IRONIC_USE_NEUTRON_SEGMENTS) # This is the storage interface to use for a node # Only 'cinder' can be set for testing boot from volume IRONIC_STORAGE_INTERFACE=${IRONIC_STORAGE_INTERFACE:-} # With multinode case all ironic-conductors should have IP from provisioning network. # IRONIC_PROVISION_SUBNET_GATEWAY - is configured on primary node. # Ironic provision subnet gateway. IRONIC_PROVISION_SUBNET_GATEWAY=${IRONIC_PROVISION_SUBNET_GATEWAY:-'10.0.5.1'} IRONIC_PROVISION_SUBNET_SUBNODE_IP=${IRONIC_PROVISION_SUBNET_SUBNODE_IP:-'10.0.5.2'} # Ironic provision subnet prefix # Example: IRONIC_PROVISION_SUBNET_PREFIX=10.0.5.0/24 IRONIC_PROVISION_SUBNET_PREFIX=${IRONIC_PROVISION_SUBNET_PREFIX:-'10.0.5.0/24'} if [[ "$HOST_TOPOLOGY_ROLE" == "primary" ]]; then # Some CI jobs get triggered without a HOST_TOPOLOGY_ROLE # If so, none of this logic is, or needs to be executed. IRONIC_TFTPSERVER_IP=$IRONIC_PROVISION_SUBNET_GATEWAY IRONIC_HTTP_SERVER=$IRONIC_PROVISION_SUBNET_GATEWAY fi if [[ "$HOST_TOPOLOGY_ROLE" == "subnode" ]]; then IRONIC_TFTPSERVER_IP=$IRONIC_PROVISION_SUBNET_SUBNODE_IP IRONIC_HTTP_SERVER=$IRONIC_PROVISION_SUBNET_SUBNODE_IP fi # NOTE(TheJulia): Last catch for this being set or not. # should only work for v4. IRONIC_HTTP_SERVER=${IRONIC_HTTP_SERVER:-$IRONIC_TFTPSERVER_IP} # Port that must be permitted for iSCSI connections to be # established from the tenant network. ISCSI_SERVICE_PORT=${ISCSI_SERVICE_PORT:-3260} # Retrieving logs from the deploy ramdisk # # IRONIC_DEPLOY_LOGS_COLLECT possible values are: # * always: Collect the ramdisk logs from the deployment on success or # failure (Default in DevStack for debugging purpose). # * on_failure: Collect the ramdisk logs upon a deployment failure # (Default in Ironic). # * never: Never collect the ramdisk logs. IRONIC_DEPLOY_LOGS_COLLECT=${IRONIC_DEPLOY_LOGS_COLLECT:-always} # IRONIC_DEPLOY_LOGS_STORAGE_BACKEND possible values are: # * local: To store the logs in the local filesystem (Default in Ironic and DevStack). # * swift: To store the logs in Swift. IRONIC_DEPLOY_LOGS_STORAGE_BACKEND=${IRONIC_DEPLOY_LOGS_STORAGE_BACKEND:-local} # The path to the directory where Ironic should put the logs when IRONIC_DEPLOY_LOGS_STORAGE_BACKEND is set to "local" IRONIC_DEPLOY_LOGS_LOCAL_PATH=${IRONIC_DEPLOY_LOGS_LOCAL_PATH:-$IRONIC_VM_LOG_DIR/deploy_logs} # Fast track option IRONIC_DEPLOY_FAST_TRACK=${IRONIC_DEPLOY_FAST_TRACK:-False} # Agent Token requirement IRONIC_REQUIRE_AGENT_TOKEN=${IRONIC_REQUIRE_AGENT_TOKEN:-True} # Define baremetal min_microversion in tempest config. Default value None is picked from tempest. TEMPEST_BAREMETAL_MIN_MICROVERSION=${TEMPEST_BAREMETAL_MIN_MICROVERSION:-} # Define baremetal max_microversion in tempest config. No default value means that it is picked from tempest. TEMPEST_BAREMETAL_MAX_MICROVERSION=${TEMPEST_BAREMETAL_MAX_MICROVERSION:-} # TODO(TheJulia): This PHYSICAL_NETWORK needs to be refactored in # our devstack plugin. It is used by the neutron-legacy integration, # however they want to name the new variable for the current neutron # plugin NEUTRON_PHYSICAL_NETWORK. For now we'll do some magic and # change it later once we migrate our jobs. PHYSICAL_NETWORK=${NEUTRON_PHYSICAL_NETWORK:-${PHYSICAL_NETWORK:-}} # get_pxe_boot_file() - Get the PXE/iPXE boot file path function get_pxe_boot_file { local pxe_boot_file if [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then # TODO(TheJulia): This is not UEFI safe. if is_ubuntu; then pxe_boot_file=/usr/lib/ipxe/undionly.kpxe elif is_fedora || is_suse; then pxe_boot_file=/usr/share/ipxe/undionly.kpxe fi else # Standard PXE if is_ubuntu; then # Ubuntu Xenial (16.04) places the file under /usr/lib/PXELINUX pxe_paths="/usr/lib/syslinux/pxelinux.0 /usr/lib/PXELINUX/pxelinux.0" for p in $pxe_paths; do if [[ -f $p ]]; then pxe_boot_file=$p fi done elif is_fedora || is_suse; then pxe_boot_file=/usr/share/syslinux/pxelinux.0 fi fi echo $pxe_boot_file } # PXE boot image IRONIC_PXE_BOOT_IMAGE=${IRONIC_PXE_BOOT_IMAGE:-$(get_pxe_boot_file)} IRONIC_AUTOMATED_CLEAN_ENABLED=$(trueorfalse True IRONIC_AUTOMATED_CLEAN_ENABLED) IRONIC_SECURE_BOOT=${IRONIC_SECURE_BOOT:-False} IRONIC_UEFI_BOOT_LOADER=${IRONIC_UEFI_BOOT_LOADER:-grub2} IRONIC_GRUB2_SHIM_FILE=${IRONIC_GRUB2_SHIM_FILE:-} IRONIC_GRUB2_FILE=${IRONIC_GRUB2_FILE:-} IRONIC_UEFI_FILES_DIR=${IRONIC_UEFI_FILES_DIR:-/var/lib/libvirt/images} UEFI_LOADER_PATH=$IRONIC_UEFI_FILES_DIR/OVMF_CODE.fd UEFI_NVRAM_PATH=$IRONIC_UEFI_FILES_DIR/OVMF_VARS.fd # Handle architecture specific package installs if [[ $IRONIC_HW_ARCH == "x86_64" ]]; then install_package shim if is_ubuntu; then install_package grub-efi-amd64-signed elif is_fedora; then install_package grub2-efi fi fi # Sanity checks if [[ "$IRONIC_BOOT_MODE" == "uefi" ]]; then if [[ "$IRONIC_IPXE_ENABLED" == "False" ]] && [[ "$IRONIC_UEFI_BOOT_LOADER" != "grub2" ]]; then die $LINENO "Boot mode UEFI is only supported with iPXE and grub2 bootloaders." fi if ! is_fedora && ! is_ubuntu; then die $LINENO "Boot mode UEFI only works in Ubuntu or Fedora for now." fi if is_arch "x86_64"; then if is_ubuntu; then install_package grub-efi elif is_fedora; then install_package grub2 grub2-efi fi fi if is_ubuntu && [[ -z $IRONIC_GRUB2_FILE ]]; then IRONIC_GRUB2_SHIM_FILE=/usr/lib/shim/shimx64.efi IRONIC_GRUB2_FILE=/usr/lib/grub/x86_64-efi-signed/grubx64.efi.signed fi if [[ "$IRONIC_IPXE_ENABLED" == "False" ]]; then # NOTE(TheJulia): While we no longer directly copy the # IRONIC_GRUB2_FILE, we still check the existence as # without the bootloader package we would be unable to build # the netboot core image. if [[ -z $IRONIC_GRUB2_SHIM_FILE ]] || [[ -z $IRONIC_GRUB2_FILE ]] || [[ ! -f $IRONIC_GRUB2_SHIM_FILE ]] || [[ ! -f $IRONIC_GRUB2_FILE ]]; then die $LINENO "Grub2 Bootloader and Shim file missing." fi fi fi # TODO(dtantsur): change this when we change the default value. IRONIC_DEFAULT_BOOT_OPTION=${IRONIC_DEFAULT_BOOT_OPTION:-local} if [ $IRONIC_DEFAULT_BOOT_OPTION != "netboot" ] && [ $IRONIC_DEFAULT_BOOT_OPTION != "local" ]; then die $LINENO "Supported values for IRONIC_DEFAULT_BOOT_OPTION are 'netboot' and 'local' only." fi # TODO(pas-ha) find a way to (cross-)sign the custom CA bundle used by tls-proxy # with default iPXE cert - for reference see http://ipxe.org/crypto if is_service_enabled tls-proxy && [[ "$IRONIC_IPXE_USE_SWIFT" == "True" ]]; then die $LINENO "Ironic in DevStack does not yet support booting iPXE from HTTPS URLs" fi # Timeout for "manage" action. 2 minutes is more than enough. IRONIC_MANAGE_TIMEOUT=${IRONIC_MANAGE_TIMEOUT:-120} # Timeout for "provide" action. This involves cleaning. Generally, 15 minutes # should be enough, but real hardware may need more. IRONIC_CLEANING_TIMEOUT=${IRONIC_CLEANING_TIMEOUT:-1200} IRONIC_CLEANING_DELAY=10 IRONIC_CLEANING_ATTEMPTS=$(( $IRONIC_CLEANING_TIMEOUT / $IRONIC_CLEANING_DELAY )) # Timeout for ironic-neutron-agent to report state before providing nodes. # The agent reports every 60 seconds, 2 minutes should do. IRONIC_NEUTRON_AGENT_REPORT_STATE_DELAY=10 IRONIC_NEUTRON_AGENT_REPORT_STATE_TIMEOUT=${IRONIC_NEUTRON_AGENT_REPORT_STATE_TIMEOUT:-120} IRONIC_NEUTRON_AGENT_REPORT_STATE_ATTEMPTS=$(( $IRONIC_NEUTRON_AGENT_REPORT_STATE_TIMEOUT / IRONIC_NEUTRON_AGENT_REPORT_STATE_DELAY )) # Username to use by Ansible to access ramdisk, # to be set as '[ansible]/default_username' option. # If not set here (default), will be set to 'tc' for TinyIPA ramdisk, # for other ramdisks it must be either provided here, # or set manually per-node via ironic API IRONIC_ANSIBLE_SSH_USER=${IRONIC_ANSIBLE_SSH_USER:-} # Path to the private SSH key to use by ansible deploy interface # that will be set as '[ansible]/default_key_file' option in config. # The public key path is assumed to be ${IRONIC_ANSIBLE_SSH_KEY}.pub # and will be used when rebuilding the image to include this public key # in ~/.ssh/authorized_keys of a $IRONIC_ANSIBLE_SSH_USER in the ramdisk. # Only the TinyIPA ramdisks are currently supported for such rebuild. # For TinyIPA ramdisks, if the specified file doesn't exist, it will # be created and will contain a new RSA passwordless key. We assume # that the directories in the path to this file exist and are # writable. # For other ramdisk types, make sure the corresponding public key is baked into # the ramdisk to be used by DevStack and provide the path to the private key here, # or set it manually per node via ironic API. # FIXME(pas-ha) auto-generated keys currently won't work for multi-node # DevStack deployment, as we do not distribute this generated key to subnodes yet. IRONIC_ANSIBLE_SSH_KEY=${IRONIC_ANSIBLE_SSH_KEY:-$IRONIC_DATA_DIR/ansible_ssh_key} IRONIC_AGENT_IMAGE_DOWNLOAD_SOURCE=${IRONIC_AGENT_IMAGE_DOWNLOAD_SOURCE:-swift} # Functions # --------- # UEFI related functions function get_uefi_ipxe_boot_file { if is_ubuntu; then echo /usr/lib/ipxe/ipxe.efi elif is_fedora; then echo /usr/share/ipxe/ipxe-x86_64.efi fi } function get_uefi_loader { if is_ubuntu; then echo /usr/share/OVMF/OVMF_CODE.fd elif is_fedora; then echo /usr/share/edk2/ovmf/OVMF_CODE.fd fi } function get_uefi_nvram { if is_ubuntu; then echo /usr/share/OVMF/OVMF_VARS.fd elif is_fedora; then echo /usr/share/edk2/ovmf/OVMF_VARS.fd fi } # Misc function restart_libvirt { local libvirt_service_name="libvirtd" if is_ubuntu && ! type libvirtd; then libvirt_service_name="libvirt-bin" fi restart_service $libvirt_service_name } # Test if any Ironic services are enabled # is_ironic_enabled function is_ironic_enabled { [[ ,${ENABLED_SERVICES} =~ ,"ir-" ]] && return 0 return 1 } function is_deployed_by_agent { [[ -z "${IRONIC_DEPLOY_DRIVER%%agent*}" || "$IRONIC_DEFAULT_DEPLOY_INTERFACE" == "direct" ]] && return 0 return 1 } function is_deployed_by_ipmi { [[ "$IRONIC_DEPLOY_DRIVER" == ipmi ]] && return 0 return 1 } function is_deployed_by_ilo { [[ "${IRONIC_DEPLOY_DRIVER}" == ilo ]] && return 0 return 1 } function is_deployed_by_drac { [[ "${IRONIC_DEPLOY_DRIVER}" == idrac ]] && return 0 return 1 } function is_deployed_by_snmp { [[ "${IRONIC_DEPLOY_DRIVER}" == snmp ]] && return 0 return 1 } function is_deployed_by_redfish { [[ "$IRONIC_DEPLOY_DRIVER" == redfish ]] && return 0 return 1 } function is_deployed_by_irmc { [[ "$IRONIC_DEPLOY_DRIVER" == irmc ]] && return 0 return 1 } function is_deployed_by_xclarity { [[ "$IRONIC_DEPLOY_DRIVER" == xclarity ]] && return 0 return 1 } function is_drac_enabled { [[ -z "${IRONIC_ENABLED_HARDWARE_TYPES%%*idrac*}" ]] && return 0 return 1 } function is_ansible_deploy_enabled { [[ -z "${IRONIC_ENABLED_DEPLOY_INTERFACES%%*ansible*}" ]] && return 0 return 1 } function is_redfish_enabled { [[ -z "${IRONIC_ENABLED_HARDWARE_TYPES%%*redfish*}" ]] && return 0 return 1 } function is_ansible_with_tinyipa { # NOTE(pas-ha) we support rebuilding the ramdisk to include (generated) SSH keys # as needed for ansible deploy interface only for TinyIPA ramdisks for now is_ansible_deploy_enabled && [[ "$IRONIC_RAMDISK_TYPE" == "tinyipa" ]] && return 0 return 1 } function is_glance_configuration_required { is_deployed_by_agent || is_ansible_deploy_enabled || [[ "$IRONIC_CONFIGURE_GLANCE_WITH_SWIFT" == "True" ]] && return 0 return 1 } function is_deploy_iso_required { [[ "$IRONIC_IS_HARDWARE" == "True" && "$IRONIC_DEPLOY_ISO_REQUIRED" == "True" ]] && return 0 return 1 } # Assert that the redfish hardware type is enabled in case we are using # the redfish driver if is_deployed_by_redfish && [[ "$IRONIC_ENABLED_HARDWARE_TYPES" != *"redfish"* ]]; then die $LINENO "Please make sure that the redfish hardware" \ "type is enabled. Take a look at the " \ "IRONIC_ENABLED_HARDWARE_TYPES configuration option" \ "for DevStack" fi # Assert that for non-TynyIPA ramdisks and Ansible, the private SSH key file to use exists. if is_ansible_deploy_enabled && [[ "$IRONIC_RAMDISK_TYPE" != "tinyipa" ]]; then if [[ ! -f $IRONIC_ANSIBLE_SSH_KEY ]]; then die $LINENO "Using non-TinyIPA ramdisks with ansible deploy interface" \ "requires setting IRONIC_ANSIBLE_SSH_KEY to existing"\ "private SSH key file to be used by Ansible." fi fi # Syslinux >= 5.00 pxelinux.0 binary is not "stand-alone" anymore, # it depends on some c32 modules to work correctly. # More info: http://www.syslinux.org/wiki/index.php/Library_modules function setup_syslinux_modules { # Ignore it for iPXE, it doesn't repend on syslinux modules [[ "$IRONIC_IPXE_ENABLED" == "True" ]] && return 0 # Ubuntu Xenial keeps doesn't ship pxelinux.0 as part of syslinux anymore if is_ubuntu && [[ -d /usr/lib/PXELINUX/ ]]; then # TODO(lucasagomes): Figure out whether its UEFI or BIOS once # we have UEFI support in DevStack cp -aR /usr/lib/syslinux/modules/bios/*.c32 $IRONIC_TFTPBOOT_DIR else cp -aR $(dirname $IRONIC_PXE_BOOT_IMAGE)/*.c32 $IRONIC_TFTPBOOT_DIR fi } function start_virtualbmc { start_service $IRONIC_VBMC_SYSTEMD_SERVICE } function stop_virtualbmc { stop_service $IRONIC_VBMC_SYSTEMD_SERVICE } function cleanup_virtualbmc { stop_virtualbmc disable_service $IRONIC_VBMC_SYSTEMD_SERVICE local unitfile="$SYSTEMD_DIR/$IRONIC_VBMC_SYSTEMD_SERVICE" sudo rm -f $unitfile $SYSTEMCTL daemon-reload } function install_virtualbmc { # Install pyghmi from source, if requested, otherwise it will be # downloaded as part of the virtualbmc installation if use_library_from_git "pyghmi"; then git_clone_by_name "pyghmi" setup_dev_lib "pyghmi" fi if use_library_from_git "virtualbmc"; then git_clone_by_name "virtualbmc" setup_dev_lib "virtualbmc" else pip_install_gr "virtualbmc" fi local cmd cmd=$(which vbmcd) cmd+=" --foreground" write_user_unit_file $IRONIC_VBMC_SYSTEMD_SERVICE "$cmd" "" "$STACK_USER" local unitfile="$SYSTEMD_DIR/$IRONIC_VBMC_SYSTEMD_SERVICE" iniset -sudo $unitfile "Service" "Environment" "VIRTUALBMC_CONFIG=$IRONIC_VBMC_CONFIG_FILE" enable_service $IRONIC_VBMC_SYSTEMD_SERVICE } function configure_virtualbmc { if [[ ! -d $(dirname $IRONIC_VBMC_CONFIG_FILE) ]]; then mkdir -p $(dirname $IRONIC_VBMC_CONFIG_FILE) fi iniset -sudo $IRONIC_VBMC_CONFIG_FILE log debug True } function start_virtualpdu { start_service $IRONIC_VPDU_SYSTEMD_SERVICE } function stop_virtualpdu { stop_service $IRONIC_VPDU_SYSTEMD_SERVICE } function cleanup_virtualpdu { stop_virtualpdu disable_service $IRONIC_VPDU_SYSTEMD_SERVICE local unitfile="$SYSTEMD_DIR/$IRONIC_VPDU_SYSTEMD_SERVICE" sudo rm -f $unitfile $SYSTEMCTL daemon-reload } function install_virtualpdu { if use_library_from_git "virtualpdu"; then git_clone_by_name "virtualpdu" setup_dev_lib "virtualpdu" else pip_install "virtualpdu" fi local cmd cmd=$(which virtualpdu) cmd+=" $IRONIC_VPDU_CONFIG_FILE" write_user_unit_file $IRONIC_VPDU_SYSTEMD_SERVICE "$cmd" "" "$STACK_USER" enable_service $IRONIC_VPDU_SYSTEMD_SERVICE } function configure_virtualpdu { mkdir -p $(dirname $IRONIC_VPDU_CONFIG_FILE) iniset -sudo $IRONIC_VPDU_CONFIG_FILE global debug True iniset -sudo $IRONIC_VPDU_CONFIG_FILE global libvirt_uri "qemu:///system" iniset -sudo $IRONIC_VPDU_CONFIG_FILE PDU listen_address ${HOST_IP} iniset -sudo $IRONIC_VPDU_CONFIG_FILE PDU listen_port ${IRONIC_VPDU_LISTEN_PORT} iniset -sudo $IRONIC_VPDU_CONFIG_FILE PDU community ${IRONIC_VPDU_COMMUNITY} iniset -sudo $IRONIC_VPDU_CONFIG_FILE PDU ports $(_generate_pdu_ports) iniset -sudo $IRONIC_VPDU_CONFIG_FILE PDU outlet_default_state "OFF" } # _generate_pdu_ports() - Generates list of port:node_name. function _generate_pdu_ports { pdu_port_number=${IRONIC_VPDU_PORT_RANGE_START} port_config=() for vm_name in $(_ironic_bm_vm_names); do port_config+=("${pdu_port_number}:${vm_name}") pdu_port_number=$(( pdu_port_number + 1 )) done echo ${port_config[*]} | tr ' ' ',' } function start_redfish { start_service $IRONIC_REDFISH_EMULATOR_SYSTEMD_SERVICE } function stop_redfish { stop_service $IRONIC_REDFISH_EMULATOR_SYSTEMD_SERVICE } function cleanup_redfish { stop_redfish rm -f $IRONIC_REDFISH_EMULATOR_CONFIG disable_service $IRONIC_REDFISH_EMULATOR_SYSTEMD_SERVICE local unitfile="$SYSTEMD_DIR/$IRONIC_REDFISH_EMULATOR_SYSTEMD_SERVICE" sudo rm -f $unitfile $SYSTEMCTL daemon-reload } function install_redfish { # TODO(lucasagomes): Use Apache WSGI instead of gunicorn gunicorn=gunicorn if is_ubuntu; then if python3_enabled; then gunicorn=${gunicorn}3 fi install_package $gunicorn else pip_install_gr "gunicorn" fi if use_library_from_git "sushy-tools"; then git_clone_by_name "sushy-tools" setup_dev_lib "sushy-tools" else pip_install "sushy-tools" fi local cmd cmd=$(which $gunicorn) cmd+=" sushy_tools.emulator.main:app" cmd+=" --bind ${HOST_IP}:${IRONIC_REDFISH_EMULATOR_PORT}" cmd+=" --env FLASK_DEBUG=1" cmd+=" --env SUSHY_EMULATOR_CONFIG=${IRONIC_REDFISH_EMULATOR_CONFIG}" # NOTE(dtantsur): handling virtual media ISO can take time, so increase # both concurrency and the worker timeout. cmd+=" --workers 2 --threads 2 --timeout 90" write_user_unit_file $IRONIC_REDFISH_EMULATOR_SYSTEMD_SERVICE "$cmd" "" "$STACK_USER" enable_service $IRONIC_REDFISH_EMULATOR_SYSTEMD_SERVICE } function configure_redfish { if [[ ! -d $(dirname $IRONIC_REDFISH_EMULATOR_CONFIG) ]]; then mkdir -p $(dirname $IRONIC_REDFISH_EMULATOR_CONFIG) fi cat - < $IRONIC_REDFISH_EMULATOR_CONFIG SUSHY_EMULATOR_BOOT_LOADER_MAP = { 'UEFI': { 'x86_64': '$UEFI_LOADER_PATH' }, 'Legacy': { 'x86_64': None } } EOF } function setup_sushy { if use_library_from_git "sushy"; then git_clone_by_name "sushy" setup_dev_lib "sushy" else pip_install_gr "sushy" fi } # install_ironic() - Install the things! function install_ironic { # NOTE(vsaienko) do not check required_services on subnode if [[ "$HOST_TOPOLOGY_ROLE" != "subnode" ]]; then # make sure all needed service were enabled local req_services="key" if is_service_enabled nova && [[ "$VIRT_DRIVER" == "ironic" ]]; then req_services+=" nova glance neutron" fi for srv in $req_services; do if ! is_service_enabled "$srv"; then die $LINENO "$srv should be enabled for Ironic." fi done fi if use_library_from_git "ironic-lib"; then git_clone_by_name "ironic-lib" setup_dev_lib "ironic-lib" fi setup_develop $IRONIC_DIR if [[ "$IRONIC_USE_WSGI" == "True" || "$IRONIC_IPXE_ENABLED" == "True" ]]; then install_apache_wsgi fi if [[ "$IRONIC_BOOT_MODE" == "uefi" && "$IRONIC_IS_HARDWARE" == "False" ]]; then # Append the nvram configuration to libvirt if it's not present already if ! sudo grep -q "^nvram" /etc/libvirt/qemu.conf; then echo "nvram=[\"$UEFI_LOADER_PATH:$UEFI_NVRAM_PATH\"]" | sudo tee -a /etc/libvirt/qemu.conf fi # Replace the default virtio PXE ROM in QEMU with an EFI capable # one. The EFI ROM should work on with both boot modes, Legacy # BIOS and UEFI. if is_ubuntu; then # (rpittau) in bionic the UEFI in the ovmf 0~20180205.c0d9813c-2 # package is broken: EFI v2.70 by EDK II # As a workaround, here we download and install the old working # version from the multiverse repository: EFI v2.60 by EDK II # Bug reference: # https://bugs.launchpad.net/ubuntu/+source/edk2/+bug/1821729 local temp_deb temp_deb="$(mktemp)" wget http://archive.ubuntu.com/ubuntu/pool/multiverse/e/edk2/ovmf_0~20160408.ffea0a2c-2_all.deb -O "$temp_deb" sudo dpkg -i "$temp_deb" rm -f "$temp_deb" # NOTE(TheJulia): This no longer seems required as the ovmf images # DO correctly network boot. The effect of this is making the # default boot loader iPXE, which is not always desired nor # realistic for hardware in the field. # If it is after Train, we should likely just delete the lines # below and consider the same for Fedora. # sudo rm /usr/share/qemu/pxe-virtio.rom # sudo ln -s /usr/lib/ipxe/qemu/efi-virtio.rom /usr/share/qemu/pxe-virtio.rom elif is_fedora; then sudo rm /usr/share/qemu/pxe-virtio.rom sudo ln -s /usr/share/ipxe.efi/1af41000.rom /usr/share/qemu/pxe-virtio.rom fi # Restart libvirt to the changes to take effect restart_libvirt fi if is_redfish_enabled || is_deployed_by_redfish; then setup_sushy fi if [[ "$IRONIC_IS_HARDWARE" == "False" ]]; then if is_deployed_by_ipmi; then install_virtualbmc fi if is_deployed_by_snmp; then install_virtualpdu fi if is_deployed_by_redfish; then install_redfish fi fi if is_drac_enabled; then pip_install python-dracclient fi if is_ansible_deploy_enabled; then pip_install "$(grep '^ansible' $IRONIC_DIR/driver-requirements.txt | awk '{print $1}')" fi } # install_ironicclient() - Collect sources and prepare function install_ironicclient { if use_library_from_git "python-ironicclient"; then git_clone_by_name "python-ironicclient" setup_dev_lib "python-ironicclient" else # nothing actually "requires" ironicclient, so force instally from pypi pip_install_gr python-ironicclient fi } # _cleanup_ironic_apache_additions() - Remove uwsgi files, disable and remove apache vhost file function _cleanup_ironic_apache_additions { if [[ "$IRONIC_IPXE_ENABLED" == "True" ]]; then sudo rm -rf $IRONIC_HTTP_DIR disable_apache_site ipxe-ironic sudo rm -f $(apache_site_config_for ipxe-ironic) fi if [[ "$IRONIC_USE_WSGI" == "True" ]]; then remove_uwsgi_config "$IRONIC_UWSGI_CONF" "$IRONIC_UWSGI" fi restart_apache_server } # _config_ironic_apache_ipxe() - Configure ironic IPXE site function _config_ironic_apache_ipxe { local ipxe_apache_conf ipxe_apache_conf=$(apache_site_config_for ipxe-ironic) sudo cp $IRONIC_DEVSTACK_FILES_DIR/apache-ipxe-ironic.template $ipxe_apache_conf sudo sed -e " s|%PUBLICPORT%|$IRONIC_HTTP_PORT|g; s|%HTTPROOT%|$IRONIC_HTTP_DIR|g; s|%APACHELOGDIR%|$APACHE_LOG_DIR|g; " -i $ipxe_apache_conf enable_apache_site ipxe-ironic } # cleanup_ironic_config_files() - Remove residual cache/config/log files, # left over from previous runs that would need to clean up. function cleanup_ironic_config_files { sudo rm -rf $IRONIC_AUTH_CACHE_DIR $IRONIC_CONF_DIR sudo rm -rf $IRONIC_VM_LOG_DIR/* } # cleanup_ironic() - Clean everything left from Ironic function cleanup_ironic { cleanup_ironic_config_files # Cleanup additions made to Apache if [[ "$IRONIC_USE_WSGI" == "True" || "$IRONIC_IPXE_ENABLED" == "True" ]]; then _cleanup_ironic_apache_additions fi cleanup_virtualbmc cleanup_virtualpdu cleanup_redfish # Remove the hook to disable log rotate sudo rm -rf $IRONIC_LIBVIRT_HOOKS_PATH/qemu } # configure_ironic_dirs() - Create all directories required by Ironic and # associated services. function configure_ironic_dirs { sudo install -d -o $STACK_USER $IRONIC_CONF_DIR $STACK_USER $IRONIC_DATA_DIR \ $IRONIC_STATE_PATH $IRONIC_TFTPBOOT_DIR $IRONIC_TFTPBOOT_DIR/pxelinux.cfg sudo chown -R $STACK_USER:$STACK_USER $IRONIC_TFTPBOOT_DIR if [[ "$IRONIC_IPXE_ENABLED" == "True" ]]; then sudo install -d -o $STACK_USER -g $STACK_USER $IRONIC_HTTP_DIR fi if [ ! -f "$IRONIC_PXE_BOOT_IMAGE" ]; then die $LINENO "PXE boot file $IRONIC_PXE_BOOT_IMAGE not found." fi # Copy PXE binary # NOTE(mjturek): The PXE binary is x86_64 specific. So it should only be copied when # deploying to an x86_64 node. if [[ $IRONIC_HW_ARCH == "x86_64" ]]; then cp $IRONIC_PXE_BOOT_IMAGE $IRONIC_TFTPBOOT_DIR setup_syslinux_modules fi if [[ "$IRONIC_BOOT_MODE" == "uefi" ]]; then local uefi_boot_file uefi_boot_file=$(get_uefi_ipxe_boot_file) if [ ! -f $uefi_boot_file ]; then die $LINENO "UEFI boot file $uefi_boot_file not found." fi cp $uefi_boot_file $IRONIC_TFTPBOOT_DIR if [[ "$IRONIC_IS_HARDWARE" == "False" ]]; then local uefi_loader local uefi_nvram # Copy the OVMF images to libvirt's path uefi_loader=$(get_uefi_loader) uefi_nvram=$(get_uefi_nvram) sudo cp $uefi_loader $UEFI_LOADER_PATH sudo cp $uefi_nvram $UEFI_NVRAM_PATH fi fi # Create the logs directory when saving the deploy logs to the filesystem if [[ "$IRONIC_DEPLOY_LOGS_STORAGE_BACKEND" == "local" && "$IRONIC_DEPLOY_LOGS_COLLECT" != "never" ]]; then install -d -o $STACK_USER $IRONIC_DEPLOY_LOGS_LOCAL_PATH fi } function configure_ironic_networks { if [[ -n "${IRONIC_PROVISION_NETWORK_NAME}" ]]; then echo_summary "Configuring Ironic provisioning network" configure_ironic_provision_network fi echo_summary "Configuring Ironic cleaning network" configure_ironic_cleaning_network echo_summary "Configuring Ironic rescue network" configure_ironic_rescue_network } function configure_ironic_cleaning_network { iniset $IRONIC_CONF_FILE neutron cleaning_network $IRONIC_CLEAN_NET_NAME } function configure_ironic_rescue_network { iniset $IRONIC_CONF_FILE neutron rescuing_network $IRONIC_RESCUE_NET_NAME } function configure_ironic_provision_network { if [[ "$IP_VERSION" == "6" ]]; then # NOTE(TheJulia): Ideally we should let this happen # with our global address, but iPXE seems to have in # consistant behavior in this configuration with devstack. # so we will setup a dummy interface and use that. sudo ip link add magicv6 type dummy sudo ip link set dev magicv6 up sudo ip -6 addr add $IRONIC_HOST_IPV6/64 dev magicv6 fi if is_service_enabled neutron-api; then if [[ "$IRONIC_IP_VERSION" == "6" ]]; then sudo sysctl -w net.ipv6.conf.all.proxy_ndp=1 configure_neutron_l3_lower_v6_ra fi # Neutron agent needs to be pre-configured before proceeding down the # path of configuring the provision network. This was done for us in # the legacy neutron code. neutron_plugin_configure_plugin_agent # This prior step updates configuration related to physnet mappings, # and we must restart neutron as a result stop_neutron sleep 15 # By default, upon start, neutron tries to create the networks... NEUTRON_CREATE_INITIAL_NETWORKS=False start_neutron_api start_neutron fi # This is only called if IRONIC_PROVISION_NETWORK_NAME has been set and # means we are using multi-tenant networking. local net_id local ironic_provision_network_ip # NOTE(vsaienko) For multinode case there is no need to create a new provisioning # network on subnode, as it was created on primary node. Just get an existed network UUID. if [[ "$HOST_TOPOLOGY_ROLE" != "subnode" ]]; then die_if_not_set $LINENO IRONIC_PROVISION_SUBNET_PREFIX "You must specify the IRONIC_PROVISION_SUBNET_PREFIX" die_if_not_set $LINENO PHYSICAL_NETWORK "You must specify the PHYSICAL_NETWORK" die_if_not_set $LINENO IRONIC_PROVISION_SUBNET_GATEWAY "You must specify the IRONIC_PROVISION_SUBNET_GATEWAY" net_id=$(openstack network create --provider-network-type $IRONIC_PROVISION_PROVIDER_NETWORK_TYPE \ --provider-physical-network "$PHYSICAL_NETWORK" \ ${IRONIC_PROVISION_SEGMENTATION_ID:+--provider-segment $IRONIC_PROVISION_SEGMENTATION_ID} \ ${IRONIC_PROVISION_NETWORK_NAME} -f value -c id) die_if_not_set $LINENO net_id "Failure creating net_id for $IRONIC_PROVISION_NETWORK_NAME" if [[ "${IRONIC_USE_NEUTRON_SEGMENTS}" == "True" ]]; then local net_segment_id net_segment_id=$(openstack network segment list --network $net_id -f value -c ID) die_if_not_set $LINENO net_segment_id "Failure getting net_segment_id for $IRONIC_PROVISION_NETWORK_NAME" fi local subnet_id if [[ "$IRONIC_IP_VERSION" == '4' ]]; then subnet_id="$(openstack subnet create --ip-version 4 \ ${IRONIC_PROVISION_ALLOCATION_POOL:+--allocation-pool $IRONIC_PROVISION_ALLOCATION_POOL} \ ${net_segment_id:+--network-segment $net_segment_id} \ $IRONIC_PROVISION_PROVIDER_SUBNET_NAME \ --gateway $IRONIC_PROVISION_SUBNET_GATEWAY --network $net_id \ --subnet-range $IRONIC_PROVISION_SUBNET_PREFIX -f value -c id)" else subnet_id="$(openstack subnet create --ip-version 6 \ --ipv6-address-mode dhcpv6-stateful \ --ipv6-ra-mode dhcpv6-stateful \ --dns-nameserver 2001:4860:4860::8888 \ ${net_segment_id:+--network-segment $net_segment_id} \ $IRONIC_PROVISION_PROVIDER_SUBNET_NAME \ --gateway $IRONIC_PROVISION_SUBNET_GATEWAY --network $net_id \ --subnet-range $IRONIC_PROVISION_SUBNET_PREFIX -f value -c id)" # NOTE(TheJulia): router must be attached to the subnet for RAs. openstack router add subnet $IRONIC_ROUTER_NAME $subnet_id # We're going to be using this router of public access to tenant networks PUBLIC_ROUTER_ID=$(openstack router show -c id -f value $IRONIC_ROUTER_NAME) fi die_if_not_set $LINENO subnet_id "Failure creating SUBNET_ID for $IRONIC_PROVISION_NETWORK_NAME" ironic_provision_network_ip=$IRONIC_PROVISION_SUBNET_GATEWAY else net_id=$(openstack network show $IRONIC_PROVISION_NETWORK_NAME -f value -c id) ironic_provision_network_ip=$IRONIC_PROVISION_SUBNET_SUBNODE_IP fi IRONIC_PROVISION_SEGMENTATION_ID=${IRONIC_PROVISION_SEGMENTATION_ID:-`openstack network show ${net_id} -f value -c provider:segmentation_id`} provision_net_prefix=${IRONIC_PROVISION_SUBNET_PREFIX##*/} # Set provision network GW on physical interface # Add vlan on br interface in case of IRONIC_PROVISION_PROVIDER_NETWORK_TYPE==vlan # othervise assign ip to br interface directly. sudo ip link set dev $OVS_PHYSICAL_BRIDGE up if [[ "$IRONIC_IP_VERSION" == "4" ]]; then if [[ "$IRONIC_PROVISION_PROVIDER_NETWORK_TYPE" == "vlan" ]]; then sudo ip link add link $OVS_PHYSICAL_BRIDGE name $OVS_PHYSICAL_BRIDGE.$IRONIC_PROVISION_SEGMENTATION_ID type vlan id $IRONIC_PROVISION_SEGMENTATION_ID sudo ip link set dev $OVS_PHYSICAL_BRIDGE.$IRONIC_PROVISION_SEGMENTATION_ID up sudo ip -$IRONIC_IP_VERSION addr add dev $OVS_PHYSICAL_BRIDGE.$IRONIC_PROVISION_SEGMENTATION_ID $ironic_provision_network_ip/$provision_net_prefix else sudo ip -$IRONIC_IP_VERSION addr add dev $OVS_PHYSICAL_BRIDGE $ironic_provision_network_ip/$provision_net_prefix fi else # Turn on the external/integration bridges, for IPV6. sudo ip link set dev br-ex up sudo ip link set dev br-int up sudo ip6tables -I FORWARD -i brbm -j LOG || true sudo ip6tables -I FORWARD -i br-ex -j LOG || true fi iniset $IRONIC_CONF_FILE neutron provisioning_network $IRONIC_PROVISION_NETWORK_NAME } function cleanup_ironic_provision_network { # Cleanup OVS_PHYSICAL_BRIDGE subinterfaces local bridge_subint bridge_subint=$(cat /proc/net/dev | sed -n "s/^\(${OVS_PHYSICAL_BRIDGE}\.[0-9]*\).*/\1/p") for sub_int in $bridge_subint; do sudo ip link set dev $sub_int down sudo ip link del dev $sub_int done } function configure_neutron_l3_lower_v6_ra { iniset $Q_L3_CONF_FILE DEFAULT min_rtr_adv_interval 5 } # configure_ironic() - Set config files, create data dirs, etc function configure_ironic { configure_ironic_dirs # (re)create ironic configuration file and configure common parameters. rm -f $IRONIC_CONF_FILE iniset $IRONIC_CONF_FILE DEFAULT debug True inicomment $IRONIC_CONF_FILE DEFAULT log_file iniset $IRONIC_CONF_FILE database connection `database_connection_url ironic` iniset $IRONIC_CONF_FILE DEFAULT state_path $IRONIC_STATE_PATH iniset $IRONIC_CONF_FILE DEFAULT use_syslog $SYSLOG # NOTE(vsaienko) with multinode each conductor should have its own host. iniset $IRONIC_CONF_FILE DEFAULT host $LOCAL_HOSTNAME # Retrieve deployment logs iniset $IRONIC_CONF_FILE agent deploy_logs_collect $IRONIC_DEPLOY_LOGS_COLLECT iniset $IRONIC_CONF_FILE agent deploy_logs_storage_backend $IRONIC_DEPLOY_LOGS_STORAGE_BACKEND iniset $IRONIC_CONF_FILE agent deploy_logs_local_path $IRONIC_DEPLOY_LOGS_LOCAL_PATH # Set image_download_source for direct interface iniset $IRONIC_CONF_FILE agent image_download_source $IRONIC_AGENT_IMAGE_DOWNLOAD_SOURCE # Configure JSON RPC backend iniset $IRONIC_CONF_FILE DEFAULT rpc_transport $IRONIC_RPC_TRANSPORT iniset $IRONIC_CONF_FILE json_rpc port $IRONIC_JSON_RPC_PORT # Set fast track options iniset $IRONIC_CONF_FILE deploy fast_track $IRONIC_DEPLOY_FAST_TRACK # Set requirement for agent tokens iniset $IRONIC_CONF_FILE DEFAULT require_agent_token $IRONIC_REQUIRE_AGENT_TOKEN # FIXME(dtantsur): configdrive downloading code does not respect IPA TLS # configuration, not even ipa-insecure. if is_service_enabled swift && ! is_service_enabled tls-proxy; then iniset $IRONIC_CONF_FILE deploy configdrive_use_object_store True fi # No need to check if RabbitMQ is enabled, this call does it in a smart way if [[ "$IRONIC_RPC_TRANSPORT" == "oslo" ]]; then iniset_rpc_backend ironic $IRONIC_CONF_FILE fi # Set IP version iniset $IRONIC_CONF_FILE pxe ip_version $IRONIC_IP_VERSION # Configure Ironic conductor, if it was enabled. if is_service_enabled ir-cond; then configure_ironic_conductor fi # Configure Ironic API, if it was enabled. if is_service_enabled ir-api; then configure_ironic_api fi # Format logging setup_logging $IRONIC_CONF_FILE # Adds ironic site for IPXE if [[ "$IRONIC_IPXE_ENABLED" == "True" ]]; then _config_ironic_apache_ipxe fi # Adds uWSGI for Ironic API if [[ "$IRONIC_USE_WSGI" == "True" ]]; then write_uwsgi_config "$IRONIC_UWSGI_CONF" "$IRONIC_UWSGI" "/baremetal" fi if [[ "$os_VENDOR" =~ (Debian|Ubuntu) ]]; then # The groups change with newer libvirt. Older Ubuntu used # 'libvirtd', but now uses libvirt like Debian. Do a quick check # to see if libvirtd group already exists to handle grenade's case. LIBVIRT_GROUP=$(cut -d ':' -f 1 /etc/group | grep 'libvirtd$' || true) LIBVIRT_GROUP=${LIBVIRT_GROUP:-libvirt} else LIBVIRT_GROUP=libvirtd fi if ! getent group $LIBVIRT_GROUP >/dev/null; then sudo groupadd $LIBVIRT_GROUP fi # NOTE(vsaienko) Add stack to libvirt group when installing without nova. if ! is_service_enabled nova; then # Disable power state change callbacks to nova. iniset $IRONIC_CONF_FILE nova send_power_notifications false add_user_to_group $STACK_USER $LIBVIRT_GROUP # This is the basic set of devices allowed / required by all virtual machines. # Add /dev/net/tun to cgroup_device_acl, needed for type=ethernet interfaces if ! sudo grep -q '^cgroup_device_acl' /etc/libvirt/qemu.conf; then cat <${IRONIC_VM_LOG_DIR}/README << EOF This directory contains the serial console log files from the virtual Ironic bare-metal nodes. The *_console_* log files are the original log files and include ANSI control codes which can make the output difficult to read. The *_no_ansi_* log files have had ANSI control codes removed from the file and are easier to read. On some occasions there won't be a corresponding *_no_ansi_* log file, for example if the job failed due to a time-out. You may see a log file without a date/time in the file name. In that case you can display the logfile in your console by doing: $ curl URL_TO_LOGFILE This will have your terminal process the ANSI escape codes. Another option, if you have the 'pv' executable installed, is to simulate a low-speed connection. In this example simulate a 300 Bytes/second connection. $ curl URL_TO_LOGFILE | pv -q -L 300 This can allow you to see some of the content before the screen is cleared by an ANSI escape sequence. EOF } function initialize_libvirt_storage_pool { [ -d $LIBVIRT_STORAGE_POOL_PATH ] || sudo mkdir -p $LIBVIRT_STORAGE_POOL_PATH if ! sudo virsh pool-list --all | grep -q $LIBVIRT_STORAGE_POOL; then sudo virsh pool-define-as --name $LIBVIRT_STORAGE_POOL dir \ --target $LIBVIRT_STORAGE_POOL_PATH >&2 sudo virsh pool-autostart $LIBVIRT_STORAGE_POOL >&2 sudo virsh pool-start $LIBVIRT_STORAGE_POOL >&2 fi pool_state=$(sudo virsh pool-info $LIBVIRT_STORAGE_POOL | grep State | awk '{ print $2 }') if [ "$pool_state" != "running" ] ; then sudo virsh pool-start $LIBVIRT_STORAGE_POOL >&2 fi } function create_bridge_and_vms { # Call libvirt setup scripts in a new shell to ensure any new group membership sudo su $STACK_USER -c "$IRONIC_SCRIPTS_DIR/setup-network.sh $IRONIC_VM_NETWORK_BRIDGE $PUBLIC_BRIDGE_MTU" if [[ "$IRONIC_VM_LOG_CONSOLE" == "True" ]] ; then local log_arg="-l $IRONIC_VM_LOG_DIR" if [[ "$IRONIC_VM_LOG_ROTATE" == "True" ]] ; then setup_qemu_log_hook fi else local log_arg="" fi local vbmc_port=$IRONIC_VBMC_PORT_RANGE_START local pdu_outlet=$IRONIC_VPDU_PORT_RANGE_START local vm_name local vm_opts="" if [[ -n "$IRONIC_VM_EMULATOR" ]]; then vm_opts+=" -e $IRONIC_VM_EMULATOR" fi vm_opts+=" -E $IRONIC_VM_ENGINE" if [[ "$IRONIC_BOOT_MODE" == "uefi" ]]; then vm_opts+=" -L $UEFI_LOADER_PATH -N $UEFI_NVRAM_PATH" fi if [[ -n "$LIBVIRT_NIC_DRIVER" ]]; then vm_opts+=" -D $LIBVIRT_NIC_DRIVER" elif [[ "$IRONIC_BOOT_MODE" == "uefi" ]]; then # Note(derekh) UEFI for the moment doesn't work with the e1000 net driver vm_opts+=" -D virtio" fi initialize_libvirt_storage_pool local bridge_mac bridge_mac=$(ip link show dev $IRONIC_VM_NETWORK_BRIDGE | grep -Eo "ether [A-Za-z0-9:]+"|sed "s/ether\ //") for vm_name in $(_ironic_bm_vm_names); do # pick up the $LIBVIRT_GROUP we have possibly joint newgrp $LIBVIRT_GROUP <> $IRONIC_VM_MACS_CSV_FILE SUBSHELL if is_deployed_by_ipmi; then vbmc --no-daemon add $vm_name --port $vbmc_port vbmc --no-daemon start $vm_name fi echo " ${bridge_mac} $IRONIC_VM_NETWORK_BRIDGE" >> $IRONIC_VM_MACS_CSV_FILE vbmc_port=$((vbmc_port+1)) pdu_outlet=$((pdu_outlet+1)) # It is sometimes useful to dump out the VM configuration to validate it. sudo virsh dumpxml $vm_name done if [[ -z "${IRONIC_PROVISION_NETWORK_NAME}" ]]; then local ironic_net_id ironic_net_id=$(openstack network show "$PRIVATE_NETWORK_NAME" -c id -f value) create_ovs_taps $ironic_net_id # NOTE(vsaienko) Neutron no longer setup routing to private network. # https://github.com/openstack-dev/devstack/commit/1493bdeba24674f6634160d51b8081c571df4017 # Add route here to have connection to VMs during provisioning. local pub_router_id local r_net_gateway local dns_server local replace_range if [[ "$IRONIC_IP_VERSION" == '4' ]]; then dns_server="8.8.8.8" if [[ -z "${SUBNETPOOL_V4_ID}" ]]; then replace_range=${FIXED_RANGE} else replace_range=${SUBNETPOOL_PREFIX_V4} fi else dns_server="2001:4860:4860::8888" if [[ -z "${SUBNETPOOL_V6_ID}" ]]; then replace_range=${FIXED_RANGE_V6} else replace_range=${SUBNETPOOL_PREFIX_V6} fi fi pub_router_id=$(openstack router show $Q_ROUTER_NAME -f value -c id) # Select the text starting at "src ", and grabbing the following field. r_net_gateway=$(sudo ip netns exec qrouter-$pub_router_id ip -$IRONIC_IP_VERSION route get $dns_server |grep dev | sed s/^.*src\ // |awk '{ print $1 }') sudo ip route replace $replace_range via $r_net_gateway fi # Here is a good place to restart tcpdump to begin capturing packets. # See: https://docs.openstack.org/devstack/latest/debugging.html # stop_tcpdump # start_tcpdump if [[ "$IRONIC_IP_VERSION" == "6" ]]; then # route us back through the neutron router! sudo ip -6 route add $IRONIC_PROVISION_SUBNET_PREFIX via $IPV6_ROUTER_GW_IP sudo ip link set dev br-ex up || true # Route back to our test subnet. Static should be safe for a while. sudo ip -6 route add fd00::/8 via $IPV6_ROUTER_GW_IP fi } function wait_for_nova_resources { # After nodes have been enrolled, we need to wait for both ironic and # nova's periodic tasks to populate the resource tracker with available # nodes and resources. Wait up to 2 minutes for a given resource before # timing out. local expected_count=$1 local resource_class=${IRONIC_DEFAULT_RESOURCE_CLASS^^} # TODO(dtantsur): switch to Placement OSC plugin, once it exists local token token=$(openstack token issue -f value -c id) local endpoint endpoint=$(openstack endpoint list --service placement --interface public -f value -c URL) die_if_not_set $LINENO endpoint "Cannot find Placement API endpoint" local i local count echo_summary "Waiting up to 3 minutes for placement to pick up $expected_count nodes" for i in $(seq 1 12); do # Fetch provider UUIDs from Placement local providers providers=$(curl -sH "X-Auth-Token: $token" $endpoint/resource_providers \ | jq -r '.resource_providers[].uuid') local p # Total count of the resource class, has to be equal to nodes count count=0 for p in $providers; do local amount # A resource class inventory record looks something like # {"max_unit": 1, "min_unit": 1, "step_size": 1, "reserved": 0, "total": 1, "allocation_ratio": 1} # Subtrack reserved from total (defaulting both to 0) amount=$(curl -sH "X-Auth-Token: $token" $endpoint/resource_providers/$p/inventories \ | jq ".inventories.CUSTOM_$resource_class as \$cls | (\$cls.total // 0) - (\$cls.reserved // 0)") # Check whether the resource provider has all expected traits # registered against it. rp_traits=$(curl -sH "X-Auth-Token: $token" \ -H "OpenStack-API-Version: placement 1.6" \ $endpoint/resource_providers/$p/traits) for trait in $IRONIC_DEFAULT_TRAITS; do if [[ $(echo "$rp_traits" | jq ".traits | contains([\"$trait\"])") == false ]]; then amount=0 fi done if [ $amount -gt 0 ]; then count=$(( count + $amount )) fi done if [ $count -ge $expected_count ]; then return 0 fi if is_service_enabled n-api; then $TOP_DIR/tools/discover_hosts.sh fi sleep 15 done die $LINENO "Timed out waiting for Nova to track $expected_count nodes" } function _clean_ncpu_failure { SCREEN_NAME=${SCREEN_NAME:-stack} SERVICE_DIR=${SERVICE_DIR:-${DEST}/status} n_cpu_failure="$SERVICE_DIR/$SCREEN_NAME/n-cpu.failure" if [ -f ${n_cpu_failure} ]; then mv ${n_cpu_failure} "${n_cpu_failure}.before-restart-by-ironic" fi } function provide_nodes { local nodes=$@ for node_id in $nodes; do $IRONIC_CMD node provide $node_id done local attempt for attempt in $(seq 1 $IRONIC_CLEANING_ATTEMPTS); do local available available=$(openstack baremetal node list --provision-state available -f value -c UUID) local nodes_not_finished= for node_id in $nodes; do if ! echo $available | grep -q $node_id; then nodes_not_finished+=" $node_id" fi done nodes=$nodes_not_finished if [[ "$nodes" == "" ]]; then break fi echo "Waiting for nodes to become available: $nodes" echo "Currently available: $available" sleep $IRONIC_CLEANING_DELAY done if [[ "$nodes" != "" ]]; then die $LINENO "Some nodes did not finish cleaning: $nodes" fi } function wait_for_ironic_neutron_agent_report_state_for_all_nodes { local nodes=$@ echo "Waiting for ironic-neutron-agent to report state for nodes: $nodes" local attempt for attempt in $(seq 1 $IRONIC_NEUTRON_AGENT_REPORT_STATE_ATTEMPTS); do local reported reported=$(openstack network agent list -f value -c Host -c Binary | grep ironic-neutron-agent | cut -d ' ' -f 1 | paste -s -d ' ') echo "Currently reported nodes: $reported" local can_break for node_id in $nodes; do if echo $reported | grep -q $node_id; then can_break="True" else can_break="False" break fi done if [[ $can_break == "True" ]]; then break fi sleep $IRONIC_NEUTRON_AGENT_REPORT_STATE_DELAY done if [[ "$can_break" == "False" ]]; then die $LINENO "ironic-neutron-agent did not report some nodes." fi } function enroll_nodes { local chassis_id chassis_id=$($IRONIC_CMD chassis create --description "ironic test chassis" -f value -c uuid) die_if_not_set $LINENO chassis_id "Failed to create chassis" local node_prefix node_prefix=$(get_ironic_node_prefix) local interface_info if [[ "$IRONIC_IS_HARDWARE" == "False" ]]; then local ironic_node_cpu=$IRONIC_VM_SPECS_CPU local ironic_node_ram=$IRONIC_VM_SPECS_RAM local ironic_node_disk=$IRONIC_VM_SPECS_DISK local ironic_ephemeral_disk=$IRONIC_VM_EPHEMERAL_DISK local ironic_node_arch=x86_64 local ironic_hwinfo_file=$IRONIC_VM_MACS_CSV_FILE if is_deployed_by_ipmi; then local node_options="\ --driver-info ipmi_address=${HOST_IP} \ --driver-info ipmi_username=admin \ --driver-info ipmi_password=password" elif is_deployed_by_snmp; then local node_options="\ --driver-info snmp_driver=${IRONIC_VPDU_SNMPDRIVER} \ --driver-info snmp_address=${HOST_IP} \ --driver-info snmp_port=${IRONIC_VPDU_LISTEN_PORT} \ --driver-info snmp_protocol=2c \ --driver-info snmp_community=${IRONIC_VPDU_COMMUNITY}" elif is_deployed_by_redfish; then local node_options="\ --driver-info redfish_address=http://${HOST_IP}:${IRONIC_REDFISH_EMULATOR_PORT} \ --driver-info redfish_username=admin \ --driver-info redfish_password=password" fi else local ironic_node_cpu=$IRONIC_HW_NODE_CPU local ironic_node_ram=$IRONIC_HW_NODE_RAM local ironic_node_disk=$IRONIC_HW_NODE_DISK local ironic_ephemeral_disk=$IRONIC_HW_EPHEMERAL_DISK local ironic_node_arch=$IRONIC_HW_ARCH local ironic_hwinfo_file=$IRONIC_HWINFO_FILE fi local total_nodes=0 local total_cpus=0 local node_uuids= local node_id while read hardware_info; do local node_name node_name=$node_prefix-$total_nodes local node_capabilities="" if [[ "$IRONIC_BOOT_MODE" == "uefi" ]]; then node_capabilities+=" --property capabilities=boot_mode:uefi" fi if [[ "$IRONIC_SECURE_BOOT" == "True" ]]; then if [[ -n "$node_capabilities" ]]; then node_capabilities+=",secure_boot:true" else node_capabilities+=" --property capabilities=secure_boot:true" fi fi if [[ "$IRONIC_IS_HARDWARE" == "False" ]]; then interface_info=$(echo $hardware_info | awk '{print $1}') if is_deployed_by_ipmi; then local vbmc_port vbmc_port=$(echo $hardware_info | awk '{print $2}') node_options+=" --driver-info ipmi_port=$vbmc_port" elif is_deployed_by_snmp; then local pdu_outlet pdu_outlet=$(echo $hardware_info | awk '{print $3}') node_options+=" --driver-info snmp_outlet=$pdu_outlet" elif is_deployed_by_redfish; then node_options+=" --driver-info redfish_system_id=/redfish/v1/Systems/$node_name" fi # Local-link-connection options local llc_opts="" if [[ "${IRONIC_USE_LINK_LOCAL}" == "True" ]]; then local switch_info local switch_id switch_id=$(echo $hardware_info |awk '{print $4}') switch_info=$(echo $hardware_info |awk '{print $5}') # NOTE(vsaienko) we will add port_id later in the code. llc_opts="--local-link-connection switch_id=${switch_id} \ --local-link-connection switch_info=${switch_info} " fi if [[ "${IRONIC_STORAGE_INTERFACE}" == "cinder" ]]; then local connector_iqn="iqn.2017-05.org.openstack.$node_prefix-$total_nodes" if [[ -n "$node_capabilities" ]]; then node_capabilities+=",iscsi_boot:True" else node_capabilities+=" --property capabilities=iscsi_boot:True" fi fi else # Currently we require all hardware platform have same CPU/RAM/DISK info # in future, this can be enhanced to support different type, and then # we create the bare metal flavor with minimum value local bmc_address bmc_address=$(echo $hardware_info |awk '{print $1}') local mac_address mac_address=$(echo $hardware_info |awk '{print $2}') local bmc_username bmc_username=$(echo $hardware_info |awk '{print $3}') local bmc_passwd bmc_passwd=$(echo $hardware_info |awk '{print $4}') local node_options="" if is_deployed_by_ipmi; then node_options+=" --driver-info ipmi_address=$bmc_address \ --driver-info ipmi_password=$bmc_passwd \ --driver-info ipmi_username=$bmc_username" elif is_deployed_by_ilo; then node_options+=" --driver-info ilo_address=$bmc_address \ --driver-info ilo_password=$bmc_passwd \ --driver-info ilo_username=$bmc_username" if [[ $IRONIC_ENABLED_BOOT_INTERFACES == *"ilo-virtual-media"* ]]; then node_options+=" --driver-info ilo_deploy_iso=$IRONIC_DEPLOY_ISO_ID" fi elif is_deployed_by_drac; then node_options+=" --driver-info drac_address=$bmc_address \ --driver-info drac_password=$bmc_passwd \ --driver-info drac_username=$bmc_username" elif is_deployed_by_redfish; then local bmc_redfish_system_id bmc_redfish_system_id=$(echo $hardware_info |awk '{print $5}') node_options+=" --driver-info redfish_address=https://$bmc_address \ --driver-info redfish_system_id=$bmc_redfish_system_id \ --driver-info redfish_password=$bmc_passwd \ --driver-info redfish_username=$bmc_username \ --driver-info redfish_verify_ca=False" elif is_deployed_by_irmc; then node_options+=" --driver-info irmc_address=$bmc_address \ --driver-info irmc_password=$bmc_passwd \ --driver-info irmc_username=$bmc_username" if [[ -n "$IRONIC_DEPLOY_ISO_ID" ]]; then node_options+=" --driver-info irmc_deploy_iso=$IRONIC_DEPLOY_ISO_ID" fi elif is_deployed_by_xclarity; then local xclarity_hardware_id xclarity_hardware_id=$(echo $hardware_info |awk '{print $5}') node_options+=" --driver-info xclarity_manager_ip=$bmc_address \ --driver-info xclarity_password=$bmc_passwd \ --driver-info xclarity_username=$bmc_username \ --driver-info xclarity_hardware_id=$xclarity_hardware_id" fi interface_info="${mac_address}" fi # First node created will be used for testing in ironic w/o glance # scenario, so we need to know its UUID. local standalone_node_uuid="" if [ $total_nodes -eq 0 ]; then standalone_node_uuid="--uuid $IRONIC_NODE_UUID" fi # TODO(dtantsur): it would be cool to test with different resource # classes, but for now just use the same. node_id=$($IRONIC_CMD node create $standalone_node_uuid \ --chassis $chassis_id \ --driver $IRONIC_DEPLOY_DRIVER \ --name $node_name \ --resource-class $IRONIC_DEFAULT_RESOURCE_CLASS \ --property cpu_arch=$ironic_node_arch \ $node_capabilities \ $node_options \ -f value -c uuid) die_if_not_set $LINENO node_id "Failed to create node" node_uuids+=" $node_id" if [[ -n $IRONIC_DEFAULT_TRAITS ]]; then $IRONIC_CMD node add trait $node_id $IRONIC_DEFAULT_TRAITS fi $IRONIC_CMD node manage $node_id --wait $IRONIC_MANAGE_TIMEOUT || \ die $LINENO "Node did not reach manageable state in $IRONIC_MANAGE_TIMEOUT seconds" # NOTE(vsaienko) IPA didn't automatically recognize root devices less than 4Gb. # Setting root hint allows to install OS on such devices. # 0x1af4 is VirtIO vendor device ID. if [[ "$ironic_node_disk" -lt "4" && is_deployed_by_agent ]]; then $IRONIC_CMD node set $node_id --property \ root_device='{"vendor": "0x1af4"}' fi # In case we using portgroups, we should API version that support them. # Othervise API will return 406 ERROR # NOTE(vsaienko) interface_info is in the following format here: # mac1,tap-node0i1;mac2,tap-node0i2;...;macN,tap-node0iN for info in ${interface_info//;/ }; do local mac_address="" local port_id="" local llc_port_opt="" local physical_network="" mac_address=$(echo $info| awk -F ',' '{print $1}') port_id=$(echo $info| awk -F ',' '{print $2}') if [[ "${IRONIC_USE_LINK_LOCAL}" == "True" ]]; then llc_port_opt+=" --local-link-connection port_id=${port_id} " fi if [[ "${IRONIC_USE_NEUTRON_SEGMENTS}" == "True" ]]; then physical_network=" --physical-network ${PHYSICAL_NETWORK} " fi $IRONIC_CMD port create --node $node_id $llc_opts $llc_port_opt $mac_address $physical_network done # NOTE(vsaienko) use node-update instead of specifying network_interface # during node creation. If node is added with latest version of API it # will NOT go to available state automatically. if [[ -n "${IRONIC_NETWORK_INTERFACE}" ]]; then $IRONIC_CMD node set $node_id --network-interface $IRONIC_NETWORK_INTERFACE || \ die $LINENO "Failed to update network interface for node" fi if [[ -n "${IRONIC_STORAGE_INTERFACE}" ]]; then $IRONIC_CMD node set $node_id --storage-interface $IRONIC_STORAGE_INTERFACE || \ die $LINENO "Failed to update storage interface for node $node_id" if [[ -n "${connector_iqn}" ]]; then $IRONIC_CMD volume connector create --node $node_id --type iqn \ --connector-id $connector_iqn || \ die $LINENO "Failed to create volume connector for node $node_id" fi fi total_nodes=$((total_nodes+1)) done < $ironic_hwinfo_file # NOTE(hjensas): ensure ironic-neutron-agent has done report_state for all # nodes we attempt cleaning. if [[ "${IRONIC_USE_NEUTRON_SEGMENTS}" == "True" ]]; then wait_for_ironic_neutron_agent_report_state_for_all_nodes $node_uuids fi # NOTE(dtantsur): doing it outside of the loop, because of cleaning provide_nodes $node_uuids if is_service_enabled nova && [[ "$VIRT_DRIVER" == "ironic" ]]; then if [[ "$HOST_TOPOLOGY_ROLE" != "subnode" ]]; then local adjusted_disk adjusted_disk=$(($ironic_node_disk - $ironic_ephemeral_disk)) openstack flavor create --ephemeral $ironic_ephemeral_disk --ram $ironic_node_ram --disk $adjusted_disk --vcpus $ironic_node_cpu baremetal local resource_class=${IRONIC_DEFAULT_RESOURCE_CLASS^^} openstack flavor set baremetal --property "resources:CUSTOM_$resource_class"="1" openstack flavor set baremetal --property "resources:DISK_GB"="0" openstack flavor set baremetal --property "resources:MEMORY_MB"="0" openstack flavor set baremetal --property "resources:VCPU"="0" openstack flavor set baremetal --property "cpu_arch"="$ironic_node_arch" if [[ "$IRONIC_BOOT_MODE" == "uefi" ]]; then openstack flavor set baremetal --property "capabilities:boot_mode"="uefi" fi for trait in $IRONIC_DEFAULT_TRAITS; do openstack flavor set baremetal --property "trait:$trait"="required" done if [[ "$IRONIC_SECURE_BOOT" == "True" ]]; then openstack flavor set baremetal --property "capabilities:secure_boot"="true" fi # NOTE(dtantsur): sometimes nova compute fails to start with ironic due # to keystone restarting and not being able to authenticate us. # Restart it just to be sure (and avoid gate problems like bug 1537076) stop_nova_compute || /bin/true # NOTE(pas-ha) if nova compute failed before restart, .failure file # that was created will fail the service_check in the end of the deployment _clean_ncpu_failure start_nova_compute else # NOTE(vsaienko) we enrolling IRONIC_VM_COUNT on each node. So on subnode # we expect to have 2 x total_cpus total_nodes=$(( total_nodes * 2 )) fi wait_for_nova_resources $total_nodes fi } function die_if_module_not_loaded { if ! grep -q $1 /proc/modules; then die $LINENO "$1 kernel module is not loaded" fi } function configure_iptables { # enable tftp natting for allowing connections to HOST_IP's tftp server if ! running_in_container; then sudo modprobe nf_conntrack_tftp sudo modprobe nf_nat_tftp else die_if_module_not_loaded nf_conntrack_tftp die_if_module_not_loaded nf_nat_tftp fi ################ NETWORK DHCP # explicitly allow DHCP - packets are occasionally being dropped here sudo iptables -I INPUT -p udp --dport 67:68 --sport 67:68 -j ACCEPT || true # nodes boot from TFTP and callback to the API server listening on $HOST_IP sudo iptables -I INPUT -d $IRONIC_TFTPSERVER_IP -p udp --dport 69 -j ACCEPT || true # dhcpv6 which is the only way to transmit boot options sudo ip6tables -I INPUT -d $IRONIC_HOST_IPV6 -p udp --dport 546:547 --sport 546:547 -j ACCEPT || true sudo ip6tables -I INPUT -d $IRONIC_HOST_IPV6 -p udp --dport 69 -j ACCEPT || true ################ Webserver/API # To use named /baremetal endpoint we should open default apache port if [[ "$IRONIC_USE_WSGI" == "False" ]]; then sudo iptables -I INPUT -d $HOST_IP -p tcp --dport $IRONIC_SERVICE_PORT -j ACCEPT || true sudo ip6tables -I INPUT -d $HOST_IP -p tcp --dport $IRONIC_SERVICE_PORT -j ACCEPT || true # open ironic API on baremetal network sudo iptables -I INPUT -d $IRONIC_HTTP_SERVER -p tcp --dport $IRONIC_SERVICE_PORT -j ACCEPT || true # allow IPA to connect to ironic API on subnode sudo iptables -I FORWARD -p tcp --dport $IRONIC_SERVICE_PORT -j ACCEPT || true else sudo iptables -I INPUT -d $HOST_IP -p tcp --dport 80 -j ACCEPT || true sudo iptables -I INPUT -d $HOST_IP -p tcp --dport 443 -j ACCEPT || true # open ironic API on baremetal network sudo iptables -I INPUT -d $IRONIC_HTTP_SERVER -p tcp --dport 80 -j ACCEPT || true sudo ip6tables -I INPUT -d $IRONIC_HTTP_SERVER -p tcp --dport 80 -j ACCEPT || true sudo iptables -I INPUT -d $IRONIC_HTTP_SERVER -p tcp --dport 443 -j ACCEPT || true sudo ip6tables -I INPUT -d $IRONIC_HTTP_SERVER -p tcp --dport 443 -j ACCEPT || true fi if is_deployed_by_agent; then # agent ramdisk gets instance image from swift sudo iptables -I INPUT -d $HOST_IP -p tcp --dport ${SWIFT_DEFAULT_BIND_PORT:-8080} -j ACCEPT || true sudo iptables -I INPUT -d $HOST_IP -p tcp --dport $GLANCE_SERVICE_PORT -j ACCEPT || true fi if [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then sudo iptables -I INPUT -d $IRONIC_HTTP_SERVER -p tcp --dport $IRONIC_HTTP_PORT -j ACCEPT || true sudo ip6tables -I INPUT -d $IRONIC_HOST_IPV6 -p tcp --dport $IRONIC_HTTP_PORT -j ACCEPT || true fi if [[ "${IRONIC_STORAGE_INTERFACE}" == "cinder" ]]; then sudo iptables -I INPUT -d $HOST_IP -p tcp --dport $ISCSI_SERVICE_PORT -s $FLOATING_RANGE -j ACCEPT || true fi # (rpittau) workaround to allow TFTP traffic on ubuntu bionic with conntrack helper disabled local qrouter qrouter=$(sudo ip netns list | grep qrouter | awk '{print $1;}') if [[ ! -z "$qrouter" ]]; then sudo ip netns exec $qrouter /sbin/iptables -A PREROUTING -t raw -p udp --dport 69 -j CT --helper tftp sudo ip netns exec $qrouter /sbin/ip6tables -A PREROUTING -t raw -p udp --dport 69 -j CT --helper tftp || true fi } function configure_tftpd { # stop tftpd and setup serving via xinetd stop_service tftpd-hpa || true [ -f /etc/init/tftpd-hpa.conf ] && echo "manual" | sudo tee /etc/init/tftpd-hpa.override sudo cp $IRONIC_TEMPLATES_DIR/tftpd-xinetd.template /etc/xinetd.d/tftp sudo sed -e "s|%TFTPBOOT_DIR%|$IRONIC_TFTPBOOT_DIR|g" -i /etc/xinetd.d/tftp sudo sed -e "s|%MAX_BLOCKSIZE%|$IRONIC_TFTP_BLOCKSIZE|g" -i /etc/xinetd.d/tftp if [[ "$IRONIC_IP_VERSION" == '6' ]]; then sudo sed -e "s|IPv4|IPv6|g" -i /etc/xinetd.d/tftp fi # setup tftp file mapping to satisfy requests at the root (booting) and # /tftpboot/ sub-dir (as per deploy-ironic elements) # this section is only for ubuntu and fedora if [[ "$IRONIC_IPXE_ENABLED" == "False" && \ ( "$IRONIC_BOOT_MODE" == "uefi" || "$IRONIC_SECURE_BOOT" == "True" ) && \ "$IRONIC_UEFI_BOOT_LOADER" == "grub2" ]]; then local grub_dir echo "re ^($IRONIC_TFTPBOOT_DIR/) $IRONIC_TFTPBOOT_DIR/\2" >$IRONIC_TFTPBOOT_DIR/map-file echo "re ^$IRONIC_TFTPBOOT_DIR/ $IRONIC_TFTPBOOT_DIR/" >>$IRONIC_TFTPBOOT_DIR/map-file echo "re ^(^/) $IRONIC_TFTPBOOT_DIR/\1" >>$IRONIC_TFTPBOOT_DIR/map-file echo "re ^([^/]) $IRONIC_TFTPBOOT_DIR/\1" >>$IRONIC_TFTPBOOT_DIR/map-file sudo cp $IRONIC_GRUB2_SHIM_FILE $IRONIC_TFTPBOOT_DIR/bootx64.efi if is_fedora; then grub_subdir="EFI/fedora" elif is_ubuntu; then grub_subdir="boot/grub" fi grub_dir=$IRONIC_TFTPBOOT_DIR/$grub_subdir mkdir -p $grub_dir # Grub looks for numerous files when the grubnetx.efi binary is used :\ # specifically .lst files which define module lists which we can't seem # to find on disk. That being said, the grub-mknetdir utility generates # these files for us. grub-mknetdir --net-directory="$IRONIC_TFTPBOOT_DIR" --subdir="$grub_subdir" sudo cp $grub_dir/x86_64-efi/core.efi $IRONIC_TFTPBOOT_DIR/grubx64.efi cat << EOF > $grub_dir/grub.cfg set default=master set timeout=1 set hidden_timeout_quiet=false menuentry "master" { configfile $IRONIC_TFTPBOOT_DIR/\$net_default_mac.conf } EOF chmod 644 $grub_dir/grub.cfg iniset $IRONIC_CONF_FILE pxe uefi_pxe_config_template '$pybasedir/drivers/modules/pxe_grub_config.template' iniset $IRONIC_CONF_FILE pxe uefi_pxe_bootfile_name "bootx64.efi" else echo "r ^([^/]) $IRONIC_TFTPBOOT_DIR/\1" >$IRONIC_TFTPBOOT_DIR/map-file echo "r ^(/tftpboot/) $IRONIC_TFTPBOOT_DIR/\2" >>$IRONIC_TFTPBOOT_DIR/map-file fi sudo chmod -R 0755 $IRONIC_TFTPBOOT_DIR restart_service xinetd } function build_ipa_ramdisk { local kernel_path=$1 local ramdisk_path=$2 local iso_path=$3 case $IRONIC_RAMDISK_TYPE in 'tinyipa') build_tinyipa_ramdisk $kernel_path $ramdisk_path $iso_path ;; 'dib') build_ipa_dib_ramdisk $kernel_path $ramdisk_path $iso_path ;; *) die $LINENO "Unrecognised IRONIC_RAMDISK_TYPE: $IRONIC_RAMDISK_TYPE. Expected either of 'dib' or 'tinyipa'." ;; esac } function setup_ipa_builder { git_clone $IRONIC_PYTHON_AGENT_BUILDER_REPO $IRONIC_PYTHON_AGENT_BUILDER_DIR $IRONIC_PYTHON_AGENT_BUILDER_BRANCH } function build_tinyipa_ramdisk { echo "Building ironic-python-agent deploy ramdisk" local kernel_path=$1 local ramdisk_path=$2 local iso_path=$3 cd $IRONIC_PYTHON_AGENT_BUILDER_DIR/tinyipa export BUILD_AND_INSTALL_TINYIPA=true if is_ansible_deploy_enabled; then export AUTHORIZE_SSH=true export SSH_PUBLIC_KEY=$IRONIC_ANSIBLE_SSH_KEY.pub fi make cp tinyipa.gz $ramdisk_path cp tinyipa.vmlinuz $kernel_path if is_deploy_iso_required; then make iso cp tinyipa.iso $iso_path fi make clean cd - } function rebuild_tinyipa_for_ansible { local ansible_tinyipa_ramdisk_name pushd $IRONIC_PYTHON_AGENT_BUILDER_DIR/tinyipa export TINYIPA_RAMDISK_FILE=$IRONIC_DEPLOY_RAMDISK export SSH_PUBLIC_KEY=$IRONIC_ANSIBLE_SSH_KEY.pub make addssh ansible_tinyipa_ramdisk_name="ansible-$(basename $IRONIC_DEPLOY_RAMDISK)" mv $ansible_tinyipa_ramdisk_name $TOP_DIR/files make clean popd IRONIC_DEPLOY_RAMDISK=$TOP_DIR/files/$ansible_tinyipa_ramdisk_name } # install_diskimage_builder() - Collect source and prepare or install from pip function install_diskimage_builder { if use_library_from_git "diskimage-builder"; then git_clone_by_name "diskimage-builder" setup_dev_lib -bindep "diskimage-builder" else local bindep_file bindep_file=$(mktemp) curl -o "$bindep_file" "$IRONIC_DIB_BINDEP_FILE" install_bindep "$bindep_file" pip_install_gr "diskimage-builder" fi } function build_ipa_dib_ramdisk { local kernel_path=$1 local ramdisk_path=$2 local iso_path=$3 local tempdir tempdir=$(mktemp -d --tmpdir=${DEST}) # install diskimage-builder if not present if ! $(type -P disk-image-create > /dev/null); then install_diskimage_builder fi echo "Building IPA ramdisk with DIB options: $IRONIC_DIB_RAMDISK_OPTIONS" if is_deploy_iso_required; then IRONIC_DIB_RAMDISK_OPTIONS+=" iso" fi git_clone $IRONIC_PYTHON_AGENT_BUILDER_REPO $IRONIC_PYTHON_AGENT_BUILDER_DIR $IRONIC_PYTHON_AGENT_BUILDER_BRANCH ELEMENTS_PATH="$IRONIC_PYTHON_AGENT_BUILDER_DIR/dib" \ DIB_DHCP_TIMEOUT=$IRONIC_DIB_DHCP_TIMEOUT \ DIB_RELEASE=$IRONIC_DIB_RAMDISK_RELEASE \ DIB_REPOLOCATION_ironic_python_agent="$IRONIC_PYTHON_AGENT_DIR" \ DIB_REPOLOCATION_requirements="$DEST/requirements" \ disk-image-create "$IRONIC_DIB_RAMDISK_OPTIONS" \ -x -o "$tempdir/ironic-agent" \ ironic-python-agent-ramdisk chmod -R +r $tempdir mv "$tempdir/ironic-agent.kernel" "$kernel_path" mv "$tempdir/ironic-agent.initramfs" "$ramdisk_path" if is_deploy_iso_required; then mv "$tempdir/ironic-agent.iso" "$iso_path" fi rm -rf $tempdir } # download EFI boot loader image and upload it to glance # this function sets ``IRONIC_EFIBOOT_ID`` function upload_baremetal_ironic_efiboot { declare -g IRONIC_EFIBOOT_ID local efiboot_name efiboot_name=$(basename $IRONIC_EFIBOOT) echo_summary "Building and uploading EFI boot image for ironic" if [ ! -e "$IRONIC_EFIBOOT" ]; then local efiboot_path efiboot_path=$(mktemp -d --tmpdir=${DEST})/$efiboot_name local efiboot_mount efiboot_mount=$(mktemp -d --tmpdir=${DEST}) dd if=/dev/zero \ of=$efiboot_path \ bs=4096 count=1024 mkfs.fat -s 4 -r 512 -S 4096 $efiboot_path sudo mount $efiboot_path $efiboot_mount sudo mkdir -p $efiboot_mount/efi/boot sudo grub-mkimage \ -C xz \ -O x86_64-efi \ -p /boot/grub \ -o $efiboot_mount/efi/boot/bootx64.efi \ boot linux linuxefi search normal configfile \ part_gpt btrfs ext2 fat iso9660 loopback \ test keystatus gfxmenu regexp probe \ efi_gop efi_uga all_video gfxterm font \ echo read ls cat png jpeg halt reboot sudo umount $efiboot_mount mv $efiboot_path $IRONIC_EFIBOOT fi # load efiboot into glance IRONIC_EFIBOOT_ID=$(openstack \ image create \ $efiboot_name \ --public --disk-format=raw \ --container-format=bare \ -f value -c id \ < $IRONIC_EFIBOOT) die_if_not_set $LINENO IRONIC_EFIBOOT_ID "Failed to load EFI bootloader image into glance" iniset $IRONIC_CONF_FILE conductor bootloader $IRONIC_EFIBOOT_ID } # build deploy kernel+ramdisk, then upload them to glance # this function sets ``IRONIC_DEPLOY_KERNEL_ID``, ``IRONIC_DEPLOY_RAMDISK_ID`` function upload_baremetal_ironic_deploy { declare -g IRONIC_DEPLOY_KERNEL_ID IRONIC_DEPLOY_RAMDISK_ID local ironic_deploy_kernel_name local ironic_deploy_ramdisk_name ironic_deploy_kernel_name=$(basename $IRONIC_DEPLOY_KERNEL) ironic_deploy_ramdisk_name=$(basename $IRONIC_DEPLOY_RAMDISK) if [[ "$HOST_TOPOLOGY_ROLE" != "subnode" ]]; then echo_summary "Creating and uploading baremetal images for ironic" if [ ! -e "$IRONIC_DEPLOY_RAMDISK" ] || \ [ ! -e "$IRONIC_DEPLOY_KERNEL" ] || \ ( is_deploy_iso_required && [ ! -e "$IRONIC_DEPLOY_ISO" ] ); then # setup IRONIC_PYTHON_AGENT_BUILDER_DIR setup_ipa_builder # files don't exist, need to build them if [ "$IRONIC_BUILD_DEPLOY_RAMDISK" = "True" ]; then # we can build them only if we're not offline if [ "$OFFLINE" != "True" ]; then build_ipa_ramdisk $IRONIC_DEPLOY_KERNEL $IRONIC_DEPLOY_RAMDISK $IRONIC_DEPLOY_ISO else die $LINENO "Deploy kernel+ramdisk or iso files don't exist and cannot be built in OFFLINE mode" fi else # Grab the agent image tarball, either from a local file or remote URL if [[ "$IRONIC_AGENT_KERNEL_URL" =~ "file://" ]]; then cp ${IRONIC_AGENT_KERNEL_URL:7} $IRONIC_DEPLOY_KERNEL else wget "$IRONIC_AGENT_KERNEL_URL" -O $IRONIC_DEPLOY_KERNEL fi if [[ "$IRONIC_AGENT_RAMDISK_URL" =~ "file://" ]]; then cp ${IRONIC_AGENT_RAMDISK_URL:7} $IRONIC_DEPLOY_RAMDISK else wget "$IRONIC_AGENT_RAMDISK_URL" -O $IRONIC_DEPLOY_RAMDISK fi if is_ansible_with_tinyipa; then # NOTE(pas-ha) if using ansible-deploy and tinyipa, # this will rebuild ramdisk and override $IRONIC_DEPLOY_RAMDISK rebuild_tinyipa_for_ansible fi fi fi # load them into glance if ! is_deploy_iso_required; then IRONIC_DEPLOY_KERNEL_ID=$(openstack \ image create \ $ironic_deploy_kernel_name \ --public --disk-format=aki \ --container-format=aki \ < $IRONIC_DEPLOY_KERNEL | grep ' id ' | get_field 2) die_if_not_set $LINENO IRONIC_DEPLOY_KERNEL_ID "Failed to load kernel image into glance" IRONIC_DEPLOY_RAMDISK_ID=$(openstack \ image create \ $ironic_deploy_ramdisk_name \ --public --disk-format=ari \ --container-format=ari \ < $IRONIC_DEPLOY_RAMDISK | grep ' id ' | get_field 2) die_if_not_set $LINENO IRONIC_DEPLOY_RAMDISK_ID "Failed to load ramdisk image into glance" else IRONIC_DEPLOY_ISO_ID=$(openstack \ image create \ $(basename $IRONIC_DEPLOY_ISO) \ --public --disk-format=iso \ --container-format=bare \ < $IRONIC_DEPLOY_ISO -f value -c id) die_if_not_set $LINENO IRONIC_DEPLOY_ISO_ID "Failed to load deploy iso into glance" fi else if is_ansible_with_tinyipa; then ironic_deploy_ramdisk_name="ansible-$ironic_deploy_ramdisk_name" fi IRONIC_DEPLOY_KERNEL_ID=$(openstack image show $ironic_deploy_kernel_name -f value -c id) IRONIC_DEPLOY_RAMDISK_ID=$(openstack image show $ironic_deploy_ramdisk_name -f value -c id) fi iniset $IRONIC_CONF_FILE conductor deploy_kernel $IRONIC_DEPLOY_KERNEL_ID iniset $IRONIC_CONF_FILE conductor deploy_ramdisk $IRONIC_DEPLOY_RAMDISK_ID iniset $IRONIC_CONF_FILE conductor rescue_kernel $IRONIC_DEPLOY_KERNEL_ID iniset $IRONIC_CONF_FILE conductor rescue_ramdisk $IRONIC_DEPLOY_RAMDISK_ID } function prepare_baremetal_basic_ops { if [[ "$IRONIC_BAREMETAL_BASIC_OPS" != "True" ]]; then return 0 fi if ! is_service_enabled nova && [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then local image_file_path if [[ ${IRONIC_WHOLEDISK_IMAGE_NAME} =~ \.img$ ]]; then image_file_path=$FILES/${IRONIC_WHOLEDISK_IMAGE_NAME} else image_file_path=$FILES/${IRONIC_WHOLEDISK_IMAGE_NAME}.img fi sudo install -g $LIBVIRT_GROUP -o $STACK_USER -m 644 $image_file_path $IRONIC_HTTP_DIR fi upload_baremetal_ironic_deploy if [[ "$IRONIC_BOOT_MODE" == "uefi" && is_deployed_by_redfish ]]; then upload_baremetal_ironic_efiboot fi configure_tftpd configure_iptables } function cleanup_baremetal_basic_ops { if [[ "$IRONIC_BAREMETAL_BASIC_OPS" != "True" ]]; then return 0 fi rm -f $IRONIC_VM_MACS_CSV_FILE sudo rm -rf $IRONIC_DATA_DIR $IRONIC_STATE_PATH local vm_name for vm_name in $(_ironic_bm_vm_names); do # Delete the Virtual BMCs if is_deployed_by_ipmi; then vbmc --no-daemon list | grep -a $vm_name && vbmc --no-daemon delete $vm_name || /bin/true fi # pick up the $LIBVIRT_GROUP we have possibly joint newgrp $LIBVIRT_GROUP <