diff options
-rwxr-xr-x | bootstrap-vpp-honeycomb.sh | 46 | ||||
-rwxr-xr-x | resources/tools/download_hc_pkgs.sh | 49 | ||||
-rw-r--r-- | resources/tools/testbed-setup/playbooks/03-virl-post-install.yaml | 4 | ||||
-rwxr-xr-x | resources/tools/virl/bin/start-honeycomb-testcase | 375 |
4 files changed, 88 insertions, 386 deletions
diff --git a/bootstrap-vpp-honeycomb.sh b/bootstrap-vpp-honeycomb.sh index df5a5f97ab..ec7be15f2b 100755 --- a/bootstrap-vpp-honeycomb.sh +++ b/bootstrap-vpp-honeycomb.sh @@ -106,17 +106,49 @@ do fi done +# Download the latest VPP and HC .deb packages +echo Downloading packages... +bash ${SCRIPT_DIR}/resources/tools/download_hc_pkgs.sh + SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +VPP_DEBS=(*.deb) +echo ${VPP_DEBS[@]} +VIRL_DIR_LOC="/tmp" +VPP_DEBS_FULL=(${VPP_DEBS[@]}) + +# Prepend directory location at remote host to deb file list +for index in "${!VPP_DEBS_FULL[@]}"; do + VPP_DEBS_FULL[${index}]=${VIRL_DIR_LOC}/${VPP_DEBS_FULL[${index}]} +done + +echo "Updated file names: " ${VPP_DEBS_FULL[@]} + cat ${VIRL_PKEY} -# Copy start-honeycomb-testcase to VIRL -START_FILE="resources/tools/virl/bin/start-honeycomb-testcase" -DST_DIR_1="/home/jenkins-in/testcase-infra/bin/" -DST_DIR_2="/home/jenkins-in/bin/" +# Copy the files to VIRL hosts +DONE="" +for index in "${!VIRL_SERVER[@]}"; do + # Do not copy files in case they have already been copied to the VIRL host + [[ "${DONE[@]}" =~ "${VIRL_SERVER[${index}]}" ]] && copy=0 || copy=1 -scp ${SSH_OPTIONS} ${START_FILE} ${VIRL_USERNAME}@${VIRL_SERVER}:${DST_DIR_1} -scp ${SSH_OPTIONS} ${START_FILE} ${VIRL_USERNAME}@${VIRL_SERVER}:${DST_DIR_2} + if [ "${copy}" -eq "0" ]; then + echo "deb files have already been copied to the VIRL host ${VIRL_SERVER[${index}]}" + else + scp ${SSH_OPTIONS} *.deb \ + ${VIRL_USERNAME}@${VIRL_SERVER[${index}]}:${VIRL_DIR_LOC}/ + + result=$? + if [ "${result}" -ne "0" ]; then + echo "Failed to copy deb files to VIRL host ${VIRL_SERVER[${index}]}" + echo ${result} + exit ${result} + else + echo "deb files successfully copied to the VIRL host ${VIRL_SERVER[${index}]}" + fi + DONE+=(${VIRL_SERVER[${index}]}) + fi +done # Start a simulation on VIRL server echo "Starting simulation on VIRL server" @@ -128,7 +160,7 @@ function stop_virl_simulation { VIRL_SID=$(ssh ${SSH_OPTIONS} \ ${VIRL_USERNAME}@${VIRL_SERVER} \ - "start-honeycomb-testcase ${VIRL_TOPOLOGY} -r ${VIRL_RELEASE}") + "start-testcase ${VIRL_TOPOLOGY} -r ${VIRL_RELEASE}") retval=$? if [ "$?" -ne "0" ]; then echo "VIRL simulation start failed" diff --git a/resources/tools/download_hc_pkgs.sh b/resources/tools/download_hc_pkgs.sh new file mode 100755 index 0000000000..d9ddff8074 --- /dev/null +++ b/resources/tools/download_hc_pkgs.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +# Copyright (c) 2016 Cisco and/or its affiliates. +# 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. + +set -ex + +trap 'rm -f *.deb.md5; exit' EXIT +trap 'rm -f *.deb.md5;rm -f *.deb; exit' ERR + +URL="https://nexus.fd.io/service/local/artifact/maven/content" +VER="RELEASE" +REPO='fd.io.master.ubuntu.trusty.main' +GROUP="io.fd.vpp" +HC_GROUP="io.fd.honeycomb" +VPP_ARTIFACTS="vpp vpp-dbg vpp-dev vpp-dpdk-dev vpp-dpdk-dkms vpp-lib vpp-plugins" +HC_ARTIFACTS="honeycomb" +PACKAGE="deb deb.md5" +CLASS="deb" + +for ART in ${VPP_ARTIFACTS}; do + for PAC in $PACKAGE; do + curl "${URL}?r=${REPO}&g=${GROUP}&a=${ART}&p=${PAC}&v=${VER}&c=${CLASS}" -O -J || exit + done +done + +for ART in ${HC_ARTIFACTS}; do + for PAC in $PACKAGE; do + curl "${URL}?r=${REPO}&g=${HC_GROUP}&a=${ART}&p=${PAC}&v=${VER}&c=${CLASS}" -O -J || exit + done +done + +for FILE in *.deb; do + echo " "${FILE} >> ${FILE}.md5 +done + +for MD5FILE in *.md5; do + md5sum -c ${MD5FILE} || exit +done diff --git a/resources/tools/testbed-setup/playbooks/03-virl-post-install.yaml b/resources/tools/testbed-setup/playbooks/03-virl-post-install.yaml index b0e857ca57..59bcfe9d65 100644 --- a/resources/tools/testbed-setup/playbooks/03-virl-post-install.yaml +++ b/resources/tools/testbed-setup/playbooks/03-virl-post-install.yaml @@ -41,10 +41,6 @@ command: ln -sf /home/jenkins-in/testcase-infra/bin/start-testcase /home/jenkins-in/bin/start-testcase args: creates: /home/jenkins-in/bin/start-testcase - - name: Link start-honeycomb-testcase executable - command: ln -sf /home/jenkins-in/testcase-infra/bin/start-honeycomb-testcase /home/jenkins-in/bin/start-honeycomb-testcase - args: - creates: /home/jenkins-in/bin/start-honeycomb-testcase - name: Link stop-testcase executable command: ln -sf /home/jenkins-in/testcase-infra/bin/stop-testcase /home/jenkins-in/bin/stop-testcase args: diff --git a/resources/tools/virl/bin/start-honeycomb-testcase b/resources/tools/virl/bin/start-honeycomb-testcase deleted file mode 100755 index f38d1a783b..0000000000 --- a/resources/tools/virl/bin/start-honeycomb-testcase +++ /dev/null @@ -1,375 +0,0 @@ -#!/usr/bin/python - -# Copyright (c) 2016 Cisco and/or its affiliates. -# 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. - -__author__ = 'ckoester@cisco.com' - -import sys -import requests -import re -import os -import argparse -import tempfile -import shutil -import time -import paramiko -import netifaces - -# Commands needed to install VPP and Honeycomb from repository -inst_cmds = [ - 'sudo rm /etc/apt/sources.list.d/99fd.io.list || true', - 'echo "deb [trusted=yes] https://nexus.fd.io/content/repositories/' - 'fd.io.master.ubuntu.trusty.main/ ./" | ' - 'sudo tee -a /etc/apt/sources.list.d/99fd.io.list', - 'sudo apt-get -y update', - 'sudo apt-get -y install vpp vpp-dbg vpp-dev vpp-dpdk-dev vpp-dpdk-dkms ' - 'vpp-lib honeycomb' -] - -# -# Helper function to indent a text string -# -def indent(lines, amount, fillchar=' '): - padding = amount * fillchar - return padding + ('\n'+padding).join(lines.split('\n')) - -def perform_remote_command(ssh_client, cmd, verbosity): - """This function performs a command on the specified remote host using given - ssh client. Depending on the level of verbosity, it prints stdout and - stderr. - - :param ssh_client: SSH client used for communication. - :param cmd: Command to be performed on th rremote client. - :param verbosity: Verbosity level. - :type ssh_client: paramiko.SSHClient - :type cmd: str - :type verbosity: int - """ - - _, stdout, stderr = ssh_client.exec_command(cmd) - c_stdout = stdout.read() - c_stderr = stderr.read() - if verbosity >= 2: - print("DEBUG: Command output was:\n{0}".format(c_stdout)) - print("DEBUG: Command stderr was:\n{0}".format(c_stderr)) - -# -# Main function. -# FIXME: Right now, this is really coded like a shell script, as one big -# function executed in sequence. This should be broken down into multiple -# functions. -# -def main(): - # - # Get our default interface IP address. This will become the default - # value for the "NFS Server IP" option. - # - gws = netifaces.gateways() - addrs = netifaces.ifaddresses(gws['default'][netifaces.AF_INET][1]) - default_addr = addrs[netifaces.AF_INET][0]['addr'] - - # - # Verify CLI parameters and try to download our VPP image into a temporary - # file first - # - parser = argparse.ArgumentParser() - parser.add_argument("topology", help="the base topology to be started") - parser.add_argument("-k", "--keep", help="Keep (do not delete) the " + - "simulation in case of error", action='store_true') - parser.add_argument("-v", "--verbosity", action="count", default=0) - parser.add_argument("-nip", "--nfs-server-ip", help="NFS server (our) IP " + - "default is derived from routing table: " + - "{}".format(default_addr), default=default_addr) - parser.add_argument("-ns", "--nfs-scratch-directory", - help="Server location for NFS scratch diretory", - default="/nfs/scratch") - parser.add_argument("-nc", "--nfs-common-directory", - help="Server location for NFS common (read-only) " + - "directory", default="/nfs/common") - parser.add_argument("-wc", "--wait-count", - help="number of intervals to wait for simulation to " + - "be ready", type=int, default=24) - parser.add_argument("-wt", "--wait-time", - help="length of a single interval to wait for " + - "simulation to be ready", type=int, default=5) - parser.add_argument("-vip", "--virl-ip", - help="VIRL IP and Port (e.g. 127.0.0.1:19399)", - default="127.0.0.1:19399") - parser.add_argument("-u", "--username", help="VIRL username", - default="tb4-virl") - parser.add_argument("-p", "--password", help="VIRL password", - default="Cisco1234") - parser.add_argument("-su", "--ssh-user", help="SSH username", - default="cisco") - parser.add_argument("-spr", "--ssh-privkey", help="SSH private keyfile", - default="/home/jenkins-in/.ssh/id_rsa_virl") - parser.add_argument("-spu", "--ssh-pubkey", help="SSH public keyfile", - default="/home/jenkins-in/.ssh/id_rsa_virl.pub") - parser.add_argument("-r", "--release", help="VM disk image/release " + - "(ex. \"csit-ubuntu-14.04.4_2016-05-25_1.0\")", - default="csit-ubuntu-14.04.4_2016-05-25_1.0") - parser.add_argument("--topology-directory", help="Topology directory", - default="/home/jenkins-in/testcase-infra/topologies") - - args = parser.parse_args() - - # - # Check if topology and template exist - # - if args.verbosity >= 2: - print "DEBUG: Running with topology {}".format(args.topology) - - topology_virl_filename = os.path.join(args.topology_directory, - args.topology + ".virl") - topology_yaml_filename = os.path.join(args.topology_directory, - args.topology + ".yaml") - - if not os.path.isfile(topology_virl_filename): - print "ERROR: Topology VIRL file {} does not exist".\ - format(topology_virl_filename) - sys.exit(1) - if not os.path.isfile(topology_yaml_filename): - print "ERROR: Topology YAML file {} does not exist".\ - format(topology_yaml_filename) - sys.exit(1) - - # - # Start VIRL topology - # - if args.verbosity >= 1: - print "DEBUG: Starting VIRL topology" - temp_handle, temp_topology = tempfile.mkstemp() - with open(args.ssh_pubkey, 'r') as pubkey_file: - pub_key = pubkey_file.read().replace('\n', '') - with open(temp_topology, 'w') as new_file, \ - open(topology_virl_filename, 'r') as old_file: - for line in old_file: - line = line.replace(" - VIRL-USER-SSH-PUBLIC-KEY", " - "+pub_key) - line = line.replace("$$NFS_SERVER_SCRATCH$$", \ - args.nfs_server_ip+":"+args.nfs_scratch_directory) - line = line.replace("$$NFS_SERVER_COMMON$$", \ - args.nfs_server_ip+":"+args.nfs_common_directory) - line = line.replace("$$VM_IMAGE$$", "server-"+args.release) - new_file.write(line) - os.close(temp_handle) - - try: - new_file = open(temp_topology, 'rb') - headers = {'Content-Type': 'text/xml'} - req = requests.post('http://' + args.virl_ip + '/simengine/rest/launch', - headers=headers, - auth=(args.username, args.password), data=new_file) - if args.verbosity >= 2: - print "DEBUG: - Response Code {}".format(req.status_code) - new_file.close() - - except: - print "ERROR: Launching VIRL simulation - received invalid response" - print req - os.remove(temp_topology) - sys.exit(1) - - if req.status_code != 200: - print "ERROR: Launching VIRL simulation - received status other " + \ - "than 200 HTTP OK" - print "Status was: {} \n".format(req.status_code) - print "Response content was: " - print req.content - os.remove(temp_topology) - sys.exit(1) - - # If we got here, we had a good response. The response content is the - # session ID. - session_id = req.content - - # - # Create simulation scratch directory. Move topology file into that - # directory. Copy or move debian packages into that directory. - # - scratch_directory = os.path.join(args.nfs_scratch_directory, session_id) - os.mkdir(scratch_directory) - shutil.move(temp_topology, os.path.join(scratch_directory, - "virl_topology.virl")) - os.mkdir(os.path.join(scratch_directory, "vpp")) - - # - # Wait for simulation to become active - # - if args.verbosity >= 1: - print "DEBUG: Waiting for simulation to become active" - - sim_is_started = False - nodelist = [] - - count = args.wait_count - while (count > 0) and not sim_is_started: - time.sleep(args.wait_time) - count -= 1 - - req = requests.get('http://' + args.virl_ip + '/simengine/rest/nodes/' + - session_id, auth=(args.username, args.password)) - data = req.json() - - active = 0 - total = 0 - - # Flush the node list every time, keep the last one - nodelist = [] - - # Hosts are the keys of the inner dictionary - for key in data[session_id].keys(): - if data[session_id][key]['management-proxy'] == "self": - continue - nodelist.append(key) - total += 1 - if data[session_id][key]['state'] == "ACTIVE": - active += 1 - if args.verbosity >= 2: - print "DEBUG: - Attempt {} out of {}, total {} hosts, {} active".\ - format(args.wait_count-count, args.wait_count, total, active) - if active == total: - sim_is_started = True - - if not sim_is_started: - print "ERROR: Simulation started OK but devices never changed to " + \ - "ACTIVE state" - print "Last VIRL response:" - print data - if not args.keep: - shutil.rmtree(scratch_directory) - req = requests.get('http://' + args.virl_ip + - '/simengine/rest/stop/' + session_id, - auth=(args.username, args.password)) - - if args.verbosity >= 2: - print "DEBUG: Nodes: " + ", ".join(nodelist) - - # - # Fetch simulation's IPs and create files - # (ansible hosts file, topology YAML file) - # - req = requests.get('http://' + args.virl_ip + - '/simengine/rest/interfaces/' + session_id + - '?fetch-state=1', auth=(args.username, args.password)) - data = req.json() - - # Populate node addresses - nodeaddrs = {} - topology = {} - for key in nodelist: - nodetype = re.split('[0-9]', key)[0] - if not nodetype in nodeaddrs: - nodeaddrs[nodetype] = {} - nodeaddrs[nodetype][key] = re.split('\\/', \ - data[session_id][key]['management']['ip-address'])[0] - if args.verbosity >= 2: - print "DEBUG: Node {} is of type {} and has management IP {}".\ - format(key, nodetype, nodeaddrs[nodetype][key]) - - topology[key] = {} - for key2 in data[session_id][key]: - topology[key]["nic-"+key2] = data[session_id][key][key2] - if 'ip-address' in topology[key]["nic-"+key2]: - if topology[key]["nic-"+key2]['ip-address'] is not None: - topology[key]["nic-"+key2]['ip-addr'] = re.split('\\/', \ - topology[key]["nic-"+key2]['ip-address'])[0] - - # Write ansible file - ansiblehosts = open(os.path.join(scratch_directory, 'ansible-hosts'), 'w') - for key1 in nodeaddrs: - ansiblehosts.write("[{}]\n".format(key1)) - for key2 in nodeaddrs[key1]: - ansiblehosts.write("{} hostname={}\n".format(nodeaddrs[key1][key2], - key2)) - ansiblehosts.close() - - # Process topology YAML template - with open(args.ssh_privkey, 'r') as privkey_file: - priv_key = indent(privkey_file.read(), 6) - - with open(os.path.join(scratch_directory, "topology.yaml"), 'w') as \ - new_file, open(topology_yaml_filename, 'r') as old_file: - for line in old_file: - new_file.write(line.format(priv_key=priv_key, topology=topology)) - - # - # Wait for hosts to become reachable over SSH - # - if args.verbosity >= 1: - print "DEBUG: Waiting for hosts to become reachable using SSH" - - missing = -1 - count = args.wait_count - while (count > 0) and missing != 0: - time.sleep(args.wait_time) - count -= 1 - - missing = 0 - for key in nodelist: - if not os.path.exists(os.path.join(scratch_directory, key)): - missing += 1 - if args.verbosity >= 2: - print "DEBUG: - Attempt {} out of {}, waiting for {} hosts".\ - format(args.wait_count-count, args.wait_count, missing) - - if missing != 0: - print "ERROR: Simulation started OK but {} hosts ".format(missing) + \ - "never mounted their NFS directory" - if not args.keep: - shutil.rmtree(scratch_directory) - req = requests.get('http://' + args.virl_ip + - '/simengine/rest/stop/' + session_id, - auth=(args.username, args.password)) - - # - # Upgrade VPP - # - if args.verbosity >= 1: - print("DEBUG: Uprading VPP") - - for key1 in nodeaddrs: - if not key1 == 'tg': - for key2 in nodeaddrs[key1]: - ipaddr = nodeaddrs[key1][key2] - if args.verbosity >= 2: - print("DEBUG: Upgrading VPP on node {}".format(ipaddr)) - paramiko.util.log_to_file(os.path.join(scratch_directory, - "ssh.log")) - client = paramiko.SSHClient() - client.load_system_host_keys() - client.load_host_keys("/dev/null") - client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - client.connect(ipaddr, username=args.ssh_user, - key_filename=args.ssh_privkey) - for cmd in inst_cmds: - perform_remote_command(client, cmd, args.verbosity) - - # - # Write a file with timestamp to scratch directory. We can use this to track - # how long a simulation has been running. - # - with open(os.path.join(scratch_directory, 'start_time'), 'a') as \ - timestampfile: - timestampfile.write('{}\n'.format(int(time.time()))) - - # - # Declare victory - # - if args.verbosity >= 1: - print "SESSION ID: {}".format(session_id) - - print "{}".format(session_id) - -if __name__ == "__main__": - sys.exit(main()) |