diff --git a/doc/source/image/rhel7/rhel7_image.rst b/doc/source/image/rhel7/rhel7_image.rst index 5969e52..b0c1c95 100644 --- a/doc/source/image/rhel7/rhel7_image.rst +++ b/doc/source/image/rhel7/rhel7_image.rst @@ -196,6 +196,8 @@ Add DPM-Guest Tools cp nova-dpm/dpm_guest_tools/usr/lib/systemd/system/autoconfigure_networking.service /usr/lib/systemd/system/autoconfigure_networking.service + cp nova-dpm/dpm_guest_tools/usr/bin/dpm_guest_image_tools_common /usr/bin/ + cp nova-dpm/dpm_guest_tools/usr/bin/setmac.sh /usr/bin/setmac.sh cp nova-dpm/dpm_guest_tools/etc/udev/rules.d/80-setmac.rules /etc/udev/rules.d/80-setmac.rules diff --git a/guest_image_tools/usr/bin/autoconfigure_networking.sh b/guest_image_tools/usr/bin/autoconfigure_networking.sh index f33f676..14fa59b 100755 --- a/guest_image_tools/usr/bin/autoconfigure_networking.sh +++ b/guest_image_tools/usr/bin/autoconfigure_networking.sh @@ -23,10 +23,14 @@ # Exit on error set -e +source $(dirname "$0")/dpm_guest_image_tools_common LOG_PREFIX=$(basename "$0") -REGEX_DEV_NO="[0-9A-Fa-f]{4}" -REGEX_MAC="[0-9A-Fa-f]{12}" +# This script usually gets called by systemd. Systemd takes care of writing +# stdout and stderr into the journal. Using "echo" here ensures, that +# all the messages show up under the corresponding systemd unit and not as +# separate entity. +LOG_TARGET="stdout" # Regex to match # ,; @@ -34,46 +38,7 @@ REGEX_MAC="[0-9A-Fa-f]{12}" REGEX="($REGEX_DEV_NO),([0-1])(,$REGEX_MAC)?;" #CMDLINE="some stuff 0001,1,000000000011;0004,;0007,0; more stuff" -CMDLINE=$(cat /proc/cmdline) - -function log { - # $1 = message to log - # This script usually gets called by systemd. Systemd takes care of writing - # stdout and stderr into the journal. Using "echo" here ensures, that - # all the messages show up under the corresponding systemd unit. - echo "$LOG_PREFIX: $1" -} - -function get_device_bus_id { - # $1 = the device number - # returns the corresponding device_bus_id - - echo "0.0.$1" -} - -function device_exists { - # $1 = the device bus id - local dev_bus_id="$1" - - # Check if device is already configured - path="/sys/bus/ccwgroup/devices/$dev_bus_id" - if ! [ -d "$path" ]; then - return 1 - fi -} - -function configure_device { - # $1 = the device bus id - # $2 = the port no - local dev_bus_id="$1" - local port_no="$2" - - # TODO(andreas_s): Do not depend on znetconf - # Errors of the following command are written to stderr, and therefore - # show up in the systemd units journal - znetconf -a $dev_bus_id -o portno=$port_no,layer2=1 - return "$?" -} +CMDLINE=$(get_cmdline) log "Start" diff --git a/guest_image_tools/usr/bin/dpm_guest_image_tools_common b/guest_image_tools/usr/bin/dpm_guest_image_tools_common new file mode 100644 index 0000000..83e41f1 --- /dev/null +++ b/guest_image_tools/usr/bin/dpm_guest_image_tools_common @@ -0,0 +1,205 @@ +#! /bin/bash +# Copyright 2017 IBM Corp. All Rights Reserved. +# +# 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. + +# Allow caller to define a different folder for the root directory +ROOT_DIR=${ROOT_DIR:-} +# Allow caller to define a different LOG prefix +LOG_PREFIX=${LOG_PREFIX:-"DPM-guest-image-tools"} +# Allow caller to define a different LOG target +LOG_TARGET=${LOG_TARGET:-"syslog"} + +# Matches MAC in format: xxxxxxxxxxxx +REGEX_MAC="[0-9A-Fa-f]{12}" +REGEX_DEV_NO="[0-9A-Fa-f]{4}" +REGEX_EXTRACT_DEVNO="qeth/0\.0\.($REGEX_DEV_NO)/net" +REGEX_EXTRACT_IFNAME="/net/(.{1,15})" + +function log { + # $1 = message to log + + local log_msg="$LOG_PREFIX: $1" + + if [[ $LOG_TARGET == "syslog" ]]; then + logger $log_msg + elif [[ $LOG_TARGET == "stdout" ]]; then + echo $log_msg + else + # Logging disabled + : + fi +} + +function extract_devno { + # Extracts the device number out of a device path + # $1 = the device path, e.g. "/devices/qeth/0.0.0001/net/enc1" + # Returns: device number, e.g. "0001" + + local dev_path="$1" + if [[ $dev_path =~ $REGEX_EXTRACT_DEVNO ]]; then + echo "${BASH_REMATCH[1]}" + else + log "Could not extract devno from '$dev_path'. Skipping!" + exit 1 + fi +} + +function extract_interface_name { + # Extracts the interface name out of a device path + # $1 = the device path, e.g. "/devices/qeth/0.0.0001/net/enc1" + # Returns: interface name, e.g. "enc1" + + local dev_path="$1" + if [[ $dev_path =~ $REGEX_EXTRACT_IFNAME ]]; then + echo "${BASH_REMATCH[1]}" + else + log "Could not find interface for device number '$devno' in path 'dev_path'. Skipping!" + exit 1 + fi +} + + +function extract_mac { + # Get the mac address to a given device number from the cmdline + # $1 = the device number, e.g. "0001" + # $2 = the cmdline, e.g. "0001,0,aabbccddeeff;" + # Returns: The MAC address in format xx:xx:xx:xx:xx:xx + + local devno="$1" + local cmdline="$2" + # Regex matches: ,,; + regex=${devno}",[0-1],("${REGEX_MAC}");" + if [[ $cmdline =~ $regex ]]; then + local mac_tmp="${BASH_REMATCH[1]}" + # Insert ':' into MAC again + echo ${mac_tmp:0:2}:${mac_tmp:2:2}:${mac_tmp:4:2}:${mac_tmp:6:2}:${mac_tmp:8:2}:${mac_tmp:10:2} + else + log "No MAC for devno '$devno' found in cmdline '$cmdline'. Exit." + exit 1 + fi +} + +function is_locally_administered_mac { + # Verifies if a MAC is a locally administered unicast MAC + # $1 = MAC, e.g. "00:11:22:33:44:55" + # Returns + # rc 0 = True, rc 1 = False + + local mac="$1" + + # Only Unicast MACs that have the "locally administered" bit set are allowed + # by OSA. The "locally administered" bit is the "second least significant" + # bit of the most significant MAC byte. In addition only unicast addresses + # are allowed. The unicast bit is the least significant bit of the most + # significant byte. + # Example: AA:BB:CC:DD:EE:FF + # Most significant Byte: ^^ + # In Binary: 1010 1010 + # Second least significant bit: ^ = locally administered = 1 + # Least significant bit: ^ = unicast = 0 + # Therefore the only MACs are allowed, that have the 10 as those 2 bits. + # This results in the following possible MACs (where X can be any hex char): + # X2:XX:XX:XX:XX:XX + # X6:XX:XX:XX:XX:XX + # XA:XX:XX:XX:XX:XX + # XE:XX:XX:XX:XX:XX + local regex="^[0-9A-Fa-f][26AaEe]" + if [[ $mac =~ $regex ]]; then + return 0 + else + return 1 + fi +} + +function get_ip_cmd { + # Determines the path of the ip cmd + # Returns: Path to ip cmd + + # When this script is called from a udev rule, it is not able to find + # the ip command. Also the 'which' command is not working. As different + # distros install it to different locations, we need to try out which + # path is working. + local paths=("/usr/sbin/ip" "/sbin/ip") + + for path in "${paths[@]}"; do + local full_path=$ROOT_DIR$path + if [[ -x $full_path ]]; then + echo "$full_path" + return 0 + fi + done + + log "'ip' command not found. Exiting." + exit 1 +} + +function set_mac { + # This function sets the given MAC on the given interface + # $1 = Interface name to set the mac on, e.g. "enc1" + # $2 = The mac address, e.g. "00:11:22:33:44:55" + + local if_name="$1" + local mac="$2" + if ! is_locally_administered_mac "$mac" ; then + log "MAC $mac is not a locally administered MAC. Aborting!" + exit 1 + fi + + local ip_cmd=$(get_ip_cmd) + local cmd="$ip_cmd link set $if_name address $mac 2>&1 > /dev/null" + stderr=$(eval "$cmd") + local rc=$? + if [[ $rc != 0 ]]; then + log "Operation '$cmd' failed with exit rc '$rc': $stderr. Aborting!" + exit 1 + fi + log "Successfully set MAC of interface '$if_name' to '$mac'" + +} + +function get_device_bus_id { + # $1 = the device number + # returns the corresponding device_bus_id + + echo "0.0.$1" +} + +function device_exists { + # $1 = the device bus id + local dev_bus_id="$1" + + # Check if device is already configured + path="/sys/bus/ccwgroup/devices/$dev_bus_id" + if ! [ -d "$path" ]; then + return 1 + fi +} + +function configure_device { + # $1 = the device bus id + # $2 = the port no + local dev_bus_id="$1" + local port_no="$2" + + # TODO(andreas_s): Do not depend on znetconf + # Errors of the following command are written to stderr, and therefore + # show up in the systemd units journal + znetconf -a $dev_bus_id -o portno=$port_no,layer2=1 + return "$?" +} + +function get_cmdline { + #Example: "some stuff nics=0001,0,aabbccddeeff;abcd,1,001122334455;" + echo $(cat /proc/cmdline) +} \ No newline at end of file diff --git a/guest_image_tools/usr/bin/setmac.sh b/guest_image_tools/usr/bin/setmac.sh index 3563314..cd43ca1 100755 --- a/guest_image_tools/usr/bin/setmac.sh +++ b/guest_image_tools/usr/bin/setmac.sh @@ -23,152 +23,10 @@ # Exit on error set -e +source $(dirname "$0")/dpm_guest_image_tools_common -# Matches MAC in format: xxxxxxxxxxxx -REGEX_MAC="[0-9A-Fa-f]{12}" -REGEX_EXTRACT_DEVNO="qeth/0\.0\.([0-9A-Fa-f]{4})/net" -REGEX_EXTRACT_IFNAME="/net/(.{1,15})" LOG_PREFIX=$(basename "$0") - -function log { - # $1 = message to log - - # Logging to syslog - logger "$LOG_PREFIX: $1" -} - -function extract_devno { - # Extracts the device number out of a device path - # $1 = the device path, e.g. "/devices/qeth/0.0.0001/net/enc1" - # Returns: device number, e.g. "0001" - - local dev_path="$1" - if [[ $dev_path =~ $REGEX_EXTRACT_DEVNO ]]; then - echo "${BASH_REMATCH[1]}" - else - log "Could not extract devno from '$dev_path'. Skipping!" - exit 1 - fi -} - -function extract_interface_name { - # Extracts the interface name out of a device path - # $1 = the device path, e.g. "/devices/qeth/0.0.0001/net/enc1" - # Returns: interface name, e.g. "enc1" - - local dev_path="$1" - if [[ $dev_path =~ $REGEX_EXTRACT_IFNAME ]]; then - echo "${BASH_REMATCH[1]}" - else - log "Could not find interface for device number '$devno' in path 'dev_path'. Skipping!" - exit 1 - fi -} - - -function extract_mac { - # Get the mac address to a given device number from the cmdline - # $1 = the device number, e.g. "0001" - # $2 = the cmdline, e.g. "0001,0,aabbccddeeff;" - # Returns: The MAC address in format xx:xx:xx:xx:xx:xx - - local devno="$1" - local cmdline="$2" - # Regex matches: ,,; - regex=${devno}",[0-1],("${REGEX_MAC}");" - if [[ $cmdline =~ $regex ]]; then - local mac_tmp="${BASH_REMATCH[1]}" - # Insert ':' into MAC again - echo ${mac_tmp:0:2}:${mac_tmp:2:2}:${mac_tmp:4:2}:${mac_tmp:6:2}:${mac_tmp:8:2}:${mac_tmp:10:2} - else - log "No MAC for devno '$devno' found in cmdline '$cmdline'. Exit." - exit 1 - fi -} - -function is_locally_administered_mac { - # Verifies if a MAC is a locally administered unicast MAC - # $1 = MAC, e.g. "00:11:22:33:44:55" - # Returns - # rc 0 = True, rc 1 = False - - local mac="$1" - - # Only Unicast MACs that have the "locally administered" bit set are allowed - # by OSA. The "locally administered" bit is the "second least significant" - # bit of the most significant MAC byte. In addition only unicast addresses - # are allowed. The unicast bit is the least significant bit of the most - # significant byte. - # Example: AA:BB:CC:DD:EE:FF - # Most significant Byte: ^^ - # In Binary: 1010 1010 - # Second least significant bit: ^ = locally administered = 1 - # Least significant bit: ^ = unicast = 0 - # Therefore the only MACs are allowed, that have the 10 as those 2 bits. - # This results in the following possible MACs (where X can be any hex char): - # X2:XX:XX:XX:XX:XX - # X6:XX:XX:XX:XX:XX - # XA:XX:XX:XX:XX:XX - # XE:XX:XX:XX:XX:XX - local regex="^[0-9A-Fa-f][26AaEe]" - if [[ $mac =~ $regex ]]; then - return 0 - else - return 1 - fi -} - -function get_ip_cmd { - # Determines the path of the ip cmd - # Returns: Path to ip cmd - - # When this script is called from a udev rule, it is not able to find - # the ip command. Also the 'which' command is not working. As different - # distros install it to different locations, we need to try out which - # path is working. - local paths=("/usr/sbin/ip" "/sbin/ip") - - for path in "${paths[@]}"; do - if [[ -x $path ]]; then - echo "$path" - return 0 - fi - done - - log "'ip' command not found. Exiting." - exit 1 -} - -function set_mac { - # This function sets the given MAC on the given interface - # $1 = Interface name to set the mac on, e.g. "enc1" - # $2 = The mac address, e.g. "00:11:22:33:44:55" - - local if_name="$1" - local mac="$2" - if ! is_locally_administered_mac "$mac" ; then - log "MAC $mac is not a locally administered MAC. Aborting!" - exit 1 - fi - - local ip_cmd=$(get_ip_cmd) - local cmd="$ip_cmd link set $if_name address $mac 2>&1 > /dev/null" - stderr=$(eval "$cmd") - local rc=$? - if [[ $rc != 0 ]]; then - log "Operation '$cmd' failed with exit rc '$rc': $stderr. Aborting!" - exit 1 - fi - log "Successfully set MAC of interface '$if_name' to '$mac'" - -} - -function get_cmdline { - #Example: "some stuff nics=0001,0,aabbccddeeff;abcd,1,001122334455;" - echo $(cat /proc/cmdline) -} - # e.g. /devices/qeth/0.0.0001/net/enc1 DEV_PATH="$1" CMDLINE=$(get_cmdline)