X-Git-Url: https://gerrit.fd.io/r/gitweb?p=csit.git;a=blobdiff_plain;f=resources%2Ftools%2Fscripts%2Ftopo_reservation.py;h=c1b5c4658e544ae2674ea7a4b810b59aa5f4e9e6;hp=bf31918c7308abc8d1cff38840ddf624d8fa753d;hb=HEAD;hpb=e4091a25520e8cf1c62254df74b7ccf57f2ce1c7 diff --git a/resources/tools/scripts/topo_reservation.py b/resources/tools/scripts/topo_reservation.py index bf31918c73..f2d18bcafd 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 python3 -# Copyright (c) 2018 Cisco and/or its affiliates. +# Copyright (c) 2022 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: @@ -20,50 +20,114 @@ 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 sys +import yaml + +from resources.libraries.python.ssh import exec_cmd + + +RESERVATION_DIR = u"/tmp/reservation_dir" +RESERVATION_NODE = u"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 node: dict + :type cmd: str + """ + print(f"+ {cmd}") + _, stdout, _ = exec_cmd(node, cmd) + print(stdout) -RESERVATION_DIR = "/tmp/reservation_dir" 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(u"-t", u"--topo", required=True, help=u"Topology file") + parser.add_argument( + u"-c", u"--cancel", help=u"Cancel reservation", action=u"store_true" + ) + parser.add_argument( + u"-r", u"--runtag", required=False, default=u"Unknown", + help=u"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, u"rt") as topo_file: + topology = yaml.safe_load(topo_file.read())[u"nodes"] # 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["TG"] + node = topology[RESERVATION_NODE] except KeyError: - print "Topology file does not contain 'TG' node" + print(f"Topology file does not contain '{RESERVATION_NODE}' 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 args.cancel: + ret, _, err = exec_cmd(node, f"rm -r {RESERVATION_DIR}") + # If connection is refused, ret==None. + if ret != 0: + print(f"Cancellation unsuccessful:\n{err!r}") + return 1 + return 0 + # Before critical section, output can be outdated already. + print(u"Diagnostic commands:") + # -d and * are to suppress "total ", see https://askubuntu.com/a/61190 + diag_cmd(node, f"ls --full-time -cd '{RESERVATION_DIR}'/*") + print(u"Attempting testbed reservation.") + # Entering critical section. + ret, _, _ = exec_cmd(node, f"mkdir '{RESERVATION_DIR}'") + # Critical section is over. + if ret is None: + print(u"Failed to connect to testbed.") + return 1 if ret != 0: - print("{} unsuccessful:\n{}". - format(("Cancellation " if cancel_reservation else "Reservation"), - err)) - return ret + _, stdo, _ = exec_cmd(node, f"ls '{RESERVATION_DIR}'/*") + print(f"Testbed already reserved by:\n{stdo}") + return 2 + # Here the script knows it is the only owner of the testbed. + print(u"Reservation success, writing additional info to reservation dir.") + ret, _, err = exec_cmd( + node, f"touch '{RESERVATION_DIR}/{args.runtag}'") + if ret != 0: + print(f"Writing test run info failed, but continuing anyway:\n{err!r}") + return 0 + -if __name__ == "__main__": +if __name__ == u"__main__": sys.exit(main())