Merge ~kissiel/checkbox-support:add-network-tool into checkbox-support:master

Proposed by Maciej Kisielewski
Status: Merged
Approved by: Maciej Kisielewski
Approved revision: c62303ba08e0310e5096cf470cdd4781d8b5e439
Merged at revision: 941d3ee75924ee9435d29fcb288e1d08a2ba48e8
Proposed branch: ~kissiel/checkbox-support:add-network-tool
Merge into: checkbox-support:master
Diff against target: 746 lines (+729/-0)
2 files modified
checkbox_support/scripts/network.py (+727/-0)
setup.py (+2/-0)
Reviewer Review Type Date Requested Status
Sylvain Pineau (community) Needs Fixing
Jonathan Cave (community) Approve
Review via email: mp+315209@code.launchpad.net

Description of the change

Include 'network' test script in checkbox-script.

It's now used in plainbox-provider-checkbox, and soon, plainbox-provider-snappy. Instead of having to copy and paste this script around, let's have it in checkbox-support.

To post a comment you must log in.
Revision history for this message
Maciej Kisielewski (kissiel) wrote :

Launchpad encountered an internal error during the following operation: generating the diff for a merge proposal. It was logged with id OOPS-08e58488e719650225fdf09e931e7103. Sorry for the inconvenience.

ugh

Revision history for this message
Jonathan Cave (jocave) wrote :

Straight import from elsewhere, looks fine to me.

review: Approve
Revision history for this message
Sylvain Pineau (sylvain-pineau) wrote :

just grep'ed for ethtool, there's a comment with it we could update with mii-tool instead. Otherwise +1

review: Needs Fixing
Revision history for this message
Maciej Kisielewski (kissiel) wrote :

> just grep'ed for ethtool, there's a comment with it we could update with mii-
> tool instead. Otherwise +1
Nice catch!
Amended, pushed.
Landing...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/checkbox_support/scripts/network.py b/checkbox_support/scripts/network.py
0new file mode 1007550new file mode 100755
index 0000000..f66c4ad
--- /dev/null
+++ b/checkbox_support/scripts/network.py
@@ -0,0 +1,727 @@
1#!/usr/bin/env python3
2"""
3Copyright (C) 2012-2015 Canonical Ltd.
4
5Authors
6 Jeff Marcom <jeff.marcom@canonical.com>
7 Daniel Manrique <roadmr@ubuntu.com>
8 Jeff Lane <jeff@ubuntu.com>
9
10This program is free software: you can redistribute it and/or modify
11it under the terms of the GNU General Public License version 3,
12as published by the Free Software Foundation.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17GNU General Public License for more details.
18
19You should have received a copy of the GNU General Public License
20along with this program. If not, see <http://www.gnu.org/licenses/>.
21"""
22
23from argparse import (
24 ArgumentParser,
25 RawTextHelpFormatter
26)
27import datetime
28import fcntl
29import ipaddress
30import logging
31import os
32import re
33import shlex
34import socket
35import struct
36import subprocess
37import tempfile
38from subprocess import (
39 CalledProcessError,
40 check_call,
41 check_output,
42 STDOUT
43)
44import sys
45import time
46
47
48class IPerfPerformanceTest(object):
49 """Measures performance of interface using iperf client
50 and target. Calculated speed is measured against theorectical
51 throughput of selected interface"""
52
53 def __init__(
54 self,
55 interface,
56 target,
57 fail_threshold,
58 cpu_load_fail_threshold,
59 iperf3,
60 protocol="tcp",
61 data_size="1",
62 run_time=None,
63 scan_timeout=3600):
64
65 self.iface = Interface(interface)
66 self.target = target
67 self.protocol = protocol
68 self.fail_threshold = fail_threshold
69 self.cpu_load_fail_threshold = cpu_load_fail_threshold
70 self.iperf3 = iperf3
71 self.data_size = data_size
72 self.run_time = run_time
73 self.scan_timeout = scan_timeout
74
75 def run(self):
76 # if max_speed is 0, assume it's wifi and move on
77 if self.iface.max_speed == 0:
78 logging.warning("No max speed detected, assuming Wireless device "
79 "and continuing with test.")
80 # Otherwise, no sense in running if we're not running at full speed.
81 elif self.iface.max_speed > self.iface.link_speed:
82 logging.error("Detected link speed (%s) is lower than "
83 "detected max speed (%s)" %
84 (self.iface.link_speed, self.iface.max_speed))
85 logging.error("Check your device configuration and try again")
86 return 1
87
88 # Set the correct binary to run
89 if (self.iperf3):
90 self.executable = "iperf3 -V"
91 else:
92 self.executable = "iperf"
93
94 # If we set run_time, use that instead to build the command.
95 if self.run_time is not None:
96 cmd = "{} -c {} -t {} -i 1 -f m".format(
97 self.executable, self.target, self.run_time)
98 else:
99 # Because we can vary the data size, we need to vary the timeout as
100 # well. It takes an estimated 15 minutes to send 1GB over 10Mb/s.
101 # 802.11b is 11 Mb/s. So we'll assume 1.2x15 minutes or 18 minutes
102 # or 1080 seconds per Gigabit. This will allow for a long period of
103 # time without timeout to catch devices that slow down, and also
104 # not prematurely end iperf on low-bandwidth devices.
105 self.timeout = 1080*int(self.data_size)
106 cmd = "timeout {} {} -c {} -n {}G -i 1 -f -m".format(
107 self.timeout, self.executable, self.target, self.data_size)
108
109 logging.debug("Executing command {}".format(cmd))
110 logging.info("Starting iperf against {}, this could take a while...".
111 format(self.target))
112 try:
113 iperf_return = check_output(
114 shlex.split(cmd), universal_newlines=True)
115 except CalledProcessError as iperf_exception:
116 if iperf_exception.returncode != 124:
117 # timeout command will return 124 if iperf timed out, so any
118 # other return value means something did fail
119 logging.error("Failed executing iperf: %s",
120 iperf_exception.output)
121 return iperf_exception.returncode
122 else:
123 # this is normal so we "except" this exception and we
124 # "pass through" whatever output iperf did manage to produce.
125 # When confronted with SIGTERM iperf should stop and output
126 # a partial (but usable) result.
127 logging.info("iperf timed out - this should be OK")
128 iperf_return = iperf_exception.output
129
130 logging.debug(iperf_return)
131 # "CPU Utilization" line present only in iperf3 output
132 cpu = re.findall(r"CPU Utilization.*local/sender\s([\w\.]+)",
133 iperf_return)
134 # iperf3 provides "sender" and "receiver" summaries; remove them
135 iperf_return = re.sub(r".*(sender|receiver)", "", iperf_return)
136 speeds = list(map(float, re.findall(r"([\w\.]+)\sMbits/sec",
137 iperf_return)))
138 invalid_speed = False
139 if speeds:
140 throughput = sum(speeds)/len(speeds)
141 try:
142 percent = throughput / int(self.iface.max_speed) * 100
143 except (ZeroDivisionError, TypeError):
144 # Catches a condition where the interface functions fine but
145 # mii-tool fails to properly report max speed. In this case
146 # it's up to the reviewer to pass or fail.
147 percent = 0
148 invalid_speed = True
149
150 logging.info("Min Transfer speed: {} Mb/s".format(min(speeds)))
151 logging.info("Max Transfer speed: {} Mb/s".format(max(speeds)))
152 logging.info("Avg Transfer speed: {} Mb/s".format(throughput))
153 if invalid_speed:
154 # If we have no link_speed (e.g. wireless interfaces don't
155 # report this), then we shouldn't penalize them because
156 # the transfer may have been reasonable. So in this case,
157 # we'll exit with a pass-warning.
158 logging.info("Unable to obtain maximum speed.")
159 logging.info("Considering the test as passed.")
160 return 0
161 # Below is guaranteed to not throw an exception because we'll
162 # have exited above if it did.
163 logging.info("{:03.2f}% of theoretical max {} Mb/s".
164 format(percent, int(self.iface.max_speed)))
165 if cpu:
166 logging.info("")
167 logging.info("CPU utilization: {}%".format(cpu[0]))
168 cpu_load = float(cpu[0])
169 else:
170 cpu_load = 0.0
171 if percent < self.fail_threshold or \
172 cpu_load > self.cpu_load_fail_threshold:
173 logging.warn("Poor network performance detected against {}".
174 format(self.target))
175 if percent < self.fail_threshold:
176 logging.warn(" Transfer speed: {} Mb/s".
177 format(throughput))
178 logging.warn(" {:03.2f}% of theoretical max {} Mb/s\n".
179 format(percent, int(self.iface.max_speed)))
180 if cpu_load > self.cpu_load_fail_threshold:
181 logging.warn(" CPU load: {}%".format(cpu_load))
182 logging.warn(" CPU load is above {}% maximum\n".
183 format(self.cpu_load_fail_threshold))
184 return 30
185
186 logging.debug("Passed benchmark against {}".format(self.target))
187 else:
188 logging.error("Failed iperf benchmark against {}".
189 format(self.target))
190 return 1
191
192
193class StressPerformanceTest:
194
195 def __init__(self, interface, target, iperf3):
196 self.interface = interface
197 self.target = target
198 self.iperf3 = iperf3
199
200 def run(self):
201 if self.iperf3:
202 iperf_cmd = 'timeout 320 iperf3 -c {} -t 300'.format(self.target)
203 else:
204 iperf_cmd = 'timeout 320 iperf -c {} -t 300'.format(self.target)
205 print("Running iperf...")
206 iperf = subprocess.Popen(shlex.split(iperf_cmd))
207
208 ping_cmd = 'ping -I {} {}'.format(self.interface, self.target)
209 ping = subprocess.Popen(shlex.split(ping_cmd), stdout=subprocess.PIPE)
210 iperf.communicate()
211
212 ping.terminate()
213 (out, err) = ping.communicate()
214
215 if iperf.returncode != 0:
216 return iperf.returncode
217
218 print("Running ping test...")
219 result = 0
220 time_re = re.compile('(?<=time=)[0-9]*')
221 for line in out.decode().split('\n'):
222 time = time_re.search(line)
223
224 if time and int(time.group()) > 2000:
225 print(line)
226 print("ICMP packet was delayed by > 2000 ms.")
227 result = 1
228 if 'unreachable' in line.lower():
229 print(line)
230 result = 1
231
232 return result
233
234
235class Interface(socket.socket):
236 """
237 Simple class that provides network interface information.
238 """
239
240 def __init__(self, interface):
241
242 super(Interface, self).__init__(
243 socket.AF_INET, socket.IPPROTO_ICMP)
244
245 self.interface = interface
246
247 self.dev_path = os.path.join("/sys/class/net", self.interface)
248
249 def _read_data(self, type):
250 try:
251 return open(os.path.join(self.dev_path, type)).read().strip()
252 except OSError:
253 logging.info("%s: Attribute not found", type)
254
255 @property
256 def ipaddress(self):
257 freq = struct.pack('256s', self.interface[:15].encode())
258
259 try:
260 nic_data = fcntl.ioctl(self.fileno(), 0x8915, freq)
261 except IOError:
262 logging.error("No IP address for %s", self.interface)
263 return 1
264 return socket.inet_ntoa(nic_data[20:24])
265
266 @property
267 def netmask(self):
268 freq = struct.pack('256s', self.interface.encode())
269
270 try:
271 mask_data = fcntl.ioctl(self.fileno(), 0x891b, freq)
272 except IOError:
273 logging.error("No netmask for %s", self.interface)
274 return 1
275 return socket.inet_ntoa(mask_data[20:24])
276
277 @property
278 def link_speed(self):
279 return int(self._read_data("speed"))
280
281 @property
282 def max_speed(self):
283 # Parse mii-tool data for max speed
284 # search for numbers in the line starting with 'capabilities'
285 # return largest number as max_speed
286 try:
287 info = check_output(['mii-tool', '-v', self.interface],
288 universal_newlines=True,
289 stderr=STDOUT).split('\n')
290 except FileNotFoundError:
291 logging.warning('mii-tool not found! Unable to get max speed')
292 ethinfo = None
293 except CalledProcessError as e:
294 logging.error('mii-tool returned an error!')
295 logging.error(e.output)
296 ethinfo = None
297 finally:
298 regex = re.compile(r'(\d+)(base)([A-Z]+)')
299 speeds = [0]
300 for line in filter(lambda l: 'capabilities' in l, info):
301 for s in line.split(' '):
302 hit = regex.search(s)
303 if hit:
304 speeds.append(int(re.sub("\D", "", hit.group(0))))
305 return max(speeds)
306
307 @property
308 def macaddress(self):
309 return self._read_data("address")
310
311 @property
312 def duplex_mode(self):
313 return self._read_data("duplex")
314
315 @property
316 def status(self):
317 return self._read_data("operstate")
318
319 @property
320 def device_name(self):
321 return self._read_data("device/label")
322
323
324def get_test_parameters(args, environ):
325 # Decide the actual values for test parameters, which can come
326 # from one of two possible sources: command-line
327 # arguments, or environment variables.
328 # - If command-line args were given, they take precedence
329 # - Next come environment variables, if set.
330
331 params = {"test_target_iperf": None}
332
333 # See if we have environment variables
334 for key in params.keys():
335 params[key] = os.environ.get(key.upper(), "")
336
337 # Finally, see if we have the command-line arguments that are the ultimate
338 # override.
339 if args.target:
340 params["test_target_iperf"] = args.target
341
342 return params
343
344
345def can_ping(the_interface, test_target):
346 working_interface = False
347 num_loops = 0
348 while (not working_interface) and (num_loops < 48):
349 working_interface = True
350
351 try:
352 with open(os.devnull, 'wb') as DEVNULL:
353 check_call(["ping", "-I", the_interface,
354 "-c", "1", test_target],
355 stdout=DEVNULL, stderr=DEVNULL)
356 except CalledProcessError as excp:
357 working_interface = False
358 if num_loops == 0:
359 logging.warning("Ping failure on {} ({})".
360 format(the_interface, excp))
361
362 if not working_interface:
363 time.sleep(5)
364 num_loops += 1
365
366 return working_interface
367
368
369def run_test(args, test_target):
370 # Ensure that interface is fully up by waiting until it can
371 # ping the test server
372 if not can_ping(args.interface, test_target):
373 logging.error("Can't ping test server {} on {}".format(test_target,
374 args.interface))
375 return 1
376
377 # Execute requested networking test
378 if args.test_type.lower() == "iperf":
379 error_number = 0
380 iperf_benchmark = IPerfPerformanceTest(args.interface, test_target,
381 args.fail_threshold,
382 args.cpu_load_fail_threshold,
383 args.iperf3)
384 if args.datasize:
385 iperf_benchmark.data_size = args.datasize
386 if args.runtime:
387 iperf_benchmark.run_time = args.runtime
388 run_num = 0
389 while not error_number and run_num < args.num_runs:
390 error_number = iperf_benchmark.run()
391 run_num += 1
392 logging.info(" Finished run number %s ".center(60, "-"), run_num)
393 elif args.test_type.lower() == "stress":
394 stress_benchmark = StressPerformanceTest(args.interface,
395 test_target, args.iperf3)
396 error_number = stress_benchmark.run()
397 else:
398 logging.error("Unknown test type {}".format(args.test_type))
399 return 10
400 return error_number
401
402
403def make_target_list(iface, test_targets, log_warnings):
404 """Convert comma-separated string of test targets into a list form.
405
406 Converts test target list in string form into Python list form, omitting
407 entries that are not on the current network segment.
408 :param iface:
409 Name of network interface device (eth0, etc.)
410 :param test_targets:
411 Input test targets as string of comma-separated IP addresses or
412 hostnames
413 :param emit_warnings:
414 Whether to log warning messages
415 :returns:
416 List form of input string, minus invalid values
417 """
418 test_targets_list = test_targets.split(",")
419 net = ipaddress.IPv4Network("{}/{}".format(Interface(iface).ipaddress,
420 Interface(iface).netmask),
421 False)
422 first_addr = net.network_address + 1
423 last_addr = first_addr + net.num_addresses - 2
424 return_list = list(test_targets_list)
425 for test_target in test_targets_list:
426 try:
427 test_target_ip = socket.gethostbyname(test_target)
428 except OSError:
429 test_target_ip = test_target
430 try:
431 target = ipaddress.IPv4Address(test_target_ip)
432 if (target < first_addr) or (target > last_addr):
433 if log_warnings:
434 logging.warning("test server {} ({}) is NOT ".
435 format(test_target, target))
436 logging.warning("within {}; skipping".format(net))
437 return_list.remove(test_target)
438 except ValueError:
439 if log_warnings:
440 logging.warning("Invalid address: {}; skipping".
441 format(test_target))
442 return_list.remove(test_target)
443 return_list.reverse()
444 return return_list
445
446
447def interface_test(args):
448 if not ("test_type" in vars(args)):
449 return
450
451 # Get the actual test data from one of two possible sources
452 test_parameters = get_test_parameters(args, os.environ)
453
454 if (args.test_type.lower() == "iperf" or
455 args.test_type.lower() == "stress"):
456 test_targets = test_parameters["test_target_iperf"]
457 test_targets_list = make_target_list(args.interface, test_targets, True)
458
459 # Validate that we got reasonable values
460 if not test_targets_list or "example.com" in test_targets:
461 # Default values found in config file
462 logging.error("Valid target server has not been supplied.")
463 logging.info("Configuration settings can be configured 3 different "
464 "ways:")
465 logging.info("1- If calling the script directly, pass the --target "
466 "option")
467 logging.info("2- Define the TEST_TARGET_IPERF environment variable")
468 logging.info("3- (If running the test via checkbox/plainbox, define "
469 "the ")
470 logging.info("target in /etc/xdg/canonical-certification.conf)")
471 logging.info("Please run this script with -h to see more details on "
472 "how to configure")
473 sys.exit(1)
474
475 # Testing begins here!
476 #
477 # Make sure that the interface is indeed connected
478 try:
479 check_call(["ip", "link", "set", "dev", args.interface, "up"])
480 except CalledProcessError as interface_failure:
481 logging.error("Failed to use %s:%s", args.interface, interface_failure)
482 return 1
483
484 # Back up routing table, since network down/up process
485 # tends to trash it....
486 temp = tempfile.TemporaryFile()
487 try:
488 check_call(["ip", "route", "save", "table", "all"], stdout=temp)
489 except CalledProcessError as route_error:
490 logging.warning("Unable to save routing table: %s", route_error)
491
492 error_number = 0
493 # Stop all other interfaces
494 extra_interfaces = \
495 [iface for iface in os.listdir("/sys/class/net")
496 if iface != "lo" and iface != args.interface]
497
498 for iface in extra_interfaces:
499 logging.debug("Shutting down interface:%s", iface)
500 try:
501 check_call(["ip", "link", "set", "dev", iface, "down"])
502 except CalledProcessError as interface_failure:
503 logging.error("Failed to shut down %s:%s",
504 iface, interface_failure)
505 error_number = 3
506
507 if error_number == 0:
508 start_time = datetime.datetime.now()
509 first_loop = True
510 # Keep testing until a success or we run out of both targets and time
511 while test_targets_list:
512 test_target = test_targets_list.pop().strip()
513 error_number = run_test(args, test_target)
514 elapsed_seconds = (datetime.datetime.now() - start_time).seconds
515 if (elapsed_seconds > args.scan_timeout and not first_loop) or \
516 not error_number:
517 break
518 if not test_targets_list:
519 logging.info(" Exhausted test target list; trying again "
520 .center(60, "="))
521 test_targets_list = make_target_list(args.interface, test_targets, False)
522 time.sleep(30)
523 first_loop = False
524
525 for iface in extra_interfaces:
526 logging.debug("Restoring interface:%s", iface)
527 try:
528 check_call(["ip", "link", "set", "dev", iface, "up"])
529 except CalledProcessError as interface_failure:
530 logging.error("Failed to restore %s:%s", iface, interface_failure)
531 error_number = 3
532
533 # Restore routing table to original state
534 temp.seek(0)
535 try:
536 # Harmless "RTNETLINK answers: File exists" messages on stderr
537 with open(os.devnull, 'wb') as DEVNULL:
538 check_call(["ip", "route", "restore"], stdin=temp,
539 stderr=DEVNULL)
540 except CalledProcessError as restore_failure:
541 logging.warning("Unable to restore routing table: %s", restore_failure)
542 temp.close()
543
544 return error_number
545
546
547def interface_info(args):
548
549 info_set = ""
550 if "all" in vars(args):
551 info_set = args.all
552
553 for key, value in vars(args).items():
554 if value is True or info_set is True:
555 key = key.replace("-", "_")
556 try:
557 print(
558 key + ":", getattr(Interface(args.interface), key),
559 file=sys.stderr)
560 except AttributeError:
561 pass
562
563
564def main():
565
566 intro_message = """
567Network module
568
569This script provides benchmarking and information for a specified network
570interface.
571
572Example NIC information usage:
573network info -i eth0 --max-speed
574
575For running iperf test:
576network test -i eth0 -t iperf --target 192.168.0.1
577NOTE: The iperf test requires an iperf server running on the same network
578segment that the test machine is running on.
579
580Also, you can use iperf3 rather than iperf2 by specifying the -3 or --iperf3
581option like so:
582
583network test -i eth0 -t iperf -3 --target 192.168.0.1
584
585Configuration
586=============
587
588Configuration can be supplied in three different ways, with the following
589priorities:
590
5911- Command-line parameters (see above).
5922- Environment variables (example will follow).
5933- If run via checkbox/plainbox, /etc/xdg/checkbox-certification.conf
594 can have the below-mentioned environment variables defined in the
595 [environment] section. An example file is provided and can be simply
596 modified with the correct values.
597
598Environment variables
599=====================
600The variables are:
601TEST_TARGET_IPERF
602
603example config file
604===================
605[environment]
606TEST_TARGET_IPERF = iperf-server.example.com
607
608
609**NOTE**
610
611"""
612
613 parser = ArgumentParser(
614 description=intro_message, formatter_class=RawTextHelpFormatter)
615 subparsers = parser.add_subparsers()
616
617 # Main cli options
618 test_parser = subparsers.add_parser(
619 'test', help=("Run network performance test"))
620 info_parser = subparsers.add_parser(
621 'info', help=("Gather network info"))
622
623 # Sub test options
624 action = test_parser.add_mutually_exclusive_group()
625
626 test_parser.add_argument(
627 '-i', '--interface', type=str, required=True)
628 test_parser.add_argument(
629 '-t', '--test_type', type=str,
630 choices=("iperf", "stress"), default="iperf",
631 help=("[iperf *Default*]"))
632 test_parser.add_argument(
633 '-3', '--iperf3', default=False, action="store_true",
634 help=("Tells the script to use iperf3 for testing, rather than the "
635 "default of iperf2"))
636 test_parser.add_argument('--target', type=str)
637 action.add_argument(
638 '--datasize', type=str,
639 default="1",
640 help=("CANNOT BE USED WITH --runtime. Amount of data to send. For "
641 "iperf tests this will direct iperf to send DATASIZE GB of "
642 "data to the target."))
643 action.add_argument(
644 '--runtime', type=int,
645 default=60,
646 help=("CANNOT BE USED WITH --datasize. Send data for *runtime* "
647 "seconds. For iperf tests, this will send data for the amount "
648 "of time indicated, rather than until a certain file size is "
649 "reached."))
650 test_parser.add_argument(
651 '--scan-timeout', type=int,
652 default=60,
653 help=("Sets the maximum time, in seconds, the test will scan for "
654 "iperf servers before giving up."))
655 test_parser.add_argument(
656 '--config', type=str,
657 default="/etc/checkbox.d/network.cfg",
658 help="Supply config file for target/host network parameters")
659 test_parser.add_argument(
660 '--fail-threshold', type=int,
661 default=40,
662 help=("IPERF Test ONLY. Set the failure threshold (Percent of maximum "
663 "theoretical bandwidth) as a number like 80. (Default is "
664 "%(default)s)"))
665 test_parser.add_argument(
666 '--cpu-load-fail-threshold', type=int,
667 default=100,
668 help=("(IPERF Test ONLY and meaningful ONLY with --iperf3. Set the "
669 "failure threshold (above which the CPU load must not rise) as "
670 "a number like 80. (Default is %(default)s)"))
671 test_parser.add_argument(
672 '--num_runs', type=int,
673 default=1,
674 help=("Number of times to run the test. (Default is %(default)s)"))
675 test_parser.add_argument(
676 '--debug', default=False, action="store_true",
677 help="Turn on verbose output")
678
679 # Sub info options
680 info_parser.add_argument(
681 '-i', '--interface', type=str, required=True)
682 info_parser.add_argument(
683 '--all', default=False, action="store_true")
684 info_parser.add_argument(
685 '--duplex-mode', default=False, action="store_true")
686 info_parser.add_argument(
687 '--link-speed', default=False, action="store_true")
688 info_parser.add_argument(
689 '--max-speed', default=False, action="store_true")
690 info_parser.add_argument(
691 '--ipaddress', default=False, action="store_true")
692 info_parser.add_argument(
693 '--netmask', default=False, action="store_true")
694 info_parser.add_argument(
695 '--device-name', default=False, action="store_true")
696 info_parser.add_argument(
697 '--macaddress', default=False, action="store_true")
698 info_parser.add_argument(
699 '--status', default=False, action="store_true",
700 help=("displays connection status"))
701 info_parser.add_argument(
702 '--debug', default=False, action="store_true",
703 help="Turn on verbose output")
704
705 test_parser.set_defaults(func=interface_test)
706 info_parser.set_defaults(func=interface_info)
707
708 args = parser.parse_args()
709 if (args.func.__name__ is interface_test and
710 not args.cpu_load_fail_threshold != 100 and
711 not args.iperf3):
712 parser.error('--cpu-load-fail-threshold can only be set with '
713 '--iperf3.')
714
715 if args.debug:
716 logging.basicConfig(level=logging.DEBUG)
717 else:
718 logging.basicConfig(level=logging.INFO)
719
720 if 'func' not in args:
721 parser.print_help()
722 else:
723 return args.func(args)
724
725
726if __name__ == "__main__":
727 sys.exit(main())
diff --git a/setup.py b/setup.py
index 6fcfef8..86866ad 100755
--- a/setup.py
+++ b/setup.py
@@ -80,6 +80,8 @@ setup(
80 "checkbox_support.scripts.run_watcher:main"),80 "checkbox_support.scripts.run_watcher:main"),
81 ("checkbox-support-fwts_test="81 ("checkbox-support-fwts_test="
82 "checkbox_support.scripts.fwts_test:main"),82 "checkbox_support.scripts.fwts_test:main"),
83 ("checkbox-support-network="
84 "checkbox_support.scripts.network:main"),
83 ],85 ],
84 },86 },
85)87)

Subscribers

People subscribed via source and target branches