Merge "Add DPM Guest Image Tools"

This commit is contained in:
Jenkins 2017-02-21 08:36:27 +00:00 committed by Gerrit Code Review
commit e760974b5c
7 changed files with 407 additions and 0 deletions

6
.gitignore vendored
View File

@ -18,6 +18,12 @@ develop-eggs
lib
lib64
# Exclude those directories, as they contain the DPM Guest Image Tools
# Don't ignore those directories, as they contain the DPM Guest Image Tools.
# '!' reverts the matching on 'bin' and 'lib' under guest_image_tools/usr/"
!guest_image_tools/usr/bin
!guest_image_tools/usr/lib
# Installer logs
pip-log.txt

View File

@ -0,0 +1,121 @@
=====================
DPM Guest Image Tools
=====================
The DPM Guest Image Tools must be installed within a DPM OpenStack image.
The purpose of the tools are to dynamically configure the network interfaces.
Doing IP configuration is not part of the tools. This is handled like usual
with cloud-init.
autoconfigure_networking
------------------------
Description
+++++++++++
Is used to configure all network interfaces that are listed in the kernels
cmdline */proc/cmdline* with the given adapter port. All interfaces are
configured in layer2 mode.
The format of the data in the cmdline must be
<devno>,<port>[,<mac>];
Example
0001,1,0a0000000011;0004,0;
This will result in
* 0001 being configured with port 1
* 0004 being configured with port 0
Content
+++++++
* systemd service autoconfigure_networking.service
* shell script autoconfigure_networking.sh
Trigger
+++++++
The systemd service autoconfigure_networking.service is configured to
run before cloud-init during boot. It's job is to trigger the shell script.
Manual execution of the shell script
/usr/bin/autoconfigure_networking.sh
Installation
++++++++++++
* Place the following files in the guest image
* dpm_guest_tools/usr/bin/autoconfigure_networking.sh
-> /usr/bin/autoconfigure_networking.sh
* dpm_guest_tools/usr/lib/systemd/system/autoconfigure_networking.service
-> /usr/lib/systemd/systemd/autoconfigure_networking.service
* Ensure permissions
chmod 644 /usr/lib/systemd/system/autoconfigure_networking.service
* Enable the service for autostart
* systemctl enable autoconfigure_networking.service
setmac
------
Description
+++++++++++
Is used to reconfigure the MAC address of a network interface. The mapping
must be provided via the kernels cmdline */proc/cmdline*.
The format of the data in the cmdline must be
<devno>,<portno>,<mac>;
Example
0001,1,0a0000000011;0004,0;
* 0001: corresponding interface will be set to mac 0a:00:00:00:00:11
* 0004: mac will not be changed
Content
+++++++
* shell script setmac.sh
* udev rule 80-setmac.rules
Trigger
+++++++
If a new network interface gets configured (e.g. for device 0.0.0001),
the udev rules triggers the shell script passing in the device-bus-id.
If a service instance for a certain device-bus-id already exists, it will not
get started again.
Manual execution of the shell script
/usr/bin/setmac.sh <dev-bus-id>
Installation
++++++++++++
* Place the following files in the guest image
* dpm_guest_tools/usr/bin/setmac.sh
-> /usr/bin/setmac.sh
* dpm_guest_tools/etc/udev/rules.d/80-setmac.rules
-> /etc/udev/rules.d/80-setmac.rules

View File

@ -61,6 +61,7 @@ Using the driver
installation
configuration
guest_image_tools
Creating DPM Images

View File

@ -0,0 +1,3 @@
# React when an qeth device has been added
# %p is the device-path, e.g. /devices/qeth/0.0.0001/net/enc1
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="qeth", RUN+="/usr/bin/setmac.sh %p"

View File

@ -0,0 +1,82 @@
#! /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.
# This script configures all qeth network interfaces that are listed in the
# kernels cmdline */proc/cmdline* with the given adapter port. All interfaces
# are configured in layer2 mode.
# The format of the cmdline parameters must be either
# <devno>,<port>;
# or
# <devno>,<port>,<mac>;
LOG_PREFIX=$(basename "$0")
REGEX_DEV_NO="[0-9A-Fa-f]{4}"
REGEX_MAC="[0-9A-Fa-f]{12}"
# Regex to match
# <devno>,<port>;
# <devno>,<port>,<mac>;
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"
}
log "Start"
# Default return code
rc=0
# Bash does not support global matching, therefore
# we remove the current match from the cmdline to
# allow matching the next value
while [[ $CMDLINE =~ $REGEX ]]; do
dev_no="${BASH_REMATCH[1]}"
dev_bus_id="0.0.$dev_no"
log "Configuring dev_bus_id $dev_bus_id."
# remove current match from variable, to allow next match in next iteration
CMDLINE=${CMDLINE#*"${dev_no}"}
# Check if device is already configured
path="/sys/bus/ccwgroup/devices/$dev_bus_id"
if [ -d "$path" ]; then
log "Interface for $dev_bus_id already configured. Skipping."
continue
fi
# Determine port
port="${BASH_REMATCH[2]}"
log "Port $port used for dev_bus_id $dev_bus_id."
# 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,layer2=1
if [[ $? != 0 ]]; then
rc=1
fi
done
log "Finished with rc '$rc'"
exit $rc

View File

@ -0,0 +1,181 @@
#! /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.
# Input parameter of this script is the device path of an existing qeth network
# interface. E.g.
# ./setmac.sh "/devices/qeth/0.0.0001/net/enc1"
# This script sets the MAC address of this qeth network interface to the MAC
# provided in the kernels cmdline */proc/cmdline*.
# The format of the cmdline parameters must be
# <devno>,<port>,<mac>;
# 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: <devno>,<port>,<mac>;
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 <nothing>
# 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)
log "Start with device path '$DEV_PATH'"
devno=$(extract_devno $DEV_PATH)
if_name=$(extract_interface_name $DEV_PATH)
mac=$(extract_mac $devno "$CMDLINE")
log "Using device number '$devno', interface '$if_name', mac '$mac'."
set_mac "$if_name" "$mac"
log "Finished"

View File

@ -0,0 +1,13 @@
[Unit]
Description=Autoconfigure Network Interfaces (pre-networking)
DefaultDependencies=no
Before=cloud-init-local.service
[Service]
Type=oneshot
ExecStart=/usr/bin/autoconfigure_networking.sh
RemainAfterExit=yes
# Output needs to appear in instance console output
StandardOutput=journal+console
[Install]
WantedBy=multi-user.target