forked from M-Labs/artiq
artiq_pcap: implement.
This commit is contained in:
parent
69fa9b38e0
commit
816ec6c52f
|
@ -6,6 +6,7 @@ __pycache__/
|
||||||
*.bin
|
*.bin
|
||||||
*.elf
|
*.elf
|
||||||
*.fbi
|
*.fbi
|
||||||
|
*.pcap
|
||||||
.ipynb_checkpoints
|
.ipynb_checkpoints
|
||||||
/doc/manual/_build
|
/doc/manual/_build
|
||||||
/build
|
/build
|
||||||
|
|
|
@ -10,12 +10,10 @@ import subprocess
|
||||||
import socket
|
import socket
|
||||||
import select
|
import select
|
||||||
import threading
|
import threading
|
||||||
import paramiko
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from artiq.tools import verbosity_args, init_logger, logger
|
from artiq.tools import verbosity_args, init_logger, logger, SSHClient
|
||||||
from random import Random
|
|
||||||
|
|
||||||
|
|
||||||
def get_argparser():
|
def get_argparser():
|
||||||
|
@ -60,43 +58,13 @@ def main():
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError("unknown target {}".format(args.target))
|
raise NotImplementedError("unknown target {}".format(args.target))
|
||||||
|
|
||||||
ssh = None
|
client = SSHClient(args.host)
|
||||||
def get_ssh():
|
substs = {
|
||||||
nonlocal ssh
|
"env": "bash -c 'export PATH=$HOME/miniconda/bin:$PATH; exec $0 $*' ",
|
||||||
if ssh is not None:
|
"serial": args.serial,
|
||||||
return ssh
|
"ip": args.ip,
|
||||||
ssh = paramiko.SSHClient()
|
"firmware": firmware,
|
||||||
ssh.load_system_host_keys()
|
}
|
||||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
||||||
ssh.connect(args.host)
|
|
||||||
return ssh
|
|
||||||
|
|
||||||
sftp = None
|
|
||||||
def get_sftp():
|
|
||||||
nonlocal sftp
|
|
||||||
if sftp is not None:
|
|
||||||
return sftp
|
|
||||||
sftp = get_ssh().open_sftp()
|
|
||||||
return sftp
|
|
||||||
|
|
||||||
rng = Random()
|
|
||||||
tmp = "artiq" + "".join([rng.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ") for _ in range(6)])
|
|
||||||
env = "bash -c 'export PATH=$HOME/miniconda/bin:$PATH; exec $0 $*' "
|
|
||||||
|
|
||||||
def run_command(cmd, **kws):
|
|
||||||
logger.info("Executing {}".format(cmd))
|
|
||||||
chan = get_ssh().get_transport().open_session()
|
|
||||||
chan.set_combine_stderr(True)
|
|
||||||
chan.exec_command(cmd.format(tmp=tmp, env=env, serial=args.serial, ip=args.ip,
|
|
||||||
firmware=firmware, **kws))
|
|
||||||
return chan.makefile()
|
|
||||||
|
|
||||||
def drain(chan):
|
|
||||||
while True:
|
|
||||||
char = chan.read(1)
|
|
||||||
if char == b"":
|
|
||||||
break
|
|
||||||
sys.stderr.write(char.decode("utf-8", errors='replace'))
|
|
||||||
|
|
||||||
for action in args.actions:
|
for action in args.actions:
|
||||||
if action == "build":
|
if action == "build":
|
||||||
|
@ -119,27 +87,29 @@ def main():
|
||||||
|
|
||||||
elif action == "reset":
|
elif action == "reset":
|
||||||
logger.info("Resetting device")
|
logger.info("Resetting device")
|
||||||
artiq_flash = run_command(
|
client.run_command(
|
||||||
"{env} artiq_flash start" +
|
"{env} artiq_flash start" +
|
||||||
(" --target-file " + args.config if args.config else ""))
|
(" --target-file " + args.config if args.config else ""),
|
||||||
drain(artiq_flash)
|
**substs)
|
||||||
|
|
||||||
elif action == "boot" or action == "boot+log":
|
elif action == "boot" or action == "boot+log":
|
||||||
logger.info("Uploading firmware")
|
logger.info("Uploading firmware")
|
||||||
get_sftp().mkdir("/tmp/{tmp}".format(tmp=tmp))
|
client.get_sftp().put("/tmp/{target}/software/{firmware}/{firmware}.bin"
|
||||||
get_sftp().put("/tmp/{target}/software/{firmware}/{firmware}.bin"
|
|
||||||
.format(target=args.target, firmware=firmware),
|
.format(target=args.target, firmware=firmware),
|
||||||
"/tmp/{tmp}/{firmware}.bin".format(tmp=tmp, firmware=firmware))
|
"{tmp}/{firmware}.bin"
|
||||||
|
.format(tmp=client.tmp, firmware=firmware))
|
||||||
|
|
||||||
logger.info("Booting firmware")
|
logger.info("Booting firmware")
|
||||||
flterm = run_command(
|
flterm = client.spawn_command(
|
||||||
"{env} python3 flterm.py {serial} " +
|
"{env} python3 flterm.py {serial} " +
|
||||||
"--kernel /tmp/{tmp}/{firmware}.bin " +
|
"--kernel {tmp}/{firmware}.bin " +
|
||||||
("--upload-only" if action == "boot" else "--output-only"))
|
("--upload-only" if action == "boot" else "--output-only"),
|
||||||
artiq_flash = run_command(
|
**substs)
|
||||||
|
artiq_flash = client.spawn_command(
|
||||||
"{env} artiq_flash start" +
|
"{env} artiq_flash start" +
|
||||||
(" --target-file " + args.config if args.config else ""))
|
(" --target-file " + args.config if args.config else ""),
|
||||||
drain(flterm)
|
**substs)
|
||||||
|
client.drain(flterm)
|
||||||
|
|
||||||
elif action == "connect":
|
elif action == "connect":
|
||||||
def forwarder(port):
|
def forwarder(port):
|
||||||
|
@ -151,12 +121,12 @@ 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.ip, port)
|
*peer_addr, args.ip, port)
|
||||||
if get_ssh().get_transport() is None:
|
if client.get_transport() is None:
|
||||||
logger.error("Trying to open a channel before the transport is ready!")
|
logger.error("Trying to open a channel before the transport is ready!")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
remote_stream = get_ssh().get_transport() \
|
remote_stream = client.get_transport() \
|
||||||
.open_channel('direct-tcpip', (args.ip, port), peer_addr)
|
.open_channel('direct-tcpip', (args.ip, port), peer_addr)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception("Cannot open channel on port %s", port)
|
logger.exception("Cannot open channel on port %s", port)
|
||||||
|
@ -186,9 +156,9 @@ def main():
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
logger.info("Connecting to device")
|
logger.info("Connecting to device")
|
||||||
flterm = run_command(
|
client.run_command(
|
||||||
"{env} python3 flterm.py {serial} --output-only")
|
"{env} python3 flterm.py {serial} --output-only",
|
||||||
drain(flterm)
|
**substs)
|
||||||
|
|
||||||
elif action == "hotswap":
|
elif action == "hotswap":
|
||||||
logger.info("Hotswapping firmware")
|
logger.info("Hotswapping firmware")
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# This script makes the following assumptions:
|
||||||
|
# * tcpdump has CAP_NET_RAW capabilities set
|
||||||
|
# use # setcap cap_net_raw+eip /usr/sbin/tcpdump
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from artiq.tools import verbosity_args, init_logger, logger, SSHClient
|
||||||
|
|
||||||
|
|
||||||
|
def get_argparser():
|
||||||
|
parser = argparse.ArgumentParser(description="ARTIQ core device "
|
||||||
|
"packet capture tool")
|
||||||
|
|
||||||
|
verbosity_args(parser)
|
||||||
|
|
||||||
|
parser.add_argument("--host", metavar="HOST",
|
||||||
|
type=str, default="lab.m-labs.hk",
|
||||||
|
help="SSH host where the development board is located")
|
||||||
|
parser.add_argument("-i", "--ip", metavar="IP",
|
||||||
|
type=str, default="kc705.lab.m-labs.hk",
|
||||||
|
help="IP address corresponding to the development board")
|
||||||
|
parser.add_argument("-f", "--file", metavar="PCAP_FILE",
|
||||||
|
type=str, default="coredevice.pcap",
|
||||||
|
help="Location to retrieve the pcap file into")
|
||||||
|
|
||||||
|
parser.add_argument("command", metavar="COMMAND",
|
||||||
|
type=str, default=[], nargs="+",
|
||||||
|
help="command to execute while capturing")
|
||||||
|
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = get_argparser().parse_args()
|
||||||
|
init_logger(args)
|
||||||
|
|
||||||
|
client = SSHClient(args.host)
|
||||||
|
|
||||||
|
sftp = client.get_sftp()
|
||||||
|
tcpdump = client.spawn_command(
|
||||||
|
"/usr/sbin/tcpdump host {ip} -w {tmp}/trace.pcap", get_pty=True,
|
||||||
|
ip=args.ip)
|
||||||
|
|
||||||
|
try:
|
||||||
|
subprocess.check_call(args.command)
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
logger.error("Command failed")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
tcpdump.close()
|
||||||
|
sftp.get("{tmp}/trace.pcap".format(tmp=client.tmp),
|
||||||
|
args.file)
|
||||||
|
logger.info("Pcap file {file} retrieved".format(file=args.file))
|
|
@ -6,8 +6,10 @@ import collections
|
||||||
import os
|
import os
|
||||||
import atexit
|
import atexit
|
||||||
import string
|
import string
|
||||||
|
import random
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import paramiko
|
||||||
|
|
||||||
from artiq.language.environment import is_experiment
|
from artiq.language.environment import is_experiment
|
||||||
from artiq.protocols import pyon
|
from artiq.protocols import pyon
|
||||||
|
@ -253,3 +255,51 @@ def get_user_config_dir():
|
||||||
dir = user_config_dir("artiq", "m-labs", major)
|
dir = user_config_dir("artiq", "m-labs", major)
|
||||||
os.makedirs(dir, exist_ok=True)
|
os.makedirs(dir, exist_ok=True)
|
||||||
return dir
|
return dir
|
||||||
|
|
||||||
|
|
||||||
|
class SSHClient:
|
||||||
|
def __init__(self, host):
|
||||||
|
self.host = host
|
||||||
|
self.ssh = None
|
||||||
|
self.sftp = None
|
||||||
|
|
||||||
|
tmpname = "".join([random.Random().choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||||
|
for _ in range(6)])
|
||||||
|
self.tmp = "/tmp/artiq" + tmpname
|
||||||
|
|
||||||
|
def get_ssh(self):
|
||||||
|
if self.ssh is None:
|
||||||
|
self.ssh = paramiko.SSHClient()
|
||||||
|
self.ssh.load_system_host_keys()
|
||||||
|
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
self.ssh.connect(self.host)
|
||||||
|
return self.ssh
|
||||||
|
|
||||||
|
def get_transport(self):
|
||||||
|
return self.get_ssh().get_transport()
|
||||||
|
|
||||||
|
def get_sftp(self):
|
||||||
|
if self.sftp is None:
|
||||||
|
self.sftp = self.get_ssh().open_sftp()
|
||||||
|
self.sftp.mkdir(self.tmp)
|
||||||
|
atexit.register(lambda: self.run_command("rm -rf {tmp}"))
|
||||||
|
return self.sftp
|
||||||
|
|
||||||
|
def spawn_command(self, cmd, get_pty=False, **kws):
|
||||||
|
logger.info("Executing {}".format(cmd))
|
||||||
|
chan = self.get_ssh().get_transport().open_session()
|
||||||
|
if get_pty:
|
||||||
|
chan.get_pty()
|
||||||
|
chan.set_combine_stderr(True)
|
||||||
|
chan.exec_command(cmd.format(tmp=self.tmp, **kws))
|
||||||
|
return chan
|
||||||
|
|
||||||
|
def drain(self, chan):
|
||||||
|
while True:
|
||||||
|
char = chan.recv(1)
|
||||||
|
if char == b"":
|
||||||
|
break
|
||||||
|
sys.stderr.write(char.decode("utf-8", errors='replace'))
|
||||||
|
|
||||||
|
def run_command(self, cmd, **kws):
|
||||||
|
self.drain(self.spawn_command(cmd, **kws))
|
||||||
|
|
1
setup.py
1
setup.py
|
@ -27,6 +27,7 @@ console_scripts = [
|
||||||
"artiq_coreboot = artiq.frontend.artiq_coreboot:main",
|
"artiq_coreboot = artiq.frontend.artiq_coreboot:main",
|
||||||
"artiq_ctlmgr = artiq.frontend.artiq_ctlmgr:main",
|
"artiq_ctlmgr = artiq.frontend.artiq_ctlmgr:main",
|
||||||
"artiq_devtool = artiq.frontend.artiq_devtool:main",
|
"artiq_devtool = artiq.frontend.artiq_devtool:main",
|
||||||
|
"artiq_pcap = artiq.frontend.artiq_pcap:main",
|
||||||
"artiq_influxdb = artiq.frontend.artiq_influxdb:main",
|
"artiq_influxdb = artiq.frontend.artiq_influxdb:main",
|
||||||
"artiq_master = artiq.frontend.artiq_master:main",
|
"artiq_master = artiq.frontend.artiq_master:main",
|
||||||
"artiq_mkfs = artiq.frontend.artiq_mkfs:main",
|
"artiq_mkfs = artiq.frontend.artiq_mkfs:main",
|
||||||
|
|
Loading…
Reference in New Issue