Update compute build & run for new base 19/60619/4
authorMatt Welch <matt.welch@intel.com>
Thu, 20 Jul 2017 18:11:36 +0000 (14:11 -0400)
committerDaniel Farrell <dfarrell@redhat.com>
Thu, 27 Jul 2017 18:48:33 +0000 (14:48 -0400)
Moved build collateral into build/ dir.
Updates to build files to reflect new systemd-based container.
Added connect_container_to_network to create veth pairs and bind
container to Linux beidges on the host.
Updates to local.conf to enable instance VNC console and use ODL by
default.
Added restart script that can usually be used instead of start.sh.
Updates to run script to reflect new systemd-based container.

Change-Id: I6e3f2d184a222cdb5ae74b2c221b0f132ae47c62
Signed-off-by: Matt Welch <matt.welch@intel.com>
Signed-off-by: Daniel Farrell <dfarrell@redhat.com>
.coafile
docker/openstack/compute/build/Dockerfile [moved from docker/openstack/compute/Dockerfile with 62% similarity]
docker/openstack/compute/build/local.conf [moved from docker/openstack/compute/local.conf with 61% similarity]
docker/openstack/compute/build/restart.sh [new file with mode: 0755]
docker/openstack/compute/build/start.sh [new file with mode: 0755]
docker/openstack/compute/build_compute.sh
docker/openstack/compute/run_compute.sh
docker/openstack/compute/start.sh [deleted file]
docker/openstack/network/connect_container_to_networks.sh [new file with mode: 0755]

index 20a5bde61625cc60052be532645c6b1d57c2367e..b688a201d9162d930889d7144060c4d5b341790a 100644 (file)
--- a/.coafile
+++ b/.coafile
@@ -54,6 +54,7 @@ default_actions = JSONFormatBear: ApplyPatchAction
 
 [all.shell]
 bears = ShellCheckBear
+shell = bash
 files = **.sh
 
 [all.python]
similarity index 62%
rename from docker/openstack/compute/Dockerfile
rename to docker/openstack/compute/build/Dockerfile
index 96dd7d3c6f76278d5a916053af6a0ce6a44f499e..2fd5250a677480ee10886573d86d07dc565c361f 100644 (file)
@@ -1,4 +1,4 @@
-FROM ubuntu:14.04
+FROM odl-registry:4000/s3p/systemd:v0.1
 
 # Schema: https://github.com/projectatomic/ContainerApplicationGenericLabels
 LABEL name="Int/Pack OpenStack Compute Node" \
@@ -8,40 +8,44 @@ LABEL name="Int/Pack OpenStack Compute Node" \
       vcs-url="https://git.opendaylight.org/gerrit/p/integration/packaging.git"
 
 ENV PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
-    DEBIAN_FRONTEND=noninteractive
+    DEBIAN_FRONTEND=noninteractive \
+    container=docker
 
 # Install devstack dependencies
 # Start ignoring DockerfileLintBear
-RUN apt-get install -y --no-install-recommends \
+RUN apt-get update && apt-get install -y --no-install-recommends \
+    ca-certificates \
     git \
+    inetutils-ping \
+    iproute2 \
+    iptables \
+    lsb-release \
     openssh-server \
-    ca-certificates \
     openvswitch-common \
     openvswitch-switch \
-    dbus && \
-    apt-get clean && \
+    sudo \
+    vim && \
     rm -rf /var/lib/apt/lists/*
 # Stop ignoring
 
 # Add stack user
 RUN groupadd stack && \
     useradd -g stack -s /bin/bash -m stack && \
-    echo "stack ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
+    echo "stack ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers && \
+    echo "stack:stack" | chpasswd
 
 # Get devstack
-USER stack
 RUN git clone https://git.openstack.org/openstack-dev/devstack /home/stack/devstack
 
-# Copy and chown local.conf to stack
+# copy local.conf & scripts
 COPY local.conf /home/stack/local.conf
-RUN sudo chown stack:stack /home/stack/local.conf
-
-# Copy start.sh and chown to stack
 COPY start.sh /home/stack/start.sh
-RUN sudo chown stack:stack /home/stack/start.sh && chmod 766 /home/stack/start.sh
+COPY restart.sh /home/stack/restart.sh
+RUN chown -R stack:stack /home/stack && \
+    chmod 766 /home/stack/start.sh && \
+    chmod 766 /home/stack/restart.sh
 
-# Open Horizon port
-EXPOSE 80
+WORKDIR /home/stack
 
 CMD ["/home/stack/start.sh"]
 
similarity index 61%
rename from docker/openstack/compute/local.conf
rename to docker/openstack/compute/build/local.conf
index 655d3c77c897f796de841fb1383048b882d17183..f4ab1e3cac128d4c5cdcaac757469a6ea9224788 100644 (file)
@@ -1,11 +1,9 @@
 [[local|localrc]]
 ## Services ##
-## When using OpenDaylight for NetVirt, remove q-agt from ENABLED_SERVICES
-ENABLED_SERVICES=n-cpu,q-agt
+ENABLED_SERVICES=n-cpu
 
 ## Repository sources ##
 GIT_BASE=${GIT_BASE:-https://git.openstack.org}
-PIP_UPGRADE=True
 
 ## logging configuration ##
 HOME=/opt/stack
@@ -16,6 +14,9 @@ SCREEN_LOGDIR=${LOGDIR}/screen-logs
 LOGFILE=${LOGDIR}/stack.sh.log
 LOG_COLOR=False
 VERBOSE=True
+# uncomment these after a successful stacking & connection to OpenStack Control node
+# OFFLINE=True
+# RECLONE=False
 
 ## Passwords & authentication ##
 ADMIN_PASSWORD=secret
@@ -28,11 +29,11 @@ SERVICE_TOKEN_PASSWORD=${ADMIN_PASSWORD}
 SERVICE_TOKEN=111222333
 
 ## Hosts & services configuration ##
-HOST_IP=172.17.0.5
+HOST_IP=10.129.10.10
 HOST_NAME=$(hostname)
-SERVICE_HOST=10.20.0.2
+SERVICE_HOST=10.129.27.2
 SERVICE_HOST_NAME=${HOST_NAME}
-SERVICE_LISTEN_ADDRESS=${HOST_IP}
+SERVICE_LISTEN_ADDRESS=0.0.0.0
 SERVICE_LOCAL_HOST=${SERVICE_HOST}
 MYSQL_HOST=${SERVICE_HOST}
 RABBIT_HOST=${SERVICE_HOST}
@@ -41,9 +42,12 @@ KEYSTONE_AUTH_HOST=${SERVICE_HOST}
 KEYSTONE_SERVICE_HOST=${SERVICE_HOST}
 
 ## Network Configuration ##
-## When using OpenDaylight for NetVirt uncomment the following two lines
-#enable_plugin networking-odl http://git.openstack.org/openstack/networking-odl stable/newton
-#ODL_MODE=compute
+## Use OpenDaylight for NetVirt
+enable_plugin networking-odl http://git.openstack.org/openstack/networking-odl stable/newton
+ODL_BOOT_WAIT_URL=restconf/operational/network-topology:network-topology/
+ODL_MODE=compute
+ODL_MGR_PORT=6640
+ODL_PORT=8080
 IP_VERSION=4
 
 # disable security groups TODO: determine impact
@@ -52,6 +56,10 @@ Q_USE_SECGROUP=False
 ## Nova configuration ##
 LIBVIRT_TYPE=qemu
 
-
+# VNC enabling
+NOVA_VNC_ENABLED=True
+NOVNCPROXY_URL="http://${SERVICE_HOST}:6080/vnc_auto.html" #Add Controller Node IP address
+VNCSERVER_LISTEN=$HOST_IP
+VNCSERVER_PROXYCLIENT_ADDRESS=$VNCSERVER_LISTEN
 # vim: set ft=conf :
 
diff --git a/docker/openstack/compute/build/restart.sh b/docker/openstack/compute/build/restart.sh
new file mode 100755 (executable)
index 0000000..c0cbdd6
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/bash
+# file: restart.sh
+# Restart/restack in a container that has already run DEVstack and start.sh
+# restart.sh can safely be used instead of start.sh
+
+# unstack first
+echo "[$(date)] S3P::${0}:: unstacking..."
+/home/stack/devstack/unstack.sh
+rm -rf /home/stack/stacking.status
+
+if [[ "$1" == "clean" ]] ; then
+    /home/stack/devstack/clean.sh
+    rm -rf /opt/stack/*
+fi
+
+# restart
+echo "[$(date)] ${0} :: stacking..."
+/home/stack/start.sh
diff --git a/docker/openstack/compute/build/start.sh b/docker/openstack/compute/build/start.sh
new file mode 100755 (executable)
index 0000000..1736a27
--- /dev/null
@@ -0,0 +1,57 @@
+#!/bin/bash
+# On docker run, Env Variables "STACK_PASS & SERVICE_HOST" should be set using -e
+#  example 'docker run -e "STACK_PASS=stack" -e "SERVICE_HOST=192.168.0.5" compute'
+set -o nounset # throw an error if a variable is unset to prevent unexpected behaviors
+# the following variables should be overridden at runtime with docker run -e "..."
+# ODL_NETWORK, SERVICE_HOST
+ODL_NETWORK=${ODL_NETWORK:-True}
+SERVICE_HOST=${SERVICE_HOST:-"192.168.1.2"}
+STACK_PASS=stack
+DEVSTACK_HOME="/home/stack/devstack"
+CONF_PATH=$DEVSTACK_HOME/local.conf
+BRANCH_NAME=stable/newton
+TAG_NAME="origin/${BRANCH_NAME}"
+
+#Set Nameserver to google
+[ -z "$(grep "8.8.8.8" /etc/resolv.conf )" ] && echo nameserver 8.8.8.8 | sudo tee -a /etc/resolv.conf
+
+# change the stack user password
+echo "stack:$STACK_PASS" | sudo chpasswd
+
+# get container IP for mgmt network interface
+ip=`/sbin/ip -o -4 addr list ethphys01 | awk '{print $4}' | cut -d/ -f1`
+
+# remove OVS db (for case of restacking a node to regenerate UUID)
+sudo /usr/share/openvswitch/scripts/ovs-ctl stop
+sudo rm -rf /etc/openvswitch/conf.db
+# remove any dead screen sessions from previous stacking
+screen -wipe
+
+# set the correct branch in devstack
+cd $DEVSTACK_HOME || exit
+# shellcheck disable=SC2063
+[ -z "$(git branch -a | grep "* ${BRANCH_NAME}")" ] && \
+        git fetch && \
+        git checkout -b ${BRANCH_NAME} -t ${TAG_NAME}
+
+# copy local.conf into devstack and customize, based on environment including:
+# ODL_NETWORK, ip, DEVSTACK_HOME, SERVICE_HOST
+cp /home/stack/local.conf $CONF_PATH
+
+# Configure local.conf
+# update the ip of this host & SERVICE_HOST
+sed -i "s/HOST_IP=.*/HOST_IP=${ip}/" $CONF_PATH
+sed -i "s/SERVICE_HOST=.*/SERVICE_HOST=$SERVICE_HOST/" $CONF_PATH
+
+$DEVSTACK_HOME/stack.sh
+
+# write a marker file to indicate successful stacking
+if [ $? = 0 ] ; then
+    echo "$(hostname) stacking successful at $(date)" >> stacking.status
+    /home/stack/devstack/tools/info.sh >> stacking.status
+    # set devstack to OFFLINE mode after a successful stack
+    sed -i "s/^#.*\(OFFLINE=True\)/\1/g" /home/stack/$SRC_CONF
+    sed -i "s/^#.*\(RECLONE=False\)/\1/g" /home/stack/$SRC_CONF
+fi
+
+# vim set et ts=4 sw=4 :
index 757f2702dedbc09476043d21c60e114d8d936ef2..af68386c3acaefba3e7a99328d555a90fe8b9d43 100755 (executable)
@@ -1,21 +1,21 @@
 #!/bin/bash
 # file: build_compute.sh
 # info: builds a docker compute image
-IMAGE_BASE=s3p/compute
-IMAGE_TAG=v0.2
+IMAGE_REGISTRY=${IMAGE_REGISTRY:-odl-registry:4000}
+IMAGE_REPO=${IMAGE_REPO:-s3p/compute}
+IMAGE_TAG=${IMAGE_TAG:-latest}
 if [ -n "$1" ] ; then
     # use arg as image tag if supplied
     IMAGE_TAG="$1"
 fi
-IMAGE_NAME=$IMAGE_BASE:$IMAGE_TAG
-DOCKERFILE=Dockerfile
+IMAGE_NAME="${IMAGE_REGISTRY}/${IMAGE_REPO}:${IMAGE_TAG}"
+DOCKERFILE=${DOCKERFILE:-"./build/Dockerfile"}
 
 echo "Building $IMAGE_NAME from Dockerfile=$DOCKERFILE at $(date) ... "
-docker build -t ${IMAGE_NAME} -f ${DOCKERFILE} \
-# noqa ShellCheckBear
-    --build-arg http_proxy=$http_proxy --build-arg https_proxy=$https_proxy .
-
-if [ $? = 0 ] ; then
+# shellcheck disable=SC2154
+docker build -t ${IMAGE_NAME} -f ${DOCKERFILE} --build-arg http_proxy=$http_proxy --build-arg https_proxy=$https_proxy .
+EXIT_CODE="$?"
+if [ "$EXIT_CODE" = 0 ] ; then
     PROXIES=""
     if [ -n "$http_proxy" ] ; then
         # noqa ShellCheckBear
@@ -27,5 +27,5 @@ if [ $? = 0 ] ; then
     echo "  docker run -it --rm $PROXIES $IMAGE_NAME bash"
 else
     echo "An error occurred during the build of $IMAGE_NAME"
+    exit $EXIT_CODE
 fi
-
index be82df0d23ae3fcd94d2751cb1d09ac713e9c86c..b0ae7409abf610e708a32257d07d39ddaa93ef0e 100755 (executable)
@@ -6,53 +6,50 @@
 # + Compute host image must also be available locally or in a registry.
 
 # image selection
-IMAGE_REPO="s3p/compute"
-IMAGE_VERSION="v0.2" # v0.2==Ubuntu 14.04
-IMAGE_NAME="${IMAGE_REPO}:${IMAGE_VERSION}"
+IMAGE_REGISTRY=${IMAGE_REGISTRY:-"odl-registry:4000"}
+IMAGE_REPO=${IMAGE_REPO:-s3p/compute}
+IMAGE_TAG=${IMAGE_TAG:-v0.5}
+IMAGE_NAME="${IMAGE_REGISTRY}/${IMAGE_REPO}:${IMAGE_TAG}"
 
 # image configuration
-HOST_ID=01
-COMP_ID=04
-NAME="compute-${HOST_ID}-${COMP_ID}"
-ODL_NETWORK=false
-# when running on a RHEL-derivative host, use "--security-opt seccomp=unconfined"
+HOST_ID=${HOST_ID:-99} # HOST_ID represents physical host ID, should be in env
+COMP_ID=${COMP_ID:-11} # COMP_ID represents compute node ID, should be in env
+DEFAULT_NAME="compute-${HOST_ID}-${COMP_ID}"
+NAME=${CONTAINER_NAME:-$DEFAULT_NAME}
 CAPABILITIES="--privileged --cap-add ALL --security-opt apparmor=docker-unconfined "
-CGROUP_MOUNT=""
-MOUNTS="-v /dev:/dev -v /lib/modules:/lib/modules $CGROUP_MOUNT "
-STACK_PASS=${STACK_PASS:-stack}
-# default SERVICE_HOST, based on openstack. This may be overridden.
-SERV_HOST=${SERV_HOST:-10.20.0.2}
-# define _no_proxy based on the cluster topology
-_no_proxy=localhost,10.0.0.0/8,192.168.0.0/16,172.17.0.0/16,127.0.0.1,127.0.0.0/8,$SERV_HOST
+SYSTEMD_ENABLING=" --stop-signal=SIGRTMIN+3 "
+CGROUP_MOUNT=" -v /sys/fs/cgroup:/sys/fs/cgroup:ro "
+MOUNTS="-v /dev:/dev -v /lib/modules:/lib/modules $CGROUP_MOUNT $SYSTEMD_ENABLING "
 
-#if [ -n "$1" ] ; then
-#    echo "Command argument supplied, running \"$1\" in $NAME..."
-#    COMMAND="$1"
-#fi
+# Container environment and OpenStack Config
+STACK_USER=${STACK_USER:-stack}
+STACK_PASS=${STACK_PASS:-stack}
+ODL_NETWORK=${ODL_NETWORK:-True}
+SERVICE_HOST=${SERVICE_HOST:-10.129.19.2}
+NO_PROXY=localhost,10.0.0.0/8,192.168.0.0/16,172.17.0.0/16,127.0.0.1,127.0.0.0/8,$SERVICE_HOST
 
 # noqa ShellCheckBear
-SERVICE_NODE_NAME="service-node"
-# check to see that service-node is running first and get its IP from Docker
-SERVICE_NODE_IP=$(docker inspect -f "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" service-node)
-if [ -z "$SERVICE_NODE_IP" ] ; then
-    echo "WARNING: no service node is available on overlay-net."
-    echo "You can launch the compute container, but it may not be able to connect to a service node."
-fi
-SERV_HOST=$SERVICE_NODE_IP
-docker run -dit --name ${NAME} --hostname ${NAME} \
+docker run -dit --name ${NAME} --hostname ${NAME} --env TZ=America/Los_Angeles \
     # noqa ShellCheckBear
     --env http_proxy=$http_proxy --env https_proxy=$https_proxy \
     # noqa ShellCheckBear
-    --env no_proxy=$_no_proxy \
+    --env no_proxy=$NO_PROXY \
     --env ODL_NETWORK=$ODL_NETWORK \
     --env STACK_PASS=$STACK_PASS \
-    --env SERV_HOST=$SERV_HOST \
+    --env SERVICE_HOST=$SERVICE_HOST \
     --env container=docker \
-    --net=overlay-net \
     $MOUNTS \
-    $CAPABILITIES $IMAGE_NAME \
-    /bin/bash
+    $CAPABILITIES \
+    $IMAGE_NAME \
+    /sbin/init
 
-CONTAINER_SHORT_ID=$(docker ps -aqf "name=${NAME}")
-docker exec -it $CONTAINER_SHORT_ID /bin/bash
+# connect containers to host bridges (assumes bridges named br_data and br_mgmt exist on the host
+../network/connect_container_to_networks.sh $HOSTNAME $COMP_ID compute
 
+CONTAINER_SHORT_ID=$(docker ps -aqf "name=${NAME}")
+AUTO_STACK=no
+if [[ "$AUTO_STACK" == "no" ]] ; then
+    docker exec -it -u stack $CONTAINER_SHORT_ID /bin/bash
+else
+    docker exec -d -u stack $CONTAINER_SHORT_ID /bin/bash -c /home/stack/start.sh
+fi
diff --git a/docker/openstack/compute/start.sh b/docker/openstack/compute/start.sh
deleted file mode 100755 (executable)
index 9341a40..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/bin/sh
-# On docker run, Env Variables "STACK_PASS & SERV_HOST" should be set using -e
-#  example 'docker run -e "STACK_PASS=stack" -e "SERV_HOST=192.168.0.5" compute'
-# or overided below by uncommenting:
-#STACK_PASS="stack"
-# SERV_HOST="192.168.0.5"
-# ODL_NETWORK should be set in the 'docker run' script
-set -o nounset # throw an error if a variable is unset to prevent unexpected behaviors
-ODL_NETWORK=${ODL_NETWORK}
-DEVSTACK_HOME="/home/stack/devstack"
-CONF_PATH=$DEVSTACK_HOME/local.conf
-BRANCH_NAME=stable/newton
-TAG_NAME="origin/${BRANCH_NAME}"
-
-#Set Nameserver to google
-echo nameserver 8.8.8.8 | sudo tee -a /etc/resolv.conf
-
-# change the stack user password
-echo "stack:$STACK_PASS" | sudo chpasswd
-
-# get container IP
-ip=`/sbin/ip -o -4 addr list eth0 | awk '{print $4}' | cut -d/ -f1`
-
-# Start SSH Service
-# Centos7: sudo: service: command not found
-#sudo service ssh start
-
-# Start openvswitch
-# Centos7: sudo: service: command not found
-#sudo service openvswitch-switch start
-
-# set the correct branch in devstack
-cd $DEVSTACK_HOME || exit
-git fetch
-git checkout -b ${BRANCH_NAME} -t ${TAG_NAME}
-
-# copy local.conf into devstack and customize, based on environment including:
-# ODL_NETWORK, ip, DEVSTACK_HOME, SERV_HOST
-cp /home/stack/local.conf $CONF_PATH
-
-# Configure local.conf
-# update the ip of this host & SERVICE_HOST
-sed -i "s/HOST_IP=.*/HOST_IP=${ip}/" $CONF_PATH
-sed -i "s/SERVICE_HOST=.*/SERVICE_HOST=$SERV_HOST/" $CONF_PATH
-# modify the local.conf according to ODL_NETWORK value
-echo "Preparing $CONF_PATH for ODL=$ODL_NETWORK"
-echo
-if [ "$ODL_NETWORK" = "false" ] ; then
-    # prepare local.conf to NOT use ODL networking (default to Neutron)
-    sed -i "s:^\(enable_plugin networking-odl\):#\1:g" $CONF_PATH
-    sed -i "s:^\(ODL_MODE=compute\):#\1:g" $CONF_PATH
-    sed -i "s:^\(ENABLED_SERVICES=\).*:\1n-cpu,q-agt:g" $CONF_PATH
-else
-    # prepare local.conf to use ODL networking
-    sed -i "s:^#\(enable_plugin networking-odl\):\1:g" $CONF_PATH
-    sed -i "s:^#\(ODL_MODE=compute\):\1:g" $CONF_PATH
-    sed -i "s:^\(ENABLED_SERVICES=\).*:\1n-cpu:g" $CONF_PATH
-fi
-
-$DEVSTACK_HOME/stack.sh
-
diff --git a/docker/openstack/network/connect_container_to_networks.sh b/docker/openstack/network/connect_container_to_networks.sh
new file mode 100755 (executable)
index 0000000..848c64d
--- /dev/null
@@ -0,0 +1,179 @@
+#!/bin/bash
+# this will
+# 1) create a pair of veth interfaces
+# 2) add one to a physical bridge (assumed to exist)
+# 3) add the peer to a docker container netns
+# 4) set its IP address
+
+set -e
+
+fn_usage() {
+    echo "Usage:"
+    echo "connect_container_to_networks.sh <physical host name> <container ID> <container_type>"
+    echo
+}
+
+fn_get_host_index() {
+    # TODO: this will need modification for cperf or other clusters
+    local PHYS_HOST_NAME=${1}
+    [ -z "$1" ] && echo "ERROR: a host ID number must be supplied" && exit
+    local __hix=${PHYS_HOST_NAME##"an11-"} # trim leading rack ID from hostname (e.g. an11-31-odl -> 31-odl-perf)
+    local H_IXd=${__hix%%-*} # trim trailing characters after rack position (e.g. 31-odl-perf -> 31)
+    H_IXd=${H_IXd##"0"} # trim leading zeroes
+    echo "$H_IXd"
+}
+
+fn_link_container_netns() {
+    echo "INFO: linking net namespace of container $CONTAINER_NAME"
+    mkdir -p $HOST_NETNS_ROOT
+    # derived variables
+    SANDBOX_KEY=$(docker inspect -f '{{.NetworkSettings.SandboxKey}}' $CONTAINER_NAME)
+    NETNS_NAME="netns-$CONTAINER_NAME"
+
+    ln -s $SANDBOX_KEY $HOST_NETNS_ROOT/$NETNS_NAME
+    ls -al $HOST_NETNS_ROOT
+}
+
+fn_attach_veth_to_container() {
+    ## Attach veth to container
+    CONTAINER_VETH_NAME="ethphys${A_IX}"
+    ip link set $VETH_CONT netns $NETNS_NAME
+    ip netns exec $NETNS_NAME ip link set dev $VETH_CONT name $CONTAINER_VETH_NAME
+    # set the device mac address
+    ip netns exec $NETNS_NAME ip link set dev $CONTAINER_VETH_NAME address $CONTAINER_MAC
+    # set the adapter IP address
+    ip netns exec $NETNS_NAME ip address add $CONTAINER_IP dev $CONTAINER_VETH_NAME
+    echo "Container net-namespace contents:"
+    ip netns exec $NETNS_NAME ip link set dev $CONTAINER_VETH_NAME up
+    ip netns exec $NETNS_NAME ip a s
+    echo
+}
+
+fn_create_and_link_veth() {
+    ## Create veth pair (peers)
+    VETH_BASE="ve${H_IXx}${C_IXx}${A_IX}"
+    VETH_HOST=${VETH_BASE}h
+    VETH_CONT=${VETH_BASE}c
+    ip link add $VETH_HOST type veth peer name $VETH_CONT
+    ip link set dev $VETH_HOST up
+    ## attach veth in host netns to PHYS_BRIDGE
+    brctl addif $PHYS_BRIDGE_NAME $VETH_HOST
+
+    fn_attach_veth_to_container
+}
+
+
+fn_display_link_status() {
+    # if all goes well, we've linked the container to the bridge, update the counter
+    if [ $? -eq 0 ] ; then
+        # display status info
+        echo "Successfully linked container $CONTAINER_NAME to bridge $PHYS_BRIDGE_NAME"
+        echo "H_IX:   ${H_IXd} (0x${H_IXx})"
+        echo "C_IX:   ${C_IXd} (0x${C_IXx})"
+        echo "C_MAC:  ${CONTAINER_MAC}"
+        echo "C_IP4:  ${CONTAINER_IP}"
+        echo "C_veth: ${CONTAINER_VETH_NAME} (${VETH_CONT})"
+        echo "H_veth: ${VETH_HOST}"
+        echo
+    fi
+}
+
+# main:
+# lab constants
+MAC_PREFIX="fe:53:00"
+HOST_NETNS_ROOT=/var/run/netns
+NETMASK_LEN=16
+
+# parse input arguments
+PHYS_HOST_NAME="$1"
+
+CONTAINER_ID_NUMBER="${2}"
+# container name can be constructed from ID num: compute-<hostID>-<CONTAINER_ID_NUMBER>
+CONTAINER_TYPE=${3}
+
+# this is deterministic where each container gets an index which is used
+# + to create the IP address, MAC address, VETH numbering, etc
+H_IXd=$(fn_get_host_index $PHYS_HOST_NAME  )
+
+# host index (rack position), convert to 2 hex digits
+# H_IXx (hex representation of host id) can be passed as an input argument or used from the environment
+H_IXx=${H_IXx:-$(printf "%.2x" $H_IXd)}
+SUBNET_SEGMENT="${H_IXd}"
+
+
+# For last octet of IP address:
+# host=1, service=2, network=3, compute=11-200, floatingIP=201-254
+case "$CONTAINER_TYPE" in
+    host)
+        echo "Host is already attached to network bridge:"
+        exit
+        ;;
+    service)
+        echo "CONTAINER_TYPE = service"
+        CONTAINER_NAME=service-node
+        CONTAINER_ID_NUMBER=2
+        ;;
+    network)
+        echo "CONTAINER_TYPE = network"
+        CONTAINER_NAME=network-node
+        CONTAINER_ID_NUMBER=3
+        ;;
+    measure)
+        echo "CONTAINER_TYPE = measure"
+        CONTAINER_NAME=measure-node
+        CONTAINER_ID_NUMBER=4
+        ;;
+    compute)  echo "CONTAINER_TYPE = compute"
+        CONTAINER_NAME="compute-${H_IXd}-${CONTAINER_ID_NUMBER}"
+        echo "Compute node # = $CONTAINER_ID_NUMBER"
+        ;;
+    *)  echo "ERROR: Invalid CONTAINER_TYPE \"$CONTAINER_TYPE\" specified"
+        exit
+esac
+
+# description:
+# input: container type (string), "ID"  (int, 11-200) supplied on the command line
+#   this script will:
+# 1) link the container to both br_mgmt and br_data
+# 2) modify their MAC addresses accordingly
+# 3) supply IP addresse
+
+DEBUG=off
+if [[ "$DEBUG" != "on" ]] ; then
+    fn_link_container_netns
+fi
+
+C_IXd=$CONTAINER_ID_NUMBER
+C_IXx=$(printf "%.2x" $C_IXd)
+
+# connect the adapter
+for ADAPTER_IX in {1..2}; do
+    A_IX="$(printf "%.2x" $ADAPTER_IX)"
+    case "$ADAPTER_IX" in
+        1)
+            # create links to the management bridge
+            PHYS_BRIDGE_NAME=br_mgmt
+            SUBNET_BASE="10.129"
+            ;;
+        2)
+            # create links to the tenant/data bridge
+            PHYS_BRIDGE_NAME=br_data
+            SUBNET_BASE="10.130"
+            ;;
+        *)  echo "ERROR: Invalid ADAPTER_IX \"$ADAPTER_IX\" specified"
+            exit
+    esac
+    SUBNET_PREFIX="${SUBNET_BASE}.${SUBNET_SEGMENT}"
+    # container index (container id per host), convert to 2 hex digits
+    CONTAINER_IP="${SUBNET_PREFIX}.${C_IXd}/${NETMASK_LEN}"
+    CONTAINER_MAC="${MAC_PREFIX}:${H_IXx}:${C_IXx}:${A_IX}"
+
+    # make links
+    fn_create_and_link_veth
+    fn_display_link_status
+done
+
+echo "You can remove the links created just now by simply removing the veth peer from the root netns with:"
+echo "    ip link delete $VETH_HOST"
+
+unlink $HOST_NETNS_ROOT/$NETNS_NAME