import re
import json
import tabulate as tab
from texpect import *

KEY_DOWN = b'\x1b[B'

column_labels = {"atse2be":("channel", "name", "port", "source", "unit", "value"), 
                               "fpga_load":("chip", "class", "currentVersion", "pciBusList", "targetVersion", "unit", "updatePolicy", "updateStatus", "variant"),
                               "fpga2fp":("chip", "name", "sourcePort", "unit", "value"),
                               "fpga2fpga":("channel", "chip", "name", "port", "unit", "value"),
                               "vqf2qat":("chip", "name", "unit", "value"),
                               "vqf2bksim":("backplane", "name", "path", "port", "source", "value"),
                               "fpga_sdkattach":("bitfileType", "chip", "unit"),
                               "fpga_driverload":("actionPerformed", "isLoaded"),
                               "xcvr_setup":("chip", "enabled", "isFaulted", "isI2cReady", "isOpticPresent", "optic", "opticHighPowerEnabled", "port", "speed"),
                               "fpga_pcie":("chip", "name", "port", "unit", "value")
                               }

##########################################################################
###                      begin script                                  ###
##########################################################################

blade = DUT("10.238.173.12", 7017, "jwhite", "default", "default", "KingstonDataTraveler 3.0PMAP", verbose=True)
blade.openConnection()

steps1 = (
	( b"root@", b"\r", b"reboot\r", 60 ),
	( b"Press <c>", b"", b"b", 200 ),
	( b"---/", b"", b"", 60 )
	)

for z in steps1: # Reboot DUT, get to the boot selection menu.
    try:
        match = texpect(blade.ssh_channel, z[0], z[1], z[2], timeout=z[3], verbose=blade.verbose)
        match.run()
    except TimeoutError:
        print(f"Timed out at steps1 step {z}")
        blade.closeConnection()
        exit()

boot_devs = re.findall(b'UEFI:.*?\\|',match.buffer)
boot_devs_desired = 0
for x, y in enumerate(boot_devs):
    if blade.boot_device in str(y): 
        boot_devs_desired = x
        break

if boot_devs_desired:
    print("Pressing down arrow to navigate to desired device...")
    for y in range(boot_devs_desired):
        blade.ssh_channel.send(KEY_DOWN)
        time.sleep(0.5)

steps2 = (
	( b"change the", "\r", b"\r", 60 ),
	( b"Writing obj", b"", b"", 120 ),
	( b"Press return", b"", b"\r", 300),
	( b"login:", b"", b"root\r", 300),
	( b"Password:", b"", b"default\r", 10),
	( b"UNIX password:", b"", b"default\r", 10),
	( b"New password:", b"", b"f5site02\r", 10),
	( b"Retype new password:", b"", b"f5site02\r", 10),
	( b"root@", b"", b"passwd root\r", 10),
	( b"New password:", b"", f"{blade.root_pass}\r", 10),
	( b"Retype new password:", b"", f"{blade.root_pass}\r", 10),
	( b"root@", b"", b'echo "QTP" > /etc/sysconfig/identity/blade\r', 10),
	( b"root@", b"", b"systemctl restart platform-services-deployment.service\r", 10),
	( b"c6xx 0000:", b"", b"dhclient\r", 500),
    ( b"root@", b"\r", f"""date -s '{time.strftime("%B %d T&")}'""", 10),
    ( b"root@", b"\r", b"", 10)
	)

for w in steps2: # Perform OS install, configure in standalone mode, set password and date
    try:
        match = texpect(blade.ssh_channel, w[0], w[1], w[2], timeout=w[3], verbose=blade.verbose)
        match.run()
    except TimeoutError:
        print(f"Timed out at steps2 step {w}")
        blade.closeConnection()
        exit()

try_again=1
match = texpect(blade.ssh_channel, b"Setup complete", b"python <(curl -fkL https://artifactory.f5net.com/artifactory/systems-eng/velocity-dev-env/master/deploy/v6y_setup.py)\r", b"", timeout=360, complications=[b"Traceback"], verbose=blade.verbose)
while try_again:
    try:
        if not match.run():
            try_again = 0
            print("Got containers!")
    except TimeoutError:
        pass
    except CaughtComplication:
        print("Failed to get containers. Waiting 20 seconds to try again.")
        time.sleep(20)
time.sleep(5) # A little nap for good measure

match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a localhost:9001 GET:lop/seeprom-platform\r", b"", verbose=blade.verbose)
match.run() # Get SEEPROM contents
for x in match.buffer[match.buffer.index(b"PcaPartNumber"):match.buffer.index(b"count :")].splitlines():
    y = re.findall(r'([a-zA-Z]+) +(.+)$', x.decode())
    if y:
        y = y.pop()
        blade.info[y[0]] = y[1]
blade.info = [x for x in blade.info.items()]

match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a localhost:9002 PUT:firmware/fpga chip=atse class=io unit=0\r", b"", verbose=blade.verbose)
match.run()
blade.setupresults.update({'atse0_load': [tuple([str(x) for x in list(json.loads(match.buffer[match.buffer.index(b"{"):match.buffer.index(b"}")+1].decode()).values())])]})
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a localhost:9002 PUT:firmware/fpga chip=atse class=io unit=1\r", b"", verbose=blade.verbose)
match.run()
blade.setupresults.update({'atse1_load': [tuple([str(x) for x in list(json.loads(match.buffer[match.buffer.index(b"{"):match.buffer.index(b"}")+1].decode()).values())])]})
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:fpga/sdk-attach bitfileType=io chip=atse unit=0\r", b"", verbose=blade.verbose)
match.run()
blade.setupresults['atse0_sdkattach'] = re.findall(r"[bitfileType|chip|unit]\s+:\s([a-z0-9]+)", match.buffer[match.buffer.index(b"bitfileType"):match.buffer.index(b"Job:")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:fpga/sdk-attach bitfileType=io chip=atse unit=1\r", b"", verbose=blade.verbose)
match.run()
blade.setupresults['atse1_sdkattach'] = re.findall(r"[bitfileType|chip|unit]\s+:\s([a-z0-9]+)", match.buffer[match.buffer.index(b"bitfileType"):match.buffer.index(b"Job:")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a fwu PUT:firmware/fpga chip=tam class=io updatePolicy=always variant=100g400g\r", b"", verbose=blade.verbose)
match.run()
blade.setupresults.update({'tam_load': [tuple([str(x) for x in list(json.loads(match.buffer[match.buffer.index(b"{"):match.buffer.index(b"}")+1].decode()).values())])]})
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:fpga/sdk-attach bitfileType=io chip=tam\r", b"", verbose=blade.verbose)
match.run()
blade.setupresults['tam_sdkattach'] = re.findall(r"[bitfileType|chip|unit]\s+:\s([a-z0-9]+)", match.buffer[match.buffer.index(b"bitfileType"):match.buffer.index(b"Job:")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:fpga/io/fp/xcvr-power-state skipOpticMgrTuning=False -j=0\r", b"", verbose=blade.verbose, timeout=120)
match.run()
blade.setupresults['xcvr_setup'] = re.findall(r"(tam)\s+(True|False)\s+(True|False)\s+(True|False)\s+(True|False)\s+(OPT.*)\s+(True|False)\s+(qsfp[01])\s+(\d{1,3}Gb)", match.buffer[match.buffer.index(b"chip"):match.buffer.index(b"count")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a fwu PUT:firmware/fpga chip=vqf bitfileType=io\r", b"", verbose=blade.verbose)
match.run()
blade.setupresults.update({'vqf_load': [tuple([str(x) for x in list(json.loads(match.buffer[match.buffer.index(b"{"):match.buffer.index(b"}")+1].decode()).values())])]})
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:fpga/sdk-attach bitfileType=io chip=vqf\r", b"", verbose=blade.verbose)
match.run()
blade.setupresults['vqf_sdkattach'] = re.findall(r"[bitfileType|chip|unit]\s+:\s([a-z0-9]+)", match.buffer[match.buffer.index(b"bitfileType"):match.buffer.index(b"Job:")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag PUT:vqf/io/driver\r", b"", verbose=blade.verbose)
match.run()
blade.setupresults['vqf_driverload'] = re.findall(r"[actionPerformed|isLoaded]\s+:\s(True|False)", match.buffer[match.buffer.index(b"Calling"):match.buffer.index(b"Job")].decode())
time.sleep(1)


match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:fpga/io/fpga2fp port=fp1.0 duration=120\r", b"", timeout=180, verbose=blade.verbose)
match.run()
blade.testresults['tam2fp_fp1.0'] = re.findall(r"(atse|vqf|tam|nse) +([A-Z0-9_]+) +(fp\d\.\d) +(\d+) +(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"chip"):match.buffer.index(b"count :")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:fpga/io/fpga2fp port=fp2.0 duration=120\r", b"", timeout=180, verbose=blade.verbose)
match.run()
blade.testresults['tam2fp_fp2.0'] = re.findall(r"(atse|vqf|tam|nse) +([A-Z0-9_]+) +(fp\d\.\d) +(\d+) +(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"chip"):match.buffer.index(b"count :")].decode())
time.sleep(1)


match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:fpga/io/prbs/atse2vqf unit=0 port=vqf0\r", b"", timeout=90, verbose=blade.verbose)
match.run()
blade.testresults['atse2vqf_atse0_vqf0'] = re.findall(r"(\d)\s+(atse|vqf)\s+(\w+)\s+(\w+\d\.?\d?)\s+(\d)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"channel"):match.buffer.index(b"count :")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:fpga/io/prbs/atse2vqf unit=0 port=vqf1\r", b"", timeout=90, verbose=blade.verbose)
match.run()
blade.testresults['atse2vqf_atse0_vqf1'] = re.findall(r"(\d)\s+(atse|vqf)\s+(\w+)\s+(\w+\d\.?\d?)\s+(\d)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"channel"):match.buffer.index(b"count :")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:fpga/io/prbs/atse2vqf unit=1 port=vqf0\r", b"", timeout=90, verbose=blade.verbose)
match.run()
blade.testresults['atse2vqf_atse1_vqf0'] = re.findall(r"(\d)\s+(atse|vqf)\s+(\w+)\s+(\w+\d\.?\d?)\s+(\d)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"channel"):match.buffer.index(b"count :")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:fpga/io/prbs/atse2vqf unit=1 port=vqf1\r", b"", timeout=90, verbose=blade.verbose)
match.run()
blade.testresults['atse2vqf_atse1_vqf1'] = re.findall(r"(\d)\s+(atse|vqf)\s+(\w+)\s+(\w+\d\.?\d?)\s+(\d)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"channel"):match.buffer.index(b"count :")].decode())
time.sleep(1)


match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:fpga/io/prbs/tam2vqf unit=0 port=vqf0\r", b"", timeout=90, verbose=blade.verbose)
match.run()
blade.testresults['tam2vqf_vqf0'] = re.findall(r"(\d)\s+(tam|vqf)\s+(\w+)\s+(\w+\d\.?\d?)\s+(\d)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"channel"):match.buffer.index(b"count :")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:fpga/io/prbs/tam2vqf unit=0 port=vqf1\r", b"", timeout=90, verbose=blade.verbose)
match.run()
blade.testresults['tam2vqf_vqf1'] = re.findall(r"(\d)\s+(tam|vqf)\s+(\w+)\s+(\w+\d\.?\d?)\s+(\d)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"channel"):match.buffer.index(b"count :")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:fpga/io/prbs/tam2vqf unit=0 port=vqf2\r", b"", timeout=90, verbose=blade.verbose)
match.run()
blade.testresults['tam2vqf_vqf2'] = re.findall(r"(\d)\s+(tam|vqf)\s+(\w+)\s+(\w+\d\.?\d?)\s+(\d)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"channel"):match.buffer.index(b"count :")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:fpga/io/prbs/tam2vqf unit=0 port=vqf3\r", b"", timeout=90, verbose=blade.verbose)
match.run()
blade.testresults['tam2vqf_vqf3'] = re.findall(r"(\d)\s+(tam|vqf)\s+(\w+)\s+(\w+\d\.?\d?)\s+(\d)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"channel"):match.buffer.index(b"count :")].decode())
time.sleep(1)


match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:vqf/io/prbs/vqf2bksim unit=0 port=bp0\r", b"", timeout=90, verbose=blade.verbose)
match.run()
blade.testresults['vqf2bksim_bp0'] = re.findall(r"(\d)\s+(vqf)\s+(\w+)\s+(bp\d)\s+(\d)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"channel"):match.buffer.index(b"count :")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:vqf/io/prbs/vqf2bksim unit=0 port=bp1\r", b"", timeout=90, verbose=blade.verbose)
match.run()
blade.testresults['vqf2bksim_bp1'] = re.findall(r"(\d)\s+(vqf)\s+(\w+)\s+(bp\d)\s+(\d)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"channel"):match.buffer.index(b"count :")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:vqf/io/prbs/vqf2bksim unit=0 port=bp2\r", b"", timeout=90, verbose=blade.verbose)
match.run()
blade.testresults['vqf2bksim_bp2'] = re.findall(r"(\d)\s+(vqf)\s+(\w+)\s+(bp\d)\s+(\d)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"channel"):match.buffer.index(b"count :")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:vqf/io/prbs/vqf2bksim unit=0 port=bp3\r", b"", timeout=90, verbose=blade.verbose)
match.run()
blade.testresults['vqf2bksim_bp3'] = re.findall(r"(\d)\s+(vqf)\s+(\w+)\s+(bp\d)\s+(\d)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"channel"):match.buffer.index(b"count :")].decode())
time.sleep(1)

match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:vqf/io/pcie2cpu unit=0 port=pcie0  duration=60\r", b"", timeout=90, verbose=blade.verbose)
match.run()
blade.testresults['pcie2cpu_vqf'] = re.findall(r"(vqf)\s+(\w+)\s+(pcie\d)\s+(\d)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"chip"):match.buffer.index(b"count :")].decode())
time.sleep(1)

match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:atse/io/pcie2cpu unit=0 port=pcie0 duration=60\r", b"", timeout=90, verbose=blade.verbose)
match.run()
blade.testresults['pcie2cpu_atse0_pcie0'] = re.findall(r"(atse)\s+(\w+)\s+(pcie\d)\s+(\d+)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"chip"):match.buffer.index(b"count :")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:atse/io/pcie2cpu unit=0 port=pcie1 duration=60\r", b"", timeout=90, verbose=blade.verbose)
match.run()
blade.testresults['pcie2cpu_atse0_pcie1'] = re.findall(r"(atse)\s+(\w+)\s+(pcie\d)\s+(\d+)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"chip"):match.buffer.index(b"count :")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:atse/io/pcie2cpu unit=0 port=pcie2 duration=60\r", b"", timeout=90, verbose=blade.verbose)
match.run()
blade.testresults['pcie2cpu_atse0_pcie2'] = re.findall(r"(atse)\s+(\w+)\s+(pcie\d)\s+(\d+)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"chip"):match.buffer.index(b"count :")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:atse/io/pcie2cpu unit=0 port=pcie3 duration=60\r", b"", timeout=90, verbose=blade.verbose)
match.run()
blade.testresults['pcie2cpu_atse0_pcie3'] = re.findall(r"(atse)\s+(\w+)\s+(pcie\d)\s+(\d+)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"chip"):match.buffer.index(b"count :")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:atse/io/pcie2cpu unit=1 port=pcie0 duration=60\r", b"", timeout=90, verbose=blade.verbose)
match.run()
blade.testresults['pcie2cpu_atse1_pcie0'] = re.findall(r"(atse)\s+(\w+)\s+(pcie\d)\s+(\d+)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"chip"):match.buffer.index(b"count :")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:atse/io/pcie2cpu unit=1 port=pcie1 duration=60\r", b"", timeout=90, verbose=blade.verbose)
match.run()
blade.testresults['pcie2cpu_atse1_pcie1'] = re.findall(r"(atse)\s+(\w+)\s+(pcie\d)\s+(\d+)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"chip"):match.buffer.index(b"count :")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:atse/io/pcie2cpu unit=1 port=pcie2 duration=60\r", b"", timeout=90, verbose=blade.verbose)
match.run()
blade.testresults['pcie2cpu_atse1_pcie2'] = re.findall(r"(atse)\s+(\w+)\s+(pcie\d)\s+(\d+)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"chip"):match.buffer.index(b"count :")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:atse/io/pcie2cpu unit=1 port=pcie3 duration=60\r", b"", timeout=90, verbose=blade.verbose)
match.run()
blade.testresults['pcie2cpu_atse1_pcie3'] = re.findall(r"(atse)\s+(\w+)\s+(pcie\d)\s+(\d+)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"chip"):match.buffer.index(b"count :")].decode())
time.sleep(1)

match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:atse/io/prbs/atse2be unit=0\r", b"", timeout=160, verbose=blade.verbose)
match.run()
blade.testresults['atse2be_atse0'] = re.findall(r"(\d)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\d)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"channel"):match.buffer.index(b"count :")].decode())
time.sleep(1)
match = texpect(blade.ssh_channel, b"Exec Time", b"aclient -a diag POST:atse/io/prbs/atse2be unit=1\r", b"", timeout=160, verbose=blade.verbose)
match.run()
blade.testresults['atse2be_atse1'] = re.findall(r"(\d)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\d)\s+(\d+\.?\d*e?\+?\d*)", match.buffer[match.buffer.index(b"channel"):match.buffer.index(b"count :")].decode())
time.sleep(1)


blade_serial = ""
for x in blade.info:
    if "EndItemSerialNumber" in x:
        blade_serial = x[1]

with open(f"{blade_serial}_results.txt", 'w+') as save_file:
    print(f"Saving to file {blade_serial}_results.txt")
    save_file.write(f"{blade_serial} Test Results\n")
    save_file.write("Blade Information:\n")
    save_file.write(tab.tabulate(blade.info, tablefmt="outline"))
    save_file.write("\nATSE0 Load:\n")
    save_file.write(tab.tabulate(blade.setupresults['atse0_load'], headers=column_labels['fpga_load'], tablefmt="outline"))
    save_file.write("\nATSE0 SDK Attach:\n")
    save_file.write(tab.tabulate([blade.setupresults['atse0_sdkattach']], headers=column_labels['fpga_sdkattach'], tablefmt="outline"))
    save_file.write("\nATSE1 Load:\n")
    save_file.write(tab.tabulate(blade.setupresults['atse1_load'], headers=column_labels['fpga_load'], tablefmt="outline"))
    save_file.write("\nATSE1 SDK Attach:\n")
    save_file.write(tab.tabulate([blade.setupresults['atse1_sdkattach']], headers=column_labels['fpga_sdkattach'], tablefmt="outline"))
    save_file.write("\nTAM Load:\n")
    save_file.write(tab.tabulate(blade.setupresults['tam_load'], headers=column_labels['fpga_load'], tablefmt="outline"))
    save_file.write("\nTAM SDK Attach:\n")
    save_file.write(tab.tabulate([blade.setupresults['tam_sdkattach']], headers=column_labels['fpga_sdkattach'], tablefmt="outline"))
    save_file.write("\nTransceiver Setup:\n")
    save_file.write(tab.tabulate(blade.setupresults['xcvr_setup'], headers=column_labels['xcvr_setup'], tablefmt="outline"))
    save_file.write("\nVQF Load:\n")
    save_file.write(tab.tabulate(blade.setupresults['vqf_load'], headers=column_labels['fpga_load'], tablefmt="outline"))
    save_file.write("\nVQF SDK Attach:\n")
    save_file.write(tab.tabulate([blade.setupresults['vqf_sdkattach']], headers=column_labels['fpga_sdkattach'], tablefmt="outline"))
    save_file.write("\nVQF Driver Load:\n")
    save_file.write(tab.tabulate([blade.setupresults['vqf_driverload']], headers=column_labels['fpga_driverload'], tablefmt="outline"))
    save_file.write("\nTAM to FP:\n")
    save_file.write(tab.tabulate(blade.testresults['tam2fp_fp1.0']+blade.testresults['tam2fp_fp2.0'], headers=column_labels['fpga2fp'], tablefmt="outline"))
    save_file.write("\nATSE to VQF:\n")
    save_file.write(tab.tabulate(blade.testresults['atse2vqf_atse0_vqf0']+blade.testresults['atse2vqf_atse0_vqf1']+blade.testresults['atse2vqf_atse1_vqf0']+blade.testresults['atse2vqf_atse1_vqf1'], headers=column_labels['fpga2fpga'], tablefmt="outline"))
    save_file.write("\nTAM to VQF:\n")
    save_file.write(tab.tabulate(blade.testresults['tam2vqf_vqf0']+blade.testresults['tam2vqf_vqf1']+blade.testresults['tam2vqf_vqf2']+blade.testresults['tam2vqf_vqf3'], headers=column_labels['fpga2fpga'], tablefmt="outline"))
    save_file.write("\nVQF to Backplane Simulator:\n")
    save_file.write(tab.tabulate(blade.testresults['vqf2bksim_bp0']+blade.testresults['vqf2bksim_bp1']+blade.testresults['vqf2bksim_bp2']+blade.testresults['vqf2bksim_bp3'], headers=column_labels['vqf2bksim'], tablefmt="outline"))
    save_file.write("\nVQF PCIe to CPU:\n")
    save_file.write(tab.tabulate(blade.testresults['pcie2cpu_vqf'], headers=column_labels['fpga_pcie'], tablefmt="outline"))
    save_file.write("\nATSE0 PCIe to CPU:\n")
    save_file.write(tab.tabulate(blade.testresults['pcie2cpu_atse0_pcie0']+blade.testresults['pcie2cpu_atse0_pcie1']+blade.testresults['pcie2cpu_atse0_pcie2']+blade.testresults['pcie2cpu_atse0_pcie3'], headers=column_labels['fpga_pcie'], tablefmt="outline"))
    save_file.write("\nATSE1 PCIe to CPU:\n")
    save_file.write(tab.tabulate(blade.testresults['pcie2cpu_atse1_pcie0']+blade.testresults['pcie2cpu_atse1_pcie1']+blade.testresults['pcie2cpu_atse1_pcie2']+blade.testresults['pcie2cpu_atse1_pcie3'], headers=column_labels['fpga_pcie'], tablefmt="outline"))
    save_file.write("\nATSEs to BEs:\n")
    save_file.write(tab.tabulate(blade.testresults['atse2be_atse0']+blade.testresults['atse2be_atse1'], headers=column_labels['atse2be'], tablefmt="outline"))

blade.closeConnection()