Add lockfile support to artiq_devtool.

This commit is contained in:
whitequark 2017-11-26 15:17:35 +00:00
parent b89929565c
commit b5d098c6db
2 changed files with 46 additions and 13 deletions

View File

@ -6,6 +6,7 @@
import sys import sys
import argparse import argparse
import logging
import subprocess import subprocess
import socket import socket
import select import select
@ -22,20 +23,23 @@ def get_argparser():
verbosity_args(parser) verbosity_args(parser)
parser.add_argument("-H", "--host", metavar="HOST", parser.add_argument("-H", "--host", metavar="HOSTNAME",
type=str, default="lab.m-labs.hk", type=str, default="lab.m-labs.hk",
help="SSH host where the development board is located") help="SSH host where the development board is located")
parser.add_argument("-D", "--device", metavar="DEVICE", parser.add_argument("-D", "--device", metavar="HOSTNAME",
type=str, default="kc705.lab.m-labs.hk", type=str, default="kc705.lab.m-labs.hk",
help="address or domain corresponding to the development board") help="address or domain corresponding to the development board")
parser.add_argument("-s", "--serial", metavar="SERIAL", parser.add_argument("-s", "--serial", metavar="PATH",
type=str, default="/dev/ttyUSB_kc705", type=str, default="/dev/ttyUSB_kc705",
help="TTY device corresponding to the development board") help="TTY device corresponding to the development board")
parser.add_argument("-l", "--lockfile", metavar="PATH",
type=str, default="/run/boards/kc705",
help="The lockfile to be acquired for the duration of the script")
parser.add_argument("-t", "--target", metavar="TARGET", parser.add_argument("-t", "--target", metavar="TARGET",
type=str, default="kc705_dds", type=str, default="kc705_dds",
help="Target to build, one of: " help="Target to build, one of: "
"kc705_dds kc705_drtio_master kc705_drtio_satellite") "kc705_dds kc705_drtio_master kc705_drtio_satellite")
parser.add_argument("-c", "--config", metavar="TARGET_CFG", parser.add_argument("-c", "--config", metavar="PATH",
type=str, default="openocd-kc705.cfg", type=str, default="openocd-kc705.cfg",
help="OpenOCD configuration file corresponding to the development board") help="OpenOCD configuration file corresponding to the development board")
@ -50,6 +54,8 @@ def get_argparser():
def main(): def main():
args = get_argparser().parse_args() args = get_argparser().parse_args()
init_logger(args) init_logger(args)
if args.verbose == args.quiet == 0:
logging.getLogger().setLevel(logging.INFO)
if args.target == "kc705_dds" or args.target == "kc705_drtio_master": if args.target == "kc705_dds" or args.target == "kc705_drtio_master":
firmware = "runtime" firmware = "runtime"
@ -65,6 +71,27 @@ def main():
"firmware": firmware, "firmware": firmware,
} }
lock_acquired = False
def lock():
nonlocal lock_acquired
if not lock_acquired:
logger.info("Acquiring device lock")
flock = client.spawn_command("flock --verbose --nonblock {} /bin/sleep 86400"
.format(args.lockfile),
get_pty=True)
flock_file = flock.makefile('r')
while not lock_acquired:
line = flock_file.readline()
if not line:
break
logger.debug(line.rstrip())
if line.startswith("flock: executing"):
lock_acquired = True
elif line.startswith("flock: failed"):
logger.error("Failed to get lock")
sys.exit(1)
for action in args.actions: for action in args.actions:
if action == "build": if action == "build":
logger.info("Building firmware") logger.info("Building firmware")
@ -85,6 +112,8 @@ def main():
shutil.rmtree(target_dir) shutil.rmtree(target_dir)
elif action == "reset": elif action == "reset":
lock()
logger.info("Resetting device") logger.info("Resetting device")
client.run_command( client.run_command(
"{env} artiq_flash start" + "{env} artiq_flash start" +
@ -92,6 +121,8 @@ def main():
**substs) **substs)
elif action == "boot" or action == "boot+log": elif action == "boot" or action == "boot+log":
lock()
logger.info("Uploading firmware") logger.info("Uploading firmware")
client.get_sftp().put("/tmp/{target}/software/{firmware}/{firmware}.bin" client.get_sftp().put("/tmp/{target}/software/{firmware}/{firmware}.bin"
.format(target=args.target, firmware=firmware), .format(target=args.target, firmware=firmware),
@ -111,6 +142,8 @@ def main():
client.drain(flterm) client.drain(flterm)
elif action == "connect": elif action == "connect":
lock()
transport = client.get_transport() transport = client.get_transport()
def forwarder(local_stream, remote_stream): def forwarder(local_stream, remote_stream):
@ -141,10 +174,6 @@ def main():
local_stream, peer_addr = listener.accept() local_stream, peer_addr = listener.accept()
logger.info("Accepting %s:%s and opening SSH channel to %s:%s", logger.info("Accepting %s:%s and opening SSH channel to %s:%s",
*peer_addr, args.device, port) *peer_addr, args.device, port)
if client.get_transport() is None:
logger.error("Trying to open a channel before the transport is ready!")
continue
try: try:
remote_stream = \ remote_stream = \
transport.open_channel('direct-tcpip', (args.device, port), peer_addr) transport.open_channel('direct-tcpip', (args.device, port), peer_addr)
@ -156,12 +185,14 @@ def main():
name="forward-{}".format(port), daemon=True) name="forward-{}".format(port), daemon=True)
thread.start() thread.start()
for port in (1380, 1381, 1382, 1383): ports = [1380, 1381, 1382, 1383]
for port in ports:
thread = threading.Thread(target=listener, args=(port,), thread = threading.Thread(target=listener, args=(port,),
name="listen-{}".format(port), daemon=True) name="listen-{}".format(port), daemon=True)
thread.start() thread.start()
logger.info("Connecting to device") logger.info("Forwarding ports {} to core device and logs from core device"
.format(", ".join(map(str, ports))))
client.run_command( client.run_command(
"{env} python3 flterm.py {serial} --output-only", "{env} python3 flterm.py {serial} --output-only",
**substs) **substs)

View File

@ -269,10 +269,12 @@ class SSHClient:
def get_ssh(self): def get_ssh(self):
if self.ssh is None: if self.ssh is None:
import paramiko import paramiko
logging.getLogger("paramiko").setLevel(logging.WARNING)
self.ssh = paramiko.SSHClient() self.ssh = paramiko.SSHClient()
self.ssh.load_system_host_keys() self.ssh.load_system_host_keys()
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.ssh.connect(self.host) self.ssh.connect(self.host)
logger.debug("Connecting to {}".format(self.host))
return self.ssh return self.ssh
def get_transport(self): def get_transport(self):
@ -286,11 +288,11 @@ class SSHClient:
return self.sftp return self.sftp
def spawn_command(self, cmd, get_pty=False, **kws): def spawn_command(self, cmd, get_pty=False, **kws):
logger.info("Executing {}".format(cmd)) chan = self.get_transport().open_session()
chan = self.get_ssh().get_transport().open_session() chan.set_combine_stderr(True)
if get_pty: if get_pty:
chan.get_pty() chan.get_pty()
chan.set_combine_stderr(True) logger.debug("Executing {}".format(cmd))
chan.exec_command(cmd.format(tmp=self.tmp, **kws)) chan.exec_command(cmd.format(tmp=self.tmp, **kws))
return chan return chan