mirror of
https://github.com/linux-system-roles/network.git
synced 2026-01-23 02:15:17 +00:00
Add support for wireless connections
WPA-PSK and WPA-EAP are supported. Uses existing 802.1x features of the role. Added extra functionality to ArgValidatorStr to enforce a min and max length.
This commit is contained in:
parent
47ad99c7f0
commit
51f8e5b05f
12 changed files with 807 additions and 23 deletions
36
README.md
36
README.md
|
|
@ -17,6 +17,7 @@ This role can be used to configure:
|
|||
- VLAN interfaces
|
||||
- MacVLAN interfaces
|
||||
- Infiniband interfaces
|
||||
- Wireless (WiFi) interfaces
|
||||
- IP configuration
|
||||
- 802.1x authentication
|
||||
|
||||
|
|
@ -63,10 +64,12 @@ List of variables:
|
|||
provider (`nm` or `initscripts`) . Setting it to `{{ network_provider_os_default }}`,
|
||||
the provider is set depending on the operating system. This is usually `nm`
|
||||
except for RHEL 6 or CentOS 6 systems.
|
||||
|
||||
* `network_connections` - The connection profiles are configured as `network_connections`,
|
||||
which is a list of dictionaries that include specific options.
|
||||
|
||||
* `network_allow_restart` - Certain configurations require the role to restart network services.
|
||||
For example, if a wireless connection is configured and NetworkManager-wifi is not installed,
|
||||
NetworkManager must be restarted prior to the connection being configured. Setting this to
|
||||
`no` will prevent the role from restarting network service.
|
||||
|
||||
Examples of Variables
|
||||
---------------------
|
||||
|
|
@ -78,6 +81,7 @@ network_provider: nm
|
|||
network_connections:
|
||||
- name: eth0
|
||||
#...
|
||||
network_allow_restart: yes
|
||||
```
|
||||
|
||||
Options
|
||||
|
|
@ -186,6 +190,7 @@ The `type` option can be set to the following values:
|
|||
- `vlan`
|
||||
- `macvlan`
|
||||
- `infiniband`
|
||||
- `wireless`
|
||||
|
||||
#### `type: ethernet`
|
||||
|
||||
|
|
@ -232,6 +237,19 @@ role.
|
|||
Similar to `master` and `vlan`, the `parent` references the connection profile in the ansible
|
||||
role.
|
||||
|
||||
#### `type: wireless`
|
||||
|
||||
The `wireless` type supports WPA-PSK (password) authentication and WPA-EAP (802.1x) authentication.
|
||||
|
||||
`nm` (NetworkManager) is the only supported `network_provider` for this type.
|
||||
|
||||
If WPA-EAP is used, ieee802_1x settings must be defined in the [ieee802_1x](#-`ieee802_1x`) option.
|
||||
|
||||
The following options are supported:
|
||||
|
||||
* `ssid`: the SSID of the wireless network (required)
|
||||
* `key_mgmt`: `wpa-psk` or `wpa-eap` (required)
|
||||
* `password`: password for the network (required if `wpa-psk` is used)
|
||||
|
||||
### `autoconnect`
|
||||
|
||||
|
|
@ -642,6 +660,20 @@ network_connections:
|
|||
- 192.168.1.1/24
|
||||
```
|
||||
|
||||
Configuring a wireless connection:
|
||||
|
||||
```yaml
|
||||
network_connections:
|
||||
- name: wlan0
|
||||
type: wireless
|
||||
wireless:
|
||||
ssid: "My WPA2-PSK Network"
|
||||
key_mgmt: "wpa-psk"
|
||||
# recommend vault encrypting the wireless password
|
||||
# see https://docs.ansible.com/ansible/latest/user_guide/vault.html
|
||||
password: "p@55w0rD"
|
||||
```
|
||||
|
||||
Setting the IP configuration:
|
||||
|
||||
```yaml
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
---
|
||||
network_connections: []
|
||||
|
||||
network_allow_restart: no
|
||||
|
||||
# Use initscripts for RHEL/CentOS < 7, nm otherwise
|
||||
network_provider_os_default: "{{
|
||||
'initscripts' if ansible_distribution in [
|
||||
|
|
@ -20,10 +22,25 @@ __network_provider_current: "{{
|
|||
# Default to the auto-detected value
|
||||
network_provider: "{{ __network_provider_current }}"
|
||||
|
||||
# wpa_supplicant is required if any ieee802_1x connections are defined
|
||||
__network_wpa_supplicant_required: "{{ network_connections |
|
||||
# check if any 802.1x connections are defined
|
||||
__network_ieee802_1x_connections_defined: "{{ network_connections |
|
||||
selectattr('ieee802_1x', 'defined') | list | count > 0 }}"
|
||||
__network_packages_default_802_1x: ["{% if __network_wpa_supplicant_required
|
||||
|
||||
# check if any wireless connections are defined
|
||||
__network_wireless_connections_defined: "{{
|
||||
['wireless'] in network_connections|json_query('[*][type]') }}"
|
||||
|
||||
# NetworkManager-wireless is required for wireless connections
|
||||
__network_packages_default_wireless: ["{%
|
||||
if __network_wireless_connections_defined
|
||||
%}NetworkManager-wifi{% endif %}"]
|
||||
|
||||
# wpa_supplicant is required if any 802.1x or wireless connections are defined
|
||||
__network_wpa_supplicant_required: "{{
|
||||
__network_ieee802_1x_connections_defined or
|
||||
__network_wireless_connections_defined }}"
|
||||
__network_packages_default_wpa_supplicant: ["{%
|
||||
if __network_wpa_supplicant_required
|
||||
%}wpa_supplicant{% endif %}"]
|
||||
|
||||
# The python-gobject-base package depends on the python version and
|
||||
|
|
@ -36,7 +53,8 @@ __network_packages_default_gobject_packages: ["python{{
|
|||
__network_service_name_default_nm: NetworkManager
|
||||
__network_packages_default_nm: "{{['NetworkManager']
|
||||
+ __network_packages_default_gobject_packages|select()|list()
|
||||
+ __network_packages_default_802_1x|select()|list()}}"
|
||||
+ __network_packages_default_wpa_supplicant|select()|list()
|
||||
+ __network_packages_default_wireless|select()|list()}}"
|
||||
|
||||
__network_service_name_default_initscripts: network
|
||||
|
||||
|
|
|
|||
13
examples/wireless_wpa_psk.yml
Normal file
13
examples/wireless_wpa_psk.yml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
---
|
||||
- hosts: network-test
|
||||
vars:
|
||||
network_connections:
|
||||
- name: wlan0
|
||||
type: wireless
|
||||
wireless:
|
||||
ssid: "My WPA2-PSK Network"
|
||||
key_mgmt: "wpa-psk"
|
||||
# recommend vault encrypting the wireless password
|
||||
# see https://docs.ansible.com/ansible/latest/user_guide/vault.html
|
||||
password: "p@55w0rD"
|
||||
|
|
@ -836,6 +836,28 @@ class NMUtil:
|
|||
NM.SETTING_MACVLAN_PARENT,
|
||||
ArgUtil.connection_find_master(connection["parent"], connections, idx),
|
||||
)
|
||||
elif connection["type"] == "wireless":
|
||||
s_con.set_property(
|
||||
NM.SETTING_CONNECTION_TYPE, NM.SETTING_WIRELESS_SETTING_NAME
|
||||
)
|
||||
s_wireless = self.connection_ensure_setting(con, NM.SettingWireless)
|
||||
s_wireless.set_property(
|
||||
NM.SETTING_WIRELESS_SSID,
|
||||
Util.GLib().Bytes.new(connection["wireless"]["ssid"].encode("utf-8")),
|
||||
)
|
||||
|
||||
s_wireless_sec = self.connection_ensure_setting(
|
||||
con, NM.SettingWirelessSecurity
|
||||
)
|
||||
s_wireless_sec.set_property(
|
||||
NM.SETTING_WIRELESS_SECURITY_KEY_MGMT,
|
||||
connection["wireless"]["key_mgmt"],
|
||||
)
|
||||
|
||||
if connection["wireless"]["key_mgmt"] == "wpa-psk":
|
||||
s_wireless_sec.set_property(
|
||||
NM.SETTING_WIRELESS_SECURITY_PSK, connection["wireless"]["password"]
|
||||
)
|
||||
else:
|
||||
raise MyError("unsupported type %s" % (connection["type"]))
|
||||
|
||||
|
|
|
|||
|
|
@ -115,11 +115,27 @@ class ArgValidatorStr(ArgValidator):
|
|||
default_value=None,
|
||||
enum_values=None,
|
||||
allow_empty=False,
|
||||
min_length=None,
|
||||
max_length=None,
|
||||
):
|
||||
ArgValidator.__init__(self, name, required, default_value)
|
||||
self.enum_values = enum_values
|
||||
self.allow_empty = allow_empty
|
||||
|
||||
if max_length is not None:
|
||||
if not isinstance(max_length, int):
|
||||
raise ValueError("max_length must be an integer")
|
||||
elif max_length < 0:
|
||||
raise ValueError("max_length must be a positive integer")
|
||||
self.max_length = max_length
|
||||
|
||||
if min_length is not None:
|
||||
if not isinstance(min_length, int):
|
||||
raise ValueError("min_length must be an integer")
|
||||
elif min_length < 0:
|
||||
raise ValueError("min_length must be a positive integer")
|
||||
self.min_length = min_length
|
||||
|
||||
def _validate_impl(self, value, name):
|
||||
if not isinstance(value, Util.STRING_TYPE):
|
||||
raise ValidationError(name, "must be a string but is '%s'" % (value))
|
||||
|
|
@ -132,8 +148,36 @@ class ArgValidatorStr(ArgValidator):
|
|||
)
|
||||
if not self.allow_empty and not value:
|
||||
raise ValidationError(name, "cannot be empty")
|
||||
if not self._validate_string_max_length(value):
|
||||
raise ValidationError(
|
||||
name, "maximum length is %s characters" % (self.max_length)
|
||||
)
|
||||
if not self._validate_string_min_length(value):
|
||||
raise ValidationError(
|
||||
name, "minimum length is %s characters" % (self.min_length)
|
||||
)
|
||||
return value
|
||||
|
||||
def _validate_string_max_length(self, value):
|
||||
"""
|
||||
Ensures that the length of string `value` is less than or equal to
|
||||
the maximum length
|
||||
"""
|
||||
if self.max_length is not None:
|
||||
return len(str(value)) <= self.max_length
|
||||
else:
|
||||
return True
|
||||
|
||||
def _validate_string_min_length(self, value):
|
||||
"""
|
||||
Ensures that the length of string `value` is more than or equal to
|
||||
the minimum length
|
||||
"""
|
||||
if self.min_length is not None:
|
||||
return len(str(value)) >= self.min_length
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
class ArgValidatorNum(ArgValidator):
|
||||
def __init__( # pylint: disable=too-many-arguments
|
||||
|
|
@ -887,6 +931,42 @@ class ArgValidator_Dict802_1X(ArgValidatorDict):
|
|||
return result
|
||||
|
||||
|
||||
class ArgValidator_DictWireless(ArgValidatorDict):
|
||||
|
||||
VALID_KEY_MGMT = [
|
||||
"wpa-psk",
|
||||
"wpa-eap",
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
ArgValidatorDict.__init__(
|
||||
self,
|
||||
name="wireless",
|
||||
nested=[
|
||||
ArgValidatorStr("ssid", max_length=32),
|
||||
ArgValidatorStr(
|
||||
"key_mgmt", enum_values=ArgValidator_DictWireless.VALID_KEY_MGMT
|
||||
),
|
||||
ArgValidatorStr("password", default_value=None, max_length=63),
|
||||
],
|
||||
default_value=None,
|
||||
)
|
||||
|
||||
def _validate_post(self, value, name, result):
|
||||
if result["key_mgmt"] == "wpa-psk":
|
||||
if result["password"] is None:
|
||||
raise ValidationError(
|
||||
name, "must supply a password if using 'wpa-psk' key management",
|
||||
)
|
||||
else:
|
||||
if result["password"] is not None:
|
||||
raise ValidationError(
|
||||
name, "password only allowed if using 'wpa-psk' key management",
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class ArgValidator_DictConnection(ArgValidatorDict):
|
||||
|
||||
VALID_PERSISTENT_STATES = ["absent", "present"]
|
||||
|
|
@ -899,6 +979,7 @@ class ArgValidator_DictConnection(ArgValidatorDict):
|
|||
"bond",
|
||||
"vlan",
|
||||
"macvlan",
|
||||
"wireless",
|
||||
]
|
||||
VALID_SLAVE_TYPES = ["bridge", "bond", "team"]
|
||||
|
||||
|
|
@ -949,6 +1030,7 @@ class ArgValidator_DictConnection(ArgValidatorDict):
|
|||
ArgValidator_DictVlan(),
|
||||
ArgValidator_DictMacvlan(),
|
||||
ArgValidator_Dict802_1X(),
|
||||
ArgValidator_DictWireless(),
|
||||
# deprecated options:
|
||||
ArgValidatorStr(
|
||||
"infiniband_transport_mode",
|
||||
|
|
@ -1081,9 +1163,42 @@ class ArgValidator_DictConnection(ArgValidatorDict):
|
|||
self.VALID_FIELDS = valid_fields
|
||||
return result
|
||||
|
||||
def _validate_post_wireless(self, value, name, result):
|
||||
"""
|
||||
Validate wireless settings
|
||||
"""
|
||||
if "type" in result:
|
||||
if result["type"] == "wireless":
|
||||
if "wireless" in result:
|
||||
if (
|
||||
result["wireless"]["key_mgmt"] == "wpa-eap"
|
||||
and "ieee802_1x" not in result
|
||||
):
|
||||
raise ValidationError(
|
||||
name + ".wireless",
|
||||
"key management set to wpa-eap but no "
|
||||
"'ieee802_1x' settings defined",
|
||||
)
|
||||
else:
|
||||
raise ValidationError(
|
||||
name + ".wireless",
|
||||
"must define 'wireless' settings for 'type' 'wireless'",
|
||||
)
|
||||
|
||||
else:
|
||||
if "wireless" in result:
|
||||
raise ValidationError(
|
||||
name + ".wireless",
|
||||
"'wireless' settings are not allowed for 'type' '%s'"
|
||||
% (result["type"]),
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
def _validate_post(self, value, name, result):
|
||||
result = self._validate_post_state(value, name, result)
|
||||
result = self._validate_post_fields(value, name, result)
|
||||
result = self._validate_post_wireless(value, name, result)
|
||||
|
||||
if "type" in result:
|
||||
|
||||
|
|
@ -1299,6 +1414,15 @@ class ArgValidator_DictConnection(ArgValidatorDict):
|
|||
% (result["type"]),
|
||||
)
|
||||
|
||||
if "ieee802_1x" in result and result["type"] not in [
|
||||
"ethernet",
|
||||
"wireless",
|
||||
]:
|
||||
raise ValidationError(
|
||||
name + ".ieee802_1x",
|
||||
"802.1x settings only allowed for ethernet or wireless interfaces.",
|
||||
)
|
||||
|
||||
for k in self.VALID_FIELDS:
|
||||
if k in result:
|
||||
continue
|
||||
|
|
@ -1409,7 +1533,12 @@ class ArgValidator_ListConnections(ArgValidatorList):
|
|||
"if you need to use initscripts.",
|
||||
)
|
||||
|
||||
if connection["type"] != "ethernet":
|
||||
# check if wireless connection is valid
|
||||
if connection["type"] == "wireless":
|
||||
if mode == self.VALIDATE_ONE_MODE_INITSCRIPTS:
|
||||
raise ValidationError.from_connection(
|
||||
idx, "802.1x settings only allowed for ethernet interfaces."
|
||||
idx,
|
||||
"Wireless WPA auth is not supported by initscripts. "
|
||||
"Configure wireless connection in /etc/wpa_supplicant.conf "
|
||||
"if you need to use initscripts.",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,6 +23,22 @@
|
|||
state: present
|
||||
when:
|
||||
- not network_packages is subset(ansible_facts.packages.keys())
|
||||
register: __network_package_install
|
||||
|
||||
# If network packages have changed and wireless connections are required,
|
||||
# NetworkManager must be restarted
|
||||
- name: Restart NetworkManager
|
||||
service:
|
||||
name: NetworkManager
|
||||
state: restarted
|
||||
when:
|
||||
- __network_wireless_connections_defined
|
||||
- network_provider == "nm"
|
||||
- network_allow_restart
|
||||
# ansible-lint wants this to be a handler, but this is not appropriate as
|
||||
# NetworkManager must be restarted prior to the connections being created.
|
||||
# see (https://docs.ansible.com/ansible-lint/rules/default_rules.html)
|
||||
- __network_package_install.changed # noqa 503
|
||||
|
||||
- name: Enable and start NetworkManager
|
||||
service:
|
||||
|
|
|
|||
|
|
@ -61,10 +61,15 @@ NM_ONLY_TESTS = {
|
|||
MINIMUM_VERSION: "'1.20.0'",
|
||||
"comment": "# NetworkManager 1.20.0 introduced ethtool settings support",
|
||||
},
|
||||
"playbooks/tests_802_1x_updated.yml": {},
|
||||
"playbooks/tests_802_1x.yml": {},
|
||||
"playbooks/tests_reapply.yml": {},
|
||||
"playbooks/tests_states.yml": {},
|
||||
"playbooks/tests_802_1x.yml": {},
|
||||
"playbooks/tests_802_1x_updated.yml": {},
|
||||
# mac80211_hwsim (used for tests_wireless) only seems to be available
|
||||
# and working on RHEL/CentOS 7
|
||||
"playbooks/tests_wireless.yml": {
|
||||
EXTRA_RUN_CONDITION: "ansible_distribution_major_version == '7'",
|
||||
},
|
||||
}
|
||||
|
||||
IGNORE = [
|
||||
|
|
|
|||
88
tests/playbooks/tests_wireless.yml
Normal file
88
tests/playbooks/tests_wireless.yml
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
---
|
||||
- hosts: all
|
||||
vars:
|
||||
interface: wlan0
|
||||
tasks:
|
||||
- name: "INIT: wireless tests"
|
||||
debug:
|
||||
msg: "##################################################"
|
||||
- include_tasks: tasks/setup_mock_wifi.yml
|
||||
- name: Copy client certs
|
||||
copy:
|
||||
src: "{{ item }}"
|
||||
dest: "/etc/pki/tls/{{ item }}"
|
||||
mode: 0644
|
||||
with_items:
|
||||
- client.key
|
||||
- client.pem
|
||||
- cacert.pem
|
||||
- block:
|
||||
- name: "TEST: wireless connection with WPA-PSK"
|
||||
debug:
|
||||
msg: "##################################################"
|
||||
- import_role:
|
||||
name: linux-system-roles.network
|
||||
vars:
|
||||
network_allow_restart: true
|
||||
network_connections:
|
||||
- name: "{{ interface }}"
|
||||
state: up
|
||||
type: wireless
|
||||
ip:
|
||||
address:
|
||||
- 203.0.113.2/24
|
||||
dhcp4: "no"
|
||||
auto6: "no"
|
||||
wireless:
|
||||
ssid: "mock_wifi"
|
||||
key_mgmt: "wpa-psk"
|
||||
password: "p@55w0rD"
|
||||
- import_role:
|
||||
name: linux-system-roles.network
|
||||
vars:
|
||||
network_connections:
|
||||
- name: "{{ interface }}"
|
||||
persistent_state: absent
|
||||
state: down
|
||||
- name: "TEST: wireless connection with 802.1x TLS-EAP"
|
||||
debug:
|
||||
msg: "##################################################"
|
||||
- import_role:
|
||||
name: linux-system-roles.network
|
||||
vars:
|
||||
network_allow_restart: true
|
||||
network_connections:
|
||||
- name: "{{ interface }}"
|
||||
state: up
|
||||
type: wireless
|
||||
ip:
|
||||
address:
|
||||
- 203.0.113.2/24
|
||||
dhcp4: "no"
|
||||
auto6: "no"
|
||||
wireless:
|
||||
ssid: "mock_wifi"
|
||||
key_mgmt: "wpa-eap"
|
||||
ieee802_1x:
|
||||
identity: myhost
|
||||
eap: tls
|
||||
private_key: /etc/pki/tls/client.key
|
||||
private_key_password: test
|
||||
private_key_password_flags:
|
||||
- none
|
||||
client_cert: /etc/pki/tls/client.pem
|
||||
ca_cert: /etc/pki/tls/cacert.pem
|
||||
always:
|
||||
- block:
|
||||
- import_role:
|
||||
name: linux-system-roles.network
|
||||
vars:
|
||||
network_connections:
|
||||
- name: "{{ interface }}"
|
||||
persistent_state: absent
|
||||
state: down
|
||||
ignore_errors: true
|
||||
- include_tasks: tasks/cleanup_mock_wifi.yml
|
||||
tags:
|
||||
- "tests::cleanup"
|
||||
7
tests/tasks/cleanup_mock_wifi.yml
Normal file
7
tests/tasks/cleanup_mock_wifi.yml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
---
|
||||
- name: Unload mac80211_hwsim module
|
||||
shell: modprobe -r mac80211_hwsim
|
||||
|
||||
- name: Kill hostapd process
|
||||
shell: pkill hostapd
|
||||
91
tests/tasks/setup_mock_wifi.yml
Normal file
91
tests/tasks/setup_mock_wifi.yml
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
---
|
||||
- name: Install EPEL on enterprise Linux for hostapd
|
||||
# yamllint disable-line rule:line-length
|
||||
command: yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-{{ ansible_distribution_major_version }}.noarch.rpm
|
||||
args:
|
||||
warn: false
|
||||
creates: /etc/yum.repos.d/epel.repo
|
||||
when:
|
||||
- ansible_distribution in ['RedHat', 'CentOS']
|
||||
|
||||
- name: Install packages required to set up mock wifi network
|
||||
package:
|
||||
name:
|
||||
- hostapd
|
||||
- NetworkManager
|
||||
- wpa_supplicant
|
||||
state: present
|
||||
|
||||
- name: Ensure NetworkManager is running
|
||||
service:
|
||||
name: NetworkManager
|
||||
state: started
|
||||
|
||||
- name: Copy server certificates
|
||||
copy:
|
||||
src: "{{ item }}"
|
||||
dest: "/etc/pki/tls/{{ item }}"
|
||||
with_items:
|
||||
- server.key
|
||||
- dh.pem
|
||||
- server.pem
|
||||
- cacert.pem
|
||||
|
||||
- name: Create hostapd config
|
||||
copy:
|
||||
content: |
|
||||
interface=wlan1
|
||||
driver=nl80211
|
||||
ctrl_interface=/var/run/hostapd
|
||||
ctrl_interface_group=0
|
||||
ssid=mock_wifi
|
||||
country_code=EN
|
||||
hw_mode=g
|
||||
channel=7
|
||||
auth_algs=3
|
||||
wpa=3
|
||||
ieee8021x=1
|
||||
eapol_version=1
|
||||
wpa_key_mgmt=WPA-EAP WPA-PSK
|
||||
wpa_passphrase=p@55w0rD
|
||||
eap_reauth_period=3600
|
||||
eap_server=1
|
||||
use_pae_group_addr=1
|
||||
eap_user_file=/etc/hostapd/hostapd.eap_user
|
||||
ca_cert=/etc/pki/tls/cacert.pem
|
||||
dh_file=/etc/pki/tls/dh.pem
|
||||
server_cert=/etc/pki/tls/server.pem
|
||||
private_key=/etc/pki/tls/server.key
|
||||
private_key_passwd=test
|
||||
logger_syslog=-1
|
||||
logger_syslog_level=0
|
||||
dest: /etc/hostapd/wireless.conf
|
||||
|
||||
- name: Create eap_user_file config
|
||||
copy:
|
||||
content: |
|
||||
* TLS
|
||||
dest: /etc/hostapd/hostapd.eap_user
|
||||
|
||||
- name: Load mac80211_hwsim kernel module to mock a wifi network
|
||||
shell: modprobe mac80211_hwsim && sleep 5
|
||||
|
||||
- name: Restart NetworkManager and wpa_supplicant
|
||||
service:
|
||||
name: "{{ item }}"
|
||||
state: restarted
|
||||
with_items:
|
||||
- NetworkManager
|
||||
- wpa_supplicant
|
||||
|
||||
- name: Configure wlan0 and wlan1 (mock wifi interfaces)
|
||||
shell: |
|
||||
ip link set up wlan0
|
||||
ip link set up wlan1
|
||||
nmcli device set wlan1 managed off
|
||||
ip add add 203.0.113.1/24 dev wlan1
|
||||
sleep 5
|
||||
|
||||
- name: Start hostapd
|
||||
shell: hostapd -B /etc/hostapd/wireless.conf && sleep 5
|
||||
22
tests/tests_wireless_nm.yml
Normal file
22
tests/tests_wireless_nm.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
# This file was generated by ensure_provider_tests.py
|
||||
---
|
||||
# set network provider and gather facts
|
||||
- hosts: all
|
||||
name: Run playbook 'playbooks/tests_wireless.yml' with nm as provider
|
||||
tasks:
|
||||
- name: Set network provider to 'nm'
|
||||
set_fact:
|
||||
network_provider: nm
|
||||
|
||||
|
||||
# workaround for: https://github.com/ansible/ansible/issues/27973
|
||||
# There is no way in Ansible to abort a playbook hosts with specific OS
|
||||
# releases Therefore we include the playbook with the tests only if the hosts
|
||||
# would support it.
|
||||
# The test requires or should run with NetworkManager, therefore it cannot run
|
||||
# on RHEL/CentOS 6
|
||||
- import_playbook: playbooks/tests_wireless.yml
|
||||
when:
|
||||
- ansible_distribution_major_version != '6'
|
||||
- ansible_distribution_major_version == '7'
|
||||
|
|
@ -156,6 +156,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": None,
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "5",
|
||||
"parent": None,
|
||||
|
|
@ -252,7 +253,10 @@ class TestValidator(unittest.TestCase):
|
|||
continue
|
||||
if "type" not in connection:
|
||||
continue
|
||||
if connection["type"] in ["macvlan"] or connection["ieee802_1x"]:
|
||||
if (
|
||||
connection["type"] in ["macvlan", "wireless"]
|
||||
or connection["ieee802_1x"]
|
||||
):
|
||||
# initscripts do not support this type. Skip the test.
|
||||
continue
|
||||
content_current = kwargs.get("initscripts_content_current", None)
|
||||
|
|
@ -284,6 +288,50 @@ class TestValidator(unittest.TestCase):
|
|||
v = network_lsr.argument_validator.ArgValidatorStr("state", required=True)
|
||||
self.assertValidationError(v, None)
|
||||
|
||||
v = network_lsr.argument_validator.ArgValidatorStr(
|
||||
"test_max_length", max_length=13
|
||||
)
|
||||
self.assertEqual("less_than_13", v.validate("less_than_13"))
|
||||
self.assertValidationError(v, "longer_than_13")
|
||||
|
||||
v = network_lsr.argument_validator.ArgValidatorStr(
|
||||
"test_min_length", min_length=13
|
||||
)
|
||||
self.assertEqual("longer_than_13", v.validate("longer_than_13"))
|
||||
self.assertValidationError(v, "less_than_13")
|
||||
|
||||
v = network_lsr.argument_validator.ArgValidatorStr(
|
||||
"test_min_max_length", min_length=10, max_length=15
|
||||
)
|
||||
self.assertEqual("13_characters", v.validate("13_characters"))
|
||||
self.assertValidationError(v, "too_short")
|
||||
self.assertValidationError(v, "string_is_too_long")
|
||||
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
network_lsr.argument_validator.ArgValidatorStr,
|
||||
"non_int",
|
||||
min_length="string",
|
||||
)
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
network_lsr.argument_validator.ArgValidatorStr,
|
||||
"non_int",
|
||||
max_length="string",
|
||||
)
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
network_lsr.argument_validator.ArgValidatorStr,
|
||||
"negative_int",
|
||||
min_length=-5,
|
||||
)
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
network_lsr.argument_validator.ArgValidatorStr,
|
||||
"negative_int",
|
||||
max_length=-5,
|
||||
)
|
||||
|
||||
def test_validate_int(self):
|
||||
|
||||
v = network_lsr.argument_validator.ArgValidatorNum(
|
||||
|
|
@ -399,6 +447,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": None,
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "5",
|
||||
"parent": None,
|
||||
|
|
@ -450,6 +499,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": None,
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "5",
|
||||
"parent": None,
|
||||
|
|
@ -495,6 +545,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": None,
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "5",
|
||||
"parent": None,
|
||||
|
|
@ -583,6 +634,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": "52:54:00:44:9f:ba",
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": 1450,
|
||||
"name": "prod1",
|
||||
"parent": None,
|
||||
|
|
@ -645,6 +697,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": None,
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "prod1",
|
||||
"parent": None,
|
||||
|
|
@ -709,6 +762,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": "52:54:00:44:9f:ba",
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": 1450,
|
||||
"name": "prod1",
|
||||
"parent": None,
|
||||
|
|
@ -765,6 +819,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": None,
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "prod.100",
|
||||
"parent": "prod1",
|
||||
|
|
@ -848,6 +903,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": "52:54:00:44:9f:ba",
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": 1450,
|
||||
"name": "prod1",
|
||||
"parent": None,
|
||||
|
|
@ -904,6 +960,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": None,
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "prod.100",
|
||||
"parent": "prod1",
|
||||
|
|
@ -982,6 +1039,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": "33:24:10:24:2f:b9",
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": 1450,
|
||||
"name": "eth0-parent",
|
||||
"parent": None,
|
||||
|
|
@ -1033,6 +1091,7 @@ class TestValidator(unittest.TestCase):
|
|||
"macvlan": {"mode": "bridge", "promiscuous": True, "tap": False},
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "veth0.0",
|
||||
"parent": "eth0-parent",
|
||||
|
|
@ -1084,6 +1143,7 @@ class TestValidator(unittest.TestCase):
|
|||
"macvlan": {"mode": "passthru", "promiscuous": False, "tap": True},
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "veth0.1",
|
||||
"parent": "eth0-parent",
|
||||
|
|
@ -1170,6 +1230,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": None,
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "prod2",
|
||||
"parent": None,
|
||||
|
|
@ -1207,6 +1268,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": None,
|
||||
"master": "prod2",
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "prod2-slave1",
|
||||
"parent": None,
|
||||
|
|
@ -1268,6 +1330,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": None,
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "bond1",
|
||||
"parent": None,
|
||||
|
|
@ -1314,6 +1377,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": None,
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "bond1",
|
||||
"parent": None,
|
||||
|
|
@ -1370,6 +1434,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": "aa:bb:cc:dd:ee:ff",
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "5",
|
||||
"parent": None,
|
||||
|
|
@ -1414,6 +1479,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": None,
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "5",
|
||||
"parent": None,
|
||||
|
|
@ -1486,6 +1552,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": None,
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "6643-master",
|
||||
"parent": None,
|
||||
|
|
@ -1523,6 +1590,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": None,
|
||||
"master": "6643-master",
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "6643",
|
||||
"parent": None,
|
||||
|
|
@ -1576,6 +1644,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": None,
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "infiniband.1",
|
||||
"parent": None,
|
||||
|
|
@ -1647,6 +1716,7 @@ class TestValidator(unittest.TestCase):
|
|||
"11:22:33:44:55:66:77:88:99:00",
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "infiniband.2",
|
||||
"parent": None,
|
||||
|
|
@ -1738,6 +1808,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": None,
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "555",
|
||||
"parent": None,
|
||||
|
|
@ -1837,6 +1908,7 @@ class TestValidator(unittest.TestCase):
|
|||
"mac": None,
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "e556",
|
||||
"parent": None,
|
||||
|
|
@ -1960,6 +2032,7 @@ class TestValidator(unittest.TestCase):
|
|||
"system_ca_certs": False,
|
||||
"domain_suffix_match": None,
|
||||
},
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "eth0",
|
||||
"parent": None,
|
||||
|
|
@ -2034,6 +2107,7 @@ class TestValidator(unittest.TestCase):
|
|||
"system_ca_certs": True,
|
||||
"domain_suffix_match": "example.com",
|
||||
},
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "eth0",
|
||||
"parent": None,
|
||||
|
|
@ -2108,6 +2182,7 @@ class TestValidator(unittest.TestCase):
|
|||
"system_ca_certs": False,
|
||||
"domain_suffix_match": None,
|
||||
},
|
||||
"wireless": None,
|
||||
"mtu": None,
|
||||
"name": "eth0",
|
||||
"parent": None,
|
||||
|
|
@ -2136,6 +2211,150 @@ class TestValidator(unittest.TestCase):
|
|||
],
|
||||
)
|
||||
|
||||
def test_wireless_psk(self):
|
||||
"""
|
||||
Test wireless connection with wpa-psk auth
|
||||
"""
|
||||
self.maxDiff = None
|
||||
self.do_connections_validate(
|
||||
[
|
||||
{
|
||||
"actions": ["present", "up"],
|
||||
"autoconnect": True,
|
||||
"check_iface_exists": True,
|
||||
"ethtool": ETHTOOL_DEFAULTS,
|
||||
"force_state_change": None,
|
||||
"ignore_errors": None,
|
||||
"interface_name": "wireless1",
|
||||
"ip": {
|
||||
"gateway6": None,
|
||||
"gateway4": None,
|
||||
"route_metric4": None,
|
||||
"auto6": True,
|
||||
"dhcp4": True,
|
||||
"address": [],
|
||||
"route_append_only": False,
|
||||
"rule_append_only": False,
|
||||
"route": [],
|
||||
"dns": [],
|
||||
"dns_search": [],
|
||||
"route_metric6": None,
|
||||
"dhcp4_send_hostname": None,
|
||||
},
|
||||
"mac": None,
|
||||
"master": None,
|
||||
"ieee802_1x": None,
|
||||
"wireless": {
|
||||
"ssid": "test wireless network",
|
||||
"key_mgmt": "wpa-psk",
|
||||
"password": "p@55w0rD",
|
||||
},
|
||||
"mtu": None,
|
||||
"name": "wireless1",
|
||||
"parent": None,
|
||||
"persistent_state": "present",
|
||||
"slave_type": None,
|
||||
"state": "up",
|
||||
"type": "wireless",
|
||||
"wait": None,
|
||||
"zone": None,
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "wireless1",
|
||||
"state": "up",
|
||||
"type": "wireless",
|
||||
"wireless": {
|
||||
"ssid": "test wireless network",
|
||||
"key_mgmt": "wpa-psk",
|
||||
"password": "p@55w0rD",
|
||||
},
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
def test_wireless_eap(self):
|
||||
"""
|
||||
Test wireless connection with wpa-eap
|
||||
"""
|
||||
self.maxDiff = None
|
||||
self.do_connections_validate(
|
||||
[
|
||||
{
|
||||
"actions": ["present", "up"],
|
||||
"autoconnect": True,
|
||||
"check_iface_exists": True,
|
||||
"ethtool": ETHTOOL_DEFAULTS,
|
||||
"force_state_change": None,
|
||||
"ignore_errors": None,
|
||||
"interface_name": "wireless1",
|
||||
"ip": {
|
||||
"gateway6": None,
|
||||
"gateway4": None,
|
||||
"route_metric4": None,
|
||||
"auto6": True,
|
||||
"dhcp4": True,
|
||||
"address": [],
|
||||
"route_append_only": False,
|
||||
"rule_append_only": False,
|
||||
"route": [],
|
||||
"dns": [],
|
||||
"dns_search": [],
|
||||
"route_metric6": None,
|
||||
"dhcp4_send_hostname": None,
|
||||
},
|
||||
"mac": None,
|
||||
"master": None,
|
||||
"ieee802_1x": {
|
||||
"identity": "myhost",
|
||||
"eap": "tls",
|
||||
"private_key": "/etc/pki/tls/client.key",
|
||||
"private_key_password": "p@55w0rD",
|
||||
"private_key_password_flags": None,
|
||||
"client_cert": "/etc/pki/tls/client.pem",
|
||||
"ca_cert": "/etc/pki/tls/cacert.pem",
|
||||
"ca_path": None,
|
||||
"system_ca_certs": False,
|
||||
"domain_suffix_match": None,
|
||||
},
|
||||
"wireless": {
|
||||
"ssid": "test wireless network",
|
||||
"password": None,
|
||||
"key_mgmt": "wpa-eap",
|
||||
},
|
||||
"mtu": None,
|
||||
"name": "wireless1",
|
||||
"parent": None,
|
||||
"persistent_state": "present",
|
||||
"slave_type": None,
|
||||
"state": "up",
|
||||
"type": "wireless",
|
||||
"wait": None,
|
||||
"zone": None,
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "wireless1",
|
||||
"state": "up",
|
||||
"type": "wireless",
|
||||
"wireless": {
|
||||
"ssid": "test wireless network",
|
||||
"key_mgmt": "wpa-eap",
|
||||
},
|
||||
"ieee802_1x": {
|
||||
"identity": "myhost",
|
||||
"eap": "tls",
|
||||
"private_key": "/etc/pki/tls/client.key",
|
||||
"private_key_password": "p@55w0rD",
|
||||
"client_cert": "/etc/pki/tls/client.pem",
|
||||
"ca_cert": "/etc/pki/tls/cacert.pem",
|
||||
},
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
def test_invalid_cert_path(self):
|
||||
"""
|
||||
should fail if a relative path is used for 802.1x certs/keys
|
||||
|
|
@ -2236,23 +2455,41 @@ class TestValidator(unittest.TestCase):
|
|||
0,
|
||||
)
|
||||
|
||||
def test_802_1x_non_ethernet(self):
|
||||
def test_802_1x_unsupported_type(self):
|
||||
"""
|
||||
should fail if a non-ethernet interface has 802.1x settings defined
|
||||
should fail if a non ethernet/wireless connection has 802.1x settings defined
|
||||
"""
|
||||
self.do_connections_check_invalid(
|
||||
[
|
||||
{
|
||||
"name": "bond0",
|
||||
"state": "up",
|
||||
"type": "bond",
|
||||
"ieee802_1x": {
|
||||
"identity": "myhost",
|
||||
"eap": "tls",
|
||||
"private_key": "/etc/pki/tls/client.key",
|
||||
"client_cert": "/etc/pki/tls/client.pem",
|
||||
"private_key_password_flags": ["not-required"],
|
||||
"system_ca_certs": True,
|
||||
},
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
def test_wireless_initscripts(self):
|
||||
"""
|
||||
should fail to create wireless connection with initscripts
|
||||
"""
|
||||
input_connections = [
|
||||
{
|
||||
"name": "bond0",
|
||||
"name": "wireless1",
|
||||
"state": "up",
|
||||
"type": "bond",
|
||||
"ieee802_1x": {
|
||||
"identity": "myhost",
|
||||
"eap": "tls",
|
||||
"private_key": "/etc/pki/tls/client.key",
|
||||
"client_cert": "/etc/pki/tls/client.pem",
|
||||
"private_key_password_flags": ["not-required"],
|
||||
"system_ca_certs": True,
|
||||
"type": "wireless",
|
||||
"wireless": {
|
||||
"ssid": "test wireless network",
|
||||
"key_mgmt": "wpa-psk",
|
||||
"password": "p@55w0rD",
|
||||
},
|
||||
}
|
||||
]
|
||||
|
|
@ -2262,11 +2499,115 @@ class TestValidator(unittest.TestCase):
|
|||
self.assertRaises(
|
||||
n.ValidationError,
|
||||
ARGS_CONNECTIONS.validate_connection_one,
|
||||
VALIDATE_ONE_MODE_NM,
|
||||
VALIDATE_ONE_MODE_INITSCRIPTS,
|
||||
connections,
|
||||
0,
|
||||
)
|
||||
|
||||
def test_wireless_unsupported_type(self):
|
||||
"""
|
||||
should fail if a non wireless connection has wireless settings defined
|
||||
"""
|
||||
self.do_connections_check_invalid(
|
||||
[
|
||||
{
|
||||
"name": "wireless-bond",
|
||||
"state": "up",
|
||||
"type": "bond",
|
||||
"wireless": {
|
||||
"ssid": "test wireless network",
|
||||
"key_mgmt": "wpa-psk",
|
||||
"password": "p@55w0rD",
|
||||
},
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
def test_wireless_ssid_too_long(self):
|
||||
"""
|
||||
should fail if ssid longer than 32 bytes
|
||||
"""
|
||||
self.do_connections_check_invalid(
|
||||
[
|
||||
{
|
||||
"name": "wireless1",
|
||||
"state": "up",
|
||||
"type": "wireless",
|
||||
"wireless": {
|
||||
"ssid": "test wireless network with ssid too long",
|
||||
"key_mgmt": "wpa-psk",
|
||||
"password": "p@55w0rD",
|
||||
},
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
def test_wireless_no_password(self):
|
||||
"""
|
||||
should fail if wpa-psk is selected and no password provided
|
||||
"""
|
||||
self.do_connections_check_invalid(
|
||||
[
|
||||
{
|
||||
"name": "wireless1",
|
||||
"state": "up",
|
||||
"type": "wireless",
|
||||
"wireless": {
|
||||
"ssid": "test wireless network",
|
||||
"key_mgmt": "wpa-psk",
|
||||
},
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
def test_wireless_password_too_long(self):
|
||||
"""
|
||||
should fail if wpa-psk is selected and no password provided
|
||||
"""
|
||||
self.do_connections_check_invalid(
|
||||
[
|
||||
{
|
||||
"name": "wireless1",
|
||||
"state": "up",
|
||||
"type": "wireless",
|
||||
"wireless": {
|
||||
"ssid": "test wireless network",
|
||||
"key_mgmt": "wpa-psk",
|
||||
"password": "This password is too long and should "
|
||||
"not be able to validate properly",
|
||||
},
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
def test_wireless_no_802_1x_for_wpa_eap(self):
|
||||
"""
|
||||
should fail if no 802.1x parameters are defined for a wireless
|
||||
connection with key_mgmt=wpa-eap
|
||||
"""
|
||||
self.do_connections_check_invalid(
|
||||
[
|
||||
{
|
||||
"name": "wireless1",
|
||||
"state": "up",
|
||||
"type": "wireless",
|
||||
"wireless": {
|
||||
"ssid": "test wireless network",
|
||||
"key_mgmt": "wpa-eap",
|
||||
},
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
def test_wireless_no_options_defined(self):
|
||||
"""
|
||||
should fail if a connection of type='wireless' does not
|
||||
have any 'wireless' settings defined
|
||||
"""
|
||||
self.do_connections_check_invalid(
|
||||
[{"name": "wireless1", "state": "up", "type": "wireless"}]
|
||||
)
|
||||
|
||||
def test_invalid_mac(self):
|
||||
self.maxDiff = None
|
||||
self.do_connections_check_invalid(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue