From e5ae9128ec358fd417433ba181faf522a55bdf16 Mon Sep 17 00:00:00 2001 From: Jonathan Cave Date: Wed, 20 Feb 2019 11:05:41 +0000 Subject: wireless: sleep on failure to request rescan In wifi_nmcli_test on recent nmcli versions a rescan is requested to ensure an up-to-date list of APs is available. However this request can fail if NM has already started a scan, but we immediately attempt the connection. This should let the background scan complete. --- bin/wifi_nmcli_test | 197 --------------------------------------------- bin/wifi_nmcli_test.py | 207 ++++++++++++++++++++++++++++++++++++++++++++++++ units/wireless/jobs.pxu | 14 ++-- 3 files changed, 214 insertions(+), 204 deletions(-) delete mode 100755 bin/wifi_nmcli_test create mode 100755 bin/wifi_nmcli_test.py diff --git a/bin/wifi_nmcli_test b/bin/wifi_nmcli_test deleted file mode 100755 index b89a525..0000000 --- a/bin/wifi_nmcli_test +++ /dev/null @@ -1,197 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2017-2018 Canonical Ltd. -# All rights reserved. -# -# Written by: -# Jonathan Cave -# Taihsiang Ho -# -# wireless connection tests using nmcli - - -import argparse -import functools -import subprocess as sp -import sys - -from distutils.version import LooseVersion - - -print = functools.partial(print, flush=True) - - -def print_head(txt): - print("##", txt) - - -def print_cmd(cmd): - print("+", cmd) - - -def legacy_nmcli(): - cmd = "nmcli -v" - output = sp.check_output(cmd, shell=True) - version = LooseVersion(output.strip().split()[-1].decode()) - # check if using an earlier nmcli version with different api - # nmcli in trusty is 0.9.8.8 - if version < LooseVersion("0.9.9"): - return True - return False - - -def cleanup_nm_connections(): - print_head("Cleaning up NM connections") - cmd = "nmcli -t -f TYPE,UUID,NAME c" - print_cmd(cmd) - output = sp.check_output(cmd, shell=True) - for line in output.decode(sys.stdout.encoding).splitlines(): - type, uuid, name = line.strip().split(':') - if type == '802-11-wireless': - print("Deleting connection", name) - if legacy_nmcli(): - cmd = "nmcli c delete uuid {}".format(uuid) - else: - cmd = "nmcli c delete {}".format(uuid) - print_cmd(cmd) - sp.call(cmd, shell=True) - print() - - -def device_rescan(): - print_head("Calling a rescan") - cmd = "nmcli d wifi rescan" - print_cmd(cmd) - sp.call(cmd, shell=True) - print() - - -def list_aps(args): - print_head("List APs") - count = 0 - if legacy_nmcli(): - fields = "SSID,FREQ,SIGNAL" - cmd = "nmcli -t -f {} d wifi list iface {}".format(fields, args.device) - else: - fields = "SSID,CHAN,FREQ,SIGNAL" - cmd = "nmcli -t -f {} d wifi list ifname {}".format(fields, args.device) - print_cmd(cmd) - output = sp.check_output(cmd, shell=True) - for line in output.decode(sys.stdout.encoding).splitlines(): - # lp bug #1723372 - extra line in output on zesty - if line.strip() == args.device: - continue - if legacy_nmcli(): - ssid, frequency, signal = line.strip().split(':') - print("SSID: {} Freq: {} Signal: {}".format( - ssid, frequency, signal)) - else: - ssid, channel, frequency, signal = line.strip().split(':') - print("SSID: {} Chan: {} Freq: {} Signal: {}".format( - ssid, channel, frequency, signal)) - if hasattr(args, 'essid'): - if ssid == args.essid: - count += 1 - else: - count += 1 - print() - return count - - -def open_connection(args): - print_head("Connection attempt") - if legacy_nmcli(): - cmd = "nmcli d wifi connect {} iface {} name TEST_CON".format( - args.essid, args.device) - else: - cmd = "nmcli d wifi connect {} ifname {} name TEST_CON".format( - args.essid, args.device) - print_cmd(cmd) - sp.call(cmd, shell=True) - if legacy_nmcli(): - cmd_part = "nmcli -m tabular -t -f GENERAL d list | " - cmd = cmd_part + "grep {} | awk -F: '{{print $15}}'".format(args.device) - else: - cmd = "nmcli -m tabular -t -f GENERAL.STATE d show {}".format(args.device) - print_cmd(cmd) - output = sp.check_output(cmd, shell=True) - state = output.decode(sys.stdout.encoding).strip() - print(state) - rc = 1 - if state.startswith('100'): - rc = 0 - print() - return rc - - -def secured_connection(args): - print_head("Connection attempt") - if legacy_nmcli(): - cmd = "nmcli d wifi connect {} password {} iface {} name TEST_CON".format( - args.essid, args.psk, args.device) - else: - cmd = "nmcli d wifi connect {} password {} ifname {} name TEST_CON".format( - args.essid, args.psk, args.device) - print_cmd(cmd) - sp.call(cmd, shell=True) - if legacy_nmcli(): - cmd_part = "nmcli -m tabular -t -f GENERAL d list | " - cmd = cmd_part + "grep {} | awk -F: '{{print $15}}'".format(args.device) - else: - cmd = "nmcli -m tabular -t -f GENERAL.STATE d show {}".format(args.device) - print_cmd(cmd) - output = sp.check_output(cmd, shell=True) - state = output.decode(sys.stdout.encoding).strip() - print(state) - rc = 1 - if state.startswith('100'): - rc = 0 - print() - return rc - - -if __name__ == '__main__': - parser = argparse.ArgumentParser( - description='WiFi connection test using mmcli') - - subparsers = parser.add_subparsers(dest='test_type') - subparsers.required = True - - parser_scan = subparsers.add_parser( - 'scan', help='Test can scan for networks only') - parser_scan.add_argument( - 'device', type=str, help='Device name e.g. wlan0') - - parser_open = subparsers.add_parser( - 'open', help='Test connection to an open access point') - parser_open.add_argument( - 'device', type=str, help='Device name e.g. wlan0') - parser_open.add_argument('essid', type=str, help='ESSID') - parser_open.set_defaults(func=open_connection) - - parser_secured = subparsers.add_parser( - 'secured', help='Test connection to a secured access point') - parser_secured.add_argument( - 'device', type=str, help='Device name e.g. wlan0') - parser_secured.add_argument('essid', type=str, help='ESSID') - parser_secured.add_argument('psk', type=str, help='Pre-Shared Key') - parser_secured.set_defaults(func=secured_connection) - args = parser.parse_args() - - cleanup_nm_connections() - if not legacy_nmcli(): - device_rescan() - count = list_aps(args) - - if args.test_type == 'scan': - if count == 0: - print("Failed to find any APs") - sys.exit(1) - else: - print("Found {} access points".format(count)) - sys.exit(0) - - if args.func: - try: - sys.exit(args.func(args)) - finally: - cleanup_nm_connections() diff --git a/bin/wifi_nmcli_test.py b/bin/wifi_nmcli_test.py new file mode 100755 index 0000000..45b8624 --- /dev/null +++ b/bin/wifi_nmcli_test.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python3 +# Copyright 2017-2019 Canonical Ltd. +# All rights reserved. +# +# Written by: +# Jonathan Cave +# Taihsiang Ho +# +# wireless connection tests using nmcli + + +import argparse +import functools +import subprocess as sp +import sys +import time + +from distutils.version import LooseVersion + + +print = functools.partial(print, flush=True) + + +def print_head(txt): + print("##", txt) + + +def print_cmd(cmd): + print("+", cmd) + + +def legacy_nmcli(): + cmd = "nmcli -v" + output = sp.check_output(cmd, shell=True) + version = LooseVersion(output.strip().split()[-1].decode()) + # check if using an earlier nmcli version with different api + # nmcli in trusty is 0.9.8.8 + if version < LooseVersion("0.9.9"): + return True + return False + + +def cleanup_nm_connections(): + print_head("Cleaning up NM connections") + cmd = "nmcli -t -f TYPE,UUID,NAME c" + print_cmd(cmd) + output = sp.check_output(cmd, shell=True) + for line in output.decode(sys.stdout.encoding).splitlines(): + type, uuid, name = line.strip().split(':') + if type == '802-11-wireless': + print("Deleting connection", name) + if legacy_nmcli(): + cmd = "nmcli c delete uuid {}".format(uuid) + else: + cmd = "nmcli c delete {}".format(uuid) + print_cmd(cmd) + sp.call(cmd, shell=True) + print() + + +def device_rescan(): + print_head("Calling a rescan") + cmd = "nmcli d wifi rescan" + print_cmd(cmd) + retcode = sp.call(cmd, shell=True) + if retcode != 0: + # Most often the rescan request fails because NM has itself started + # a scan in recent past, we should let these operations complete before + # attempting a connection + print('Scan request failed, allow other operations to complete (15s)') + time.sleep(15) + print() + + +def list_aps(args): + print_head("List APs") + count = 0 + if legacy_nmcli(): + fields = "SSID,FREQ,SIGNAL" + cmd = "nmcli -t -f {} d wifi list iface {}".format(fields, args.device) + else: + fields = "SSID,CHAN,FREQ,SIGNAL" + cmd = "nmcli -t -f {} d wifi list ifname {}".format( + fields, args.device) + print_cmd(cmd) + output = sp.check_output(cmd, shell=True) + for line in output.decode(sys.stdout.encoding).splitlines(): + # lp bug #1723372 - extra line in output on zesty + if line.strip() == args.device: + continue + if legacy_nmcli(): + ssid, frequency, signal = line.strip().split(':') + print("SSID: {} Freq: {} Signal: {}".format( + ssid, frequency, signal)) + else: + ssid, channel, frequency, signal = line.strip().split(':') + print("SSID: {} Chan: {} Freq: {} Signal: {}".format( + ssid, channel, frequency, signal)) + if hasattr(args, 'essid'): + if ssid == args.essid: + count += 1 + else: + count += 1 + print() + return count + + +def open_connection(args): + print_head("Connection attempt") + if legacy_nmcli(): + cmd = "nmcli d wifi connect {} iface {} name TEST_CON".format( + args.essid, args.device) + else: + cmd = "nmcli d wifi connect {} ifname {} name TEST_CON".format( + args.essid, args.device) + print_cmd(cmd) + sp.call(cmd, shell=True) + if legacy_nmcli(): + cmd = ("nmcli -m tabular -t -f GENERAL d list | grep {} | " + "awk -F: '{{print $15}}'".format(args.device)) + else: + cmd = "nmcli -m tabular -t -f GENERAL.STATE d show {}".format( + args.device) + print_cmd(cmd) + output = sp.check_output(cmd, shell=True) + state = output.decode(sys.stdout.encoding).strip() + print(state) + rc = 1 + if state.startswith('100'): + rc = 0 + print() + return rc + + +def secured_connection(args): + print_head("Connection attempt") + if legacy_nmcli(): + cmd = ("nmcli d wifi connect {} password {} iface {} name " + "TEST_CON".format(args.essid, args.psk, args.device)) + else: + cmd = ("nmcli d wifi connect {} password {} ifname {} name " + "TEST_CON".format(args.essid, args.psk, args.device)) + print_cmd(cmd) + sp.call(cmd, shell=True) + if legacy_nmcli(): + cmd = ("nmcli -m tabular -t -f GENERAL d list | " + "grep {} | awk -F: '{{print $15}}'".format(args.device)) + else: + cmd = "nmcli -m tabular -t -f GENERAL.STATE d show {}".format( + args.device) + print_cmd(cmd) + output = sp.check_output(cmd, shell=True) + state = output.decode(sys.stdout.encoding).strip() + print(state) + rc = 1 + if state.startswith('100'): + rc = 0 + print() + return rc + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description='WiFi connection test using mmcli') + + subparsers = parser.add_subparsers(dest='test_type') + subparsers.required = True + + parser_scan = subparsers.add_parser( + 'scan', help='Test can scan for networks only') + parser_scan.add_argument( + 'device', type=str, help='Device name e.g. wlan0') + + parser_open = subparsers.add_parser( + 'open', help='Test connection to an open access point') + parser_open.add_argument( + 'device', type=str, help='Device name e.g. wlan0') + parser_open.add_argument('essid', type=str, help='ESSID') + parser_open.set_defaults(func=open_connection) + + parser_secured = subparsers.add_parser( + 'secured', help='Test connection to a secured access point') + parser_secured.add_argument( + 'device', type=str, help='Device name e.g. wlan0') + parser_secured.add_argument('essid', type=str, help='ESSID') + parser_secured.add_argument('psk', type=str, help='Pre-Shared Key') + parser_secured.set_defaults(func=secured_connection) + args = parser.parse_args() + + cleanup_nm_connections() + if not legacy_nmcli(): + device_rescan() + count = list_aps(args) + + if args.test_type == 'scan': + if count == 0: + print("Failed to find any APs") + sys.exit(1) + else: + print("Found {} access points".format(count)) + sys.exit(0) + + if args.func: + try: + sys.exit(args.func(args)) + finally: + cleanup_nm_connections() diff --git a/units/wireless/jobs.pxu b/units/wireless/jobs.pxu index 9c25c71..a186979 100644 --- a/units/wireless/jobs.pxu +++ b/units/wireless/jobs.pxu @@ -7,7 +7,7 @@ id: wireless/wireless_scanning_{{ interface }} _summary: Test system can discover Wi-Fi networks on {{ interface }} command: net_driver_info $NET_DRIVER_INFO - wifi_nmcli_test scan {{ interface }} + wifi_nmcli_test.py scan {{ interface }} plugin: shell category_id: com.canonical.plainbox::wireless estimated_duration: 6 @@ -31,7 +31,7 @@ _purpose: plugin: shell command: net_driver_info $NET_DRIVER_INFO - wifi_nmcli_test secured {{ interface }} "$WPA_BG_SSID" "$WPA_BG_PSK" + wifi_nmcli_test.py secured {{ interface }} "$WPA_BG_SSID" "$WPA_BG_PSK" category_id: com.canonical.plainbox::wireless estimated_duration: 30.0 flags: preserve-locale also-after-suspend also-after-suspend-manual @@ -52,7 +52,7 @@ _purpose: plugin: shell command: net_driver_info $NET_DRIVER_INFO - wifi_nmcli_test open {{ interface }} "$OPEN_BG_SSID" + wifi_nmcli_test.py open {{ interface }} "$OPEN_BG_SSID" category_id: com.canonical.plainbox::wireless estimated_duration: 30.0 flags: preserve-locale also-after-suspend also-after-suspend-manual @@ -73,7 +73,7 @@ _purpose: plugin: shell command: net_driver_info $NET_DRIVER_INFO - wifi_nmcli_test secured {{ interface }} "$WPA_N_SSID" "$WPA_N_PSK" + wifi_nmcli_test.py secured {{ interface }} "$WPA_N_SSID" "$WPA_N_PSK" category_id: com.canonical.plainbox::wireless estimated_duration: 30.0 flags: preserve-locale also-after-suspend also-after-suspend-manual @@ -94,7 +94,7 @@ _purpose: plugin: shell command: net_driver_info $NET_DRIVER_INFO - wifi_nmcli_test open {{ interface }} "$OPEN_N_SSID" + wifi_nmcli_test.py open {{ interface }} "$OPEN_N_SSID" category_id: com.canonical.plainbox::wireless estimated_duration: 30.0 flags: preserve-locale also-after-suspend also-after-suspend-manual @@ -115,7 +115,7 @@ _purpose: plugin: shell command: net_driver_info $NET_DRIVER_INFO - wifi_nmcli_test secured {{ interface }} "$WPA_AC_SSID" "$WPA_AC_PSK" + wifi_nmcli_test.py secured {{ interface }} "$WPA_AC_SSID" "$WPA_AC_PSK" category_id: com.canonical.plainbox::wireless estimated_duration: 30.0 flags: preserve-locale also-after-suspend also-after-suspend-manual @@ -137,7 +137,7 @@ _purpose: plugin: shell command: net_driver_info $NET_DRIVER_INFO - wifi_nmcli_test open {{ interface }} "$OPEN_AC_SSID" + wifi_nmcli_test.py open {{ interface }} "$OPEN_AC_SSID" category_id: com.canonical.plainbox::wireless estimated_duration: 30.0 flags: preserve-locale also-after-suspend also-after-suspend-manual -- cgit v1.2.3