Using the Python Interface
While the web interface, the XRA-31 Command-line Interface and the XRA-31 Python Interface provide the same options for interaction with the XRA-31, the latter introduces the power of Python to allow for more complex scenarios.
After the basics have been introduced, the tutorial will move on to a typical debugging scenario (Full example).
Storing and loading configurations
The same JSON files that can be stored and loaded using the command-line interface (Storing and loading configurations), can also be created or loaded from the Python interface.
Channel configuration
Assuming the XRA-31 can be accessed at http://xra31_hostname
,
the following script will store the active configuration to a.json
,
and load b.json
for a new test:
import json
from excentis import xra31
# Connect to the XRA-31
with xra31.connect(address="xra31_hostname") as client:
# Store the active configuration
description = client.configuration.describe()
json.dump(description, open("a.json", "w"), indent=4)
# Load the desired configuration
description = json.load(open("b.json", "r"))
client.configuration.apply(description)
Capture configuration
The same can be done for capture configurations:
import json
from excentis import xra31
# Connect to the XRA-31
with xra31.connect(address="xra31_hostname") as client:
# Store the active capture settings
description = client.capture.describe()
json.dump(description, open("a.json", "w"), indent=4)
# Load the desired capture settings
description = json.load(open("b.json", "r"))
client.capture.apply(description)
Fine-grained configuration
For fine-grained channel configuration, a minimal example can be found in the Introduction. This also covers configuring, starting and stopping a capture, as well as downloading the result.
Full example
As a full example, this script configures the XRA-31 channels using a previously stored JSON file, starts an unlimited rolling file capture and waits for the moment a given cable modem goes offline.
We’re only interested in the final minute before, and the first minute after the cable modem goes offline, so using 3 files with a per-file duration limit of 1 minute will provide the necessary time window.
While we choose to stop the capture, it can also be kept alive and the procedure can then be repeated to observe multiple occurrences.
This script uses the OS-specific system ping to avoid permission issues.
1#!/usr/bin/env python3
2"""Capture the final minute before, and the first after a CM goes offline."""
3
4import argparse
5import datetime
6import json
7import logging
8import pathlib
9import subprocess
10import sys
11import time
12
13from excentis import xra31
14
15# Arguments
16parser = argparse.ArgumentParser(
17 description="Capture while waiting for a modem to go offline")
18parser.add_argument("address",
19 metavar="XRA-31",
20 help="the XRA-31 hostname or IP address")
21parser.add_argument("cm",
22 metavar="cable-modem",
23 help="the IP address of the cable modem",
24 type=str)
25parser.add_argument("--load",
26 metavar="configuration.json",
27 help="load a JSON configuration to the XRA-31",
28 type=argparse.FileType('r'))
29
30if __name__ == "__main__":
31 args = parser.parse_args()
32
33 # Logging
34 logger = logging.getLogger(pathlib.Path(__file__).name)
35 logging.basicConfig(format="[%(name)s][%(levelname)s]: %(message)s")
36 logger.setLevel(logging.INFO)
37
38 # Create a day-specific file for the output
39 path = "cm-offline/cm-offline-{}".format(
40 datetime.datetime.now().strftime("%Y-%m-%d"))
41
42 # Connect to the XRA-31, force full access mode
43 with xra31.connect(address=args.address, full_access=True,
44 force=True) as client:
45
46 # Stop capturing if needed
47 logger.info("Stop capturing")
48 client.capture.stop()
49
50 # Optionally change its channels
51 if args.load:
52 logger.info("Load configuration")
53 description = json.load(args.load)
54 client.configuration.apply(description)
55
56 # Select all channels
57 logger.info("Update channel selection")
58 client.capture.channels = client.configuration.channels
59
60 # Select all packet types, OFDM streams and OFDM profiles
61 logger.info("Update filtering")
62 client.capture.filtering.packet_types = xra31.capture.PacketType
63 client.capture.filtering.ofdm_streams = xra31.capture.OfdmStream
64 client.capture.filtering.ofdm_profiles = xra31.capture.OfdmProfile
65 # Remove NCP
66 logger.info("Ignore NCP")
67 client.capture.filtering.remove_ofdm_stream(
68 xra31.capture.OfdmStream.NCP)
69
70 # Update the capture output settings
71 logger.info("Update capture output settings")
72 client.capture.output.path = path
73 client.capture.output.size = None # unlimited
74 client.capture.output.duration = None # unlimited
75 client.capture.output.number_of_files = 3
76 client.capture.output.file_size = None # unlimited
77 client.capture.output.file_duration = 60
78
79 # Start the capture
80 logger.info("Start capturing")
81 client.capture.start()
82
83 # Check if the CM is online by pinging with a 2s timeout
84 ping_command = ["ping", "/n", "1", "/w", "2000", args.cm
85 ] if sys.platform == "win32" else [
86 "ping", "-c", "1", "-W", "2", args.cm
87 ]
88 # Ping about every 10s
89 # until non-zero exit code causes CalledProcessError (check=True)
90 try:
91 while True:
92 logger.info("ping")
93 subprocess.run(ping_command,
94 stdout=subprocess.DEVNULL,
95 stderr=subprocess.DEVNULL,
96 check=True)
97 time.sleep(10)
98 except subprocess.CalledProcessError:
99 pass
100
101 logger.info("No ping response, continue capturing for 1 minute")
102 time.sleep(60)
103 logger.info("Stop capturing")
104 client.capture.stop()
105 logger.info("Download the captured files")
106 client.analysis.download(path,
107 rolling=True,
108 compress=True,
109 verbose=True)
110 logger.info("Remove the captured files from the XRA-31")
111 client.analysis.delete(path, rolling=True)