X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=resources%2Ftools%2Fscripts%2Ftopo_reservation.py;h=e7e1ff6bab4f93f6f46d397d3924bd203a8cc775;hb=db24a2e63a447599b5125da4b6f93f0f9184bfcc;hp=4b5ed2459c866054e90d4f402aadd7917f850b47;hpb=6721e7f09aa95bff6622068332a3f56afad9c87b;p=csit.git diff --git a/resources/tools/scripts/topo_reservation.py b/resources/tools/scripts/topo_reservation.py index 4b5ed2459c..e7e1ff6bab 100755 --- a/resources/tools/scripts/topo_reservation.py +++ b/resources/tools/scripts/topo_reservation.py @@ -1,6 +1,6 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 -# Copyright (c) 2016 Cisco and/or its affiliates. +# Copyright (c) 2019 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: @@ -13,53 +13,117 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""This script provides simple reservation mechanism to avoid - simultaneous use of nodes listed in topology file. - As source of truth is used DUT1 node from the topology file.""" +"""Script managing reservation and un-reservation of testbeds. + +This script provides simple reservation mechanism to avoid +simultaneous use of nodes listed in topology file. +As source of truth, TG node from the topology file is used. +""" import sys import argparse -from resources.libraries.python.ssh import SSH -from yaml import load +import yaml + +from resources.libraries.python.ssh import exec_cmd + RESERVATION_DIR = "/tmp/reservation_dir" +RESERVATION_NODE = "TG" + + +def diag_cmd(node, cmd): + """Execute cmd, print cmd and stdout, ignore stderr and rc; return None. + + :param node: Node object as parsed from topology file to execute cmd on. + :param cmd: Command to execute. + :type ssh: dict + :type cmd: str + """ + print('+ {cmd}'.format(cmd=cmd)) + _, stdout, _ = exec_cmd(node, cmd) + print(stdout) + def main(): + """Parse arguments, perform the action, write useful output, propagate RC. + + If the intended action is cancellation, reservation dir is deleted. + + If the intended action is reservation, the list is longer: + 1. List contents of reservation dir. + 2. List contents of test.url file in the dir. + 3. Create reservation dir. + 4. Touch file according to -r option. + From these 4 steps, 1 and 2 are performed always, their RC ignored. + RC of step 3 gives the overall result. + If the result is success, step 4 is executed without any output, + their RC is ignored. + + The "run tag" as a filename is useful for admins accessing the testbed + via a graphical terminal, which does not allow copying of text, + as they need less keypresses to identify the test run holding the testbed. + Also, the listing shows timestamps, which is useful for both audiences. + + This all assumes the target system accepts ssh connections. + If it does not, the caller probably wants to stop trying + to reserve this system. Therefore this script can return 3 different codes. + Return code 0 means the reservation was successful. + Return code 1 means the system is inaccessible (or similarly unsuitable). + Return code 2 means the system is accessible, but already reserved. + The reason unsuitable systems return 1 is because that is also the value + Python returns on encountering and unexcepted exception. + """ parser = argparse.ArgumentParser() parser.add_argument("-t", "--topo", required=True, help="Topology file") parser.add_argument("-c", "--cancel", help="Cancel reservation", action="store_true") + parser.add_argument("-r", "--runtag", required=False, default="Unknown", + help="Identifier for test run suitable as filename") args = parser.parse_args() - topology_file = args.topo - cancel_reservation = args.cancel - work_file = open(topology_file) - topology = load(work_file.read())['nodes'] + with open(args.topo, "r") as topo_file: + topology = yaml.load(topo_file.read())['nodes'] - #we are using DUT1 node because we expect DUT1 to be a linux host - #we don't use TG because we don't expect TG to be linux only host + # Even if TG is not guaranteed to be a Linux host, + # we are using it, because testing shows SSH access to DUT + # during test affects its performance (bursts of lost packets). try: - tg_node = topology["DUT1"] + node = topology[RESERVATION_NODE] except KeyError: - print "Topology file does not contain 'DUT1' node" + print("Topology file does not contain '{node}' node". + format(node=RESERVATION_NODE)) return 1 - ssh = SSH() - ssh.connect(tg_node) - - #For system reservation we use mkdir it is an atomic operation and we can - #store additional data (time, client_ID, ..) within reservation directory - if cancel_reservation: - ret, _, err = ssh.exec_command("rm -r {}".format(RESERVATION_DIR)) - else: - ret, _, err = ssh.exec_command("mkdir {}".format(RESERVATION_DIR)) - - if ret != 0: - print("{} unsuccessful:\n{}". - format(("Cancellation " if cancel_reservation else "Reservation"), - err)) - return ret + # For system reservation we use mkdir it is an atomic operation and we can + # store additional data (time, client_ID, ..) within reservation directory. + if args.cancel: + ret, _, err = exec_cmd(node, "rm -r {dir}".format(dir=RESERVATION_DIR)) + if ret: + print("Cancellation unsuccessful:\n{err}".format(err=err)) + return ret + # Before critical section, output can be outdated already. + print("Diagnostic commands:") + # -d and * are to supress "total ", see https://askubuntu.com/a/61190 + diag_cmd(node, "ls --full-time -cd '{dir}'/*".format(dir=RESERVATION_DIR)) + print("Attempting testbed reservation.") + # Entering critical section. + ret, _, _ = exec_cmd(node, "mkdir '{dir}'".format(dir=RESERVATION_DIR)) + # Critical section is over. + if ret: + _, stdo, _ = exec_cmd(node, "ls '{dir}'/*".format(dir=RESERVATION_DIR)) + print("Testbed already reserved by:\n{stdo}".format(stdo=stdo)) + return 2 + # Here the script knows it is the only owner of the testbed. + print("Reservation success, writing additional info to reservation dir.") + ret, _, err = exec_cmd( + node, "touch '{dir}/{runtag}'"\ + .format(dir=RESERVATION_DIR, runtag=args.runtag)) + if ret: + print("Writing test run info failed, but continuing anyway:\n{err}". + format(err=err)) + return 0 + if __name__ == "__main__": sys.exit(main())