Skip to content

Commit 20348af

Browse files
authored
Merge pull request #84 from ushiboy/feat/add-supported
Feat: Add support for device up/down and general reload commands
2 parents 27dc840 + b4f06c7 commit 20348af

File tree

7 files changed

+173
-7
lines changed

7 files changed

+173
-7
lines changed

README.md

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ except Exception as e:
4444
| general | hostname | supported |
4545
| general | permissions | not supported |
4646
| general | logging | not supported |
47+
| general | reload | supported |
4748
| networking | | supported |
4849
| networking | on | supported |
4950
| networking | off | supported |
@@ -61,6 +62,7 @@ except Exception as e:
6162
| connection | clone | not supported |
6263
| connection | edit | not supported |
6364
| connection | delete | supported |
65+
| connection | monitor | not supported |
6466
| connection | reload | supported |
6567
| connection | load | not supported |
6668
| connection | import | not supported |
@@ -69,17 +71,20 @@ except Exception as e:
6971
| device | status | supported |
7072
| device | show | supported |
7173
| device | set | not supported |
74+
| device | up | supported |
7275
| device | connect | supported |
7376
| device | reapply | supported |
7477
| device | modify | not supported |
78+
| device | down | supported |
7579
| device | disconnect | supported |
7680
| device | delete | supported |
7781
| device | monitor | not supported |
78-
| device | wifi | supported |
79-
| device | wifi connect | supported |
80-
| device | wifi rescan | supported |
81-
| device | wifi hotspot | supported |
82-
| device | lldp | not supported |
82+
| device | wifi | supported |
83+
| device | wifi connect | supported |
84+
| device | wifi rescan | supported |
85+
| device | wifi hotspot | supported |
86+
| device | wifi show-password | not supported |
87+
| device | lldp | not supported |
8388
| agent | | not supported |
8489
| agent | secret | not supported |
8590
| agent | polkit | not supported |
@@ -216,6 +221,16 @@ The `fields` argument applies the same effect to the command as the `-f | --fiel
216221
nmcli.device.show_all(fields: str = None) -> List[DeviceDetails]
217222
```
218223

224+
#### nmcli.device.up
225+
226+
Connect the device.
227+
228+
The `wait` argument applies the same effect to the command as the `--wait` option. If it is omitted, the default behavior is followed.
229+
230+
```
231+
nmcli.device.up(ifname: str, wait: int = None) -> None
232+
```
233+
219234
#### nmcli.device.connect
220235

221236
Connect the device.
@@ -226,6 +241,16 @@ The `wait` argument applies the same effect to the command as the `--wait` optio
226241
nmcli.device.connect(ifname: str, wait: int = None) -> None
227242
```
228243

244+
#### nmcli.device.down
245+
246+
Disconnect a device and prevent the device from automatically activating further connections without user/manual intervention.
247+
248+
The `wait` argument applies the same effect to the command as the `--wait` option. If it is omitted, the default behavior is followed.
249+
250+
```
251+
nmcli.device.down(ifname: str, wait: int = None) -> None
252+
```
253+
229254
#### nmcli.device.disconnect
230255

231256
Disconnect devices.
@@ -330,6 +355,21 @@ Change persistent system hostname.
330355
nmcli.general.set_hostname(hostname: str) -> None
331356
```
332357

358+
#### nmcli.general.reload
359+
360+
Reload NetworkManager's configuration and perform certain updates.
361+
362+
The `flags` argument specifies which configurations to reload. Valid flags are:
363+
- `conf`: Reload NetworkManager.conf configuration from disk
364+
- `dns-rc`: Update DNS configuration (equivalent to SIGUSR1)
365+
- `dns-full`: Restart the DNS plugin
366+
367+
If no flags are provided, everything that is supported is reloaded.
368+
369+
```
370+
nmcli.general.reload(flags: Optional[List[str]] = None) -> None
371+
```
372+
333373
### networking
334374

335375
#### nmcli.networking

nmcli/_device.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,15 @@ def show(self, ifname: str, fields: str = None) -> DeviceDetails:
5252
def show_all(self, fields: str = None) -> List[DeviceDetails]:
5353
raise NotImplementedError
5454

55+
def up(self, ifname: str, wait: int = None) -> None:
56+
raise NotImplementedError
57+
5558
def connect(self, ifname: str, wait: int = None) -> None:
5659
raise NotImplementedError
5760

61+
def down(self, ifname: str, wait: int = None) -> None:
62+
raise NotImplementedError
63+
5864
def disconnect(self, ifname: str, wait: int = None) -> None:
5965
raise NotImplementedError
6066

@@ -131,11 +137,21 @@ def show_all(self, fields: str = None) -> List[DeviceDetails]:
131137
details[key] = None if value in ('--', '""') else value
132138
return results
133139

140+
def up(self, ifname: str, wait: int = None) -> None:
141+
cmd = add_wait_option_if_needed(
142+
wait) + ['device', 'up', ifname]
143+
self._syscmd.nmcli(cmd)
144+
134145
def connect(self, ifname: str, wait: int = None) -> None:
135146
cmd = add_wait_option_if_needed(
136147
wait) + ['device', 'connect', ifname]
137148
self._syscmd.nmcli(cmd)
138149

150+
def down(self, ifname: str, wait: int = None) -> None:
151+
cmd = add_wait_option_if_needed(
152+
wait) + ['device', 'down', ifname]
153+
self._syscmd.nmcli(cmd)
154+
139155
def disconnect(self, ifname: str, wait: int = None) -> None:
140156
cmd = add_wait_option_if_needed(
141157
wait) + ['device', 'disconnect', ifname]

nmcli/_general.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import List, Optional
2+
13
from ._system import SystemCommand, SystemCommandInterface
24
from .data import General
35

@@ -16,9 +18,14 @@ def get_hostname(self) -> str:
1618
def set_hostname(self, hostname: str):
1719
raise NotImplementedError
1820

21+
def reload(self, flags: Optional[List[str]] = None) -> None:
22+
raise NotImplementedError
23+
1924

2025
class GeneralControl(GeneralControlInterface):
2126

27+
VALID_RELOAD_FLAGS = ['conf', 'dns-rc', 'dns-full']
28+
2229
def __init__(self, syscmd: SystemCommandInterface = None):
2330
self._syscmd = syscmd or SystemCommand()
2431

@@ -35,3 +42,16 @@ def get_hostname(self) -> str:
3542

3643
def set_hostname(self, hostname: str):
3744
self._syscmd.nmcli(['general', 'hostname', hostname])
45+
46+
def reload(self, flags: Optional[List[str]] = None) -> None:
47+
if flags is not None:
48+
for flag in flags:
49+
if flag not in self.VALID_RELOAD_FLAGS:
50+
raise ValueError(
51+
f"Invalid reload flag '{flag}'. "
52+
f"Valid flags are: {', '.join(self.VALID_RELOAD_FLAGS)}"
53+
)
54+
cmd = ['general', 'reload']
55+
if flags:
56+
cmd += flags
57+
self._syscmd.nmcli(cmd)

nmcli/dummy/_device.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,24 @@
55
from ..data.hotspot import Hotspot
66

77

8-
class DummyDeviceControl(DeviceControlInterface):
8+
class DummyDeviceControl(DeviceControlInterface): # pylint: disable=too-many-public-methods
99

1010
@property
1111
def show_args(self):
1212
return self._show_args
1313

14+
@property
15+
def up_args(self):
16+
return self._up_args
17+
1418
@property
1519
def connect_args(self):
1620
return self._connect_args
1721

22+
@property
23+
def down_args(self):
24+
return self._down_args
25+
1826
@property
1927
def disconnect_args(self):
2028
return self._disconnect_args
@@ -57,7 +65,9 @@ def __init__(self,
5765
self._result_show_all = result_show_all or []
5866
self._result_wifi_hotspot = result_wifi_hotspot
5967
self._show_args: List[Tuple] = []
68+
self._up_args: List[Tuple] = []
6069
self._connect_args: List[Tuple] = []
70+
self._down_args: List[Tuple] = []
6171
self._disconnect_args: List[Tuple] = []
6272
self._reapply_args: List[str] = []
6373
self._delete_args: List[Tuple] = []
@@ -85,10 +95,18 @@ def show_all(self, fields: str = None) -> List[DeviceDetails]:
8595
self._raise_error_if_needed()
8696
return self._result_show_all
8797

98+
def up(self, ifname: str, wait: int = None) -> None:
99+
self._raise_error_if_needed()
100+
self._up_args.append((ifname, wait))
101+
88102
def connect(self, ifname: str, wait: int = None) -> None:
89103
self._raise_error_if_needed()
90104
self._connect_args.append((ifname, wait))
91105

106+
def down(self, ifname: str, wait: int = None) -> None:
107+
self._raise_error_if_needed()
108+
self._down_args.append((ifname, wait))
109+
92110
def disconnect(self, ifname: str, wait: int = None) -> None:
93111
self._raise_error_if_needed()
94112
self._disconnect_args.append((ifname, wait))

nmcli/dummy/_general.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import List
1+
from typing import List, Optional
22

33
from .._general import GeneralControlInterface
44
from ..data.general import General
@@ -10,6 +10,10 @@ class DummyGeneralControl(GeneralControlInterface):
1010
def set_hostname_args(self):
1111
return self._set_hostname_args
1212

13+
@property
14+
def reload_args(self):
15+
return self._reload_args
16+
1317
def __init__(self,
1418
result_call: General = None,
1519
result_status: General = None,
@@ -20,6 +24,7 @@ def __init__(self,
2024
self._result_status = result_status
2125
self._result_hostname = result_hostname
2226
self._set_hostname_args: List[str] = []
27+
self._reload_args: List[Optional[List[str]]] = []
2328

2429
def __call__(self) -> General:
2530
self._raise_error_if_needed()
@@ -41,6 +46,10 @@ def set_hostname(self, hostname: str):
4146
self._raise_error_if_needed()
4247
self._set_hostname_args.append(hostname)
4348

49+
def reload(self, flags: Optional[List[str]] = None) -> None:
50+
self._raise_error_if_needed()
51+
self._reload_args.append(flags)
52+
4453
def _raise_error_if_needed(self):
4554
if not self._raise_error is None:
4655
raise self._raise_error

tests/test_device.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,17 @@ def test_show_all():
171171
assert s2.passed_parameters == ['-f', 'all', 'device', 'show']
172172

173173

174+
def test_up():
175+
s = DummySystemCommand()
176+
device = DeviceControl(s)
177+
ifname = 'eth0'
178+
device.up(ifname)
179+
assert s.passed_parameters == ['device', 'up', ifname]
180+
181+
device.up(ifname, wait=10)
182+
assert s.passed_parameters == ['--wait', '10', 'device', 'up', ifname]
183+
184+
174185
def test_connect():
175186
s = DummySystemCommand()
176187
device = DeviceControl(s)
@@ -182,6 +193,18 @@ def test_connect():
182193
assert s.passed_parameters == ['--wait', '10', 'device', 'connect', ifname]
183194

184195

196+
def test_down():
197+
s = DummySystemCommand()
198+
device = DeviceControl(s)
199+
ifname = 'eth0'
200+
device.down(ifname)
201+
assert s.passed_parameters == ['device', 'down', ifname]
202+
203+
device.down(ifname, wait=10)
204+
assert s.passed_parameters == [
205+
'--wait', '10', 'device', 'down', ifname]
206+
207+
185208
def test_disconnect():
186209
s = DummySystemCommand()
187210
device = DeviceControl(s)

tests/test_general.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import pytest
2+
13
from nmcli._const import NetworkConnectivity, NetworkManagerState
24
from nmcli._general import GeneralControl
35
from nmcli.data import General
@@ -37,3 +39,41 @@ def test_set_hostname():
3739
general = GeneralControl(s)
3840
general.set_hostname('test')
3941
assert s.passed_parameters == ['general', 'hostname', 'test']
42+
43+
44+
def test_reload_without_flags():
45+
s = DummySystemCommand()
46+
general = GeneralControl(s)
47+
general.reload()
48+
assert s.passed_parameters == ['general', 'reload']
49+
50+
51+
def test_reload_with_single_flag():
52+
s = DummySystemCommand()
53+
general = GeneralControl(s)
54+
general.reload(['conf'])
55+
assert s.passed_parameters == ['general', 'reload', 'conf']
56+
57+
58+
def test_reload_with_all_valid_flags():
59+
s = DummySystemCommand()
60+
general = GeneralControl(s)
61+
general.reload(['conf', 'dns-rc', 'dns-full'])
62+
assert s.passed_parameters == ['general', 'reload', 'conf', 'dns-rc', 'dns-full']
63+
64+
65+
def test_reload_with_invalid_flag():
66+
s = DummySystemCommand()
67+
general = GeneralControl(s)
68+
with pytest.raises(ValueError) as exc_info:
69+
general.reload(['invalid-flag'])
70+
assert "Invalid reload flag 'invalid-flag'" in str(exc_info.value)
71+
assert "Valid flags are: conf, dns-rc, dns-full" in str(exc_info.value)
72+
73+
74+
def test_reload_with_mixed_valid_and_invalid_flags():
75+
s = DummySystemCommand()
76+
general = GeneralControl(s)
77+
with pytest.raises(ValueError) as exc_info:
78+
general.reload(['conf', 'invalid'])
79+
assert "Invalid reload flag 'invalid'" in str(exc_info.value)

0 commit comments

Comments
 (0)