ifstate 1.9.0__py3-none-any.whl → 1.10.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ifstate-1.9.0.dist-info → ifstate-1.10.1.dist-info}/METADATA +2 -2
- {ifstate-1.9.0.dist-info → ifstate-1.10.1.dist-info}/RECORD +14 -13
- {ifstate-1.9.0.dist-info → ifstate-1.10.1.dist-info}/WHEEL +1 -1
- libifstate/__init__.py +58 -11
- libifstate/fdb/__init__.py +174 -0
- libifstate/link/base.py +122 -7
- libifstate/netns/__init__.py +34 -13
- libifstate/parser/base.py +29 -1
- libifstate/sysctl/__init__.py +15 -0
- libifstate/util.py +63 -6
- schema/ifstate.conf.schema.json +487 -278
- {ifstate-1.9.0.dist-info → ifstate-1.10.1.dist-info}/LICENSE +0 -0
- {ifstate-1.9.0.dist-info → ifstate-1.10.1.dist-info}/entry_points.txt +0 -0
- {ifstate-1.9.0.dist-info → ifstate-1.10.1.dist-info}/top_level.txt +0 -0
libifstate/netns/__init__.py
CHANGED
@@ -6,10 +6,16 @@ import logging
|
|
6
6
|
import pyroute2
|
7
7
|
import re
|
8
8
|
import secrets
|
9
|
+
import shutil
|
10
|
+
import subprocess
|
9
11
|
|
10
12
|
netns_name_map = {}
|
11
13
|
netns_name_root = None
|
12
14
|
netns_nsid_map = {}
|
15
|
+
findmnt_cmd = shutil.which('findmnt')
|
16
|
+
|
17
|
+
if findmnt_cmd is None:
|
18
|
+
logger.debug("findmnt binary is not available, netns binding of links might not be correct")
|
13
19
|
|
14
20
|
@atexit.register
|
15
21
|
def close_netns():
|
@@ -25,6 +31,7 @@ class NetNameSpace():
|
|
25
31
|
self.links = {}
|
26
32
|
self.addresses = {}
|
27
33
|
self.bpf_progs = None
|
34
|
+
self.fdb = {}
|
28
35
|
self.neighbours = {}
|
29
36
|
self.vrrp = {
|
30
37
|
'links': [],
|
@@ -40,9 +47,14 @@ class NetNameSpace():
|
|
40
47
|
|
41
48
|
if name is None:
|
42
49
|
self.ipr = root_ipr
|
50
|
+
self.mount = b''
|
43
51
|
else:
|
44
52
|
self.ipr = NetNSExt(name)
|
45
53
|
netns_name_map[name] = self.ipr
|
54
|
+
if findmnt_cmd is None:
|
55
|
+
self.mount = name.encode("utf-8")
|
56
|
+
else:
|
57
|
+
self.mount = subprocess.check_output([findmnt_cmd, '-f', '-J', "/run/netns/{}".format(name)])
|
46
58
|
|
47
59
|
def get_netnsid(self, peer_netns_name):
|
48
60
|
if peer_netns_name is None:
|
@@ -51,17 +63,17 @@ class NetNameSpace():
|
|
51
63
|
else:
|
52
64
|
peer_ipr = netns_name_map[peer_netns_name]
|
53
65
|
peer_pid = peer_ipr.child
|
54
|
-
|
66
|
+
|
55
67
|
result = self.ipr.get_netnsid(pid=peer_pid)
|
56
68
|
if result['nsid'] == 4294967295:
|
57
69
|
self.ipr.set_netnsid(pid=peer_pid)
|
58
70
|
result = self.ipr.get_netnsid(pid=peer_pid)
|
59
|
-
|
71
|
+
|
60
72
|
peer_nsid = result['nsid']
|
61
73
|
|
62
74
|
return (peer_ipr, peer_nsid)
|
63
75
|
|
64
|
-
def prepare_netns(do_apply, target_netns_list):
|
76
|
+
def prepare_netns(do_apply, target_netns_list, new_netns_list):
|
65
77
|
logger.info("configure network namespaces...")
|
66
78
|
|
67
79
|
# get mapping of netns names to lists of pids
|
@@ -84,7 +96,7 @@ def prepare_netns(do_apply, target_netns_list):
|
|
84
96
|
pyroute2.netns.remove(name)
|
85
97
|
|
86
98
|
# create missing netns
|
87
|
-
elif name not in current_netns_list:
|
99
|
+
elif name not in current_netns_list or name in new_netns_list:
|
88
100
|
logger.log_add(name)
|
89
101
|
|
90
102
|
# log already existing namespaces
|
@@ -123,25 +135,33 @@ class LinkRegistry():
|
|
123
135
|
self.ignores = ignores
|
124
136
|
|
125
137
|
for namespace in namespaces:
|
126
|
-
self.
|
138
|
+
self.inventory_netns(namespace)
|
127
139
|
|
128
140
|
if logger.getEffectiveLevel() <= logging.DEBUG:
|
129
141
|
self.debug_dump()
|
130
142
|
|
143
|
+
def add_link(self, netns, link):
|
144
|
+
item = LinkRegistryItem(
|
145
|
+
self,
|
146
|
+
netns,
|
147
|
+
link,
|
148
|
+
)
|
149
|
+
self.registry.append(item)
|
150
|
+
return item
|
151
|
+
|
131
152
|
def get_link(self, **attributes):
|
132
153
|
for link in self.registry:
|
133
154
|
if link.match(**attributes):
|
134
155
|
return link
|
135
156
|
return None
|
136
157
|
|
137
|
-
def
|
158
|
+
def inventory_netns(self, target_netns):
|
138
159
|
for link in target_netns.ipr.get_links():
|
139
|
-
|
140
|
-
self
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
))
|
160
|
+
self.registry.append(LinkRegistryItem(
|
161
|
+
self,
|
162
|
+
target_netns,
|
163
|
+
link,
|
164
|
+
))
|
145
165
|
|
146
166
|
def get_random_name(self, prefix):
|
147
167
|
hex_length = int((15-len(prefix))/2)
|
@@ -209,7 +229,8 @@ class LinkRegistryItem():
|
|
209
229
|
|
210
230
|
idx = next(iter(netns.ipr.link_lookup(ifname=self.attributes['ifname'])), None)
|
211
231
|
if idx is not None:
|
212
|
-
|
232
|
+
# ToDo
|
233
|
+
self.update_ifname( self.link_registry.get_random_name('__netns__') )
|
213
234
|
|
214
235
|
self.__ipr_link('set', index=self.attributes['index'], net_ns_fd=netns_name)
|
215
236
|
self.netns = netns
|
libifstate/parser/base.py
CHANGED
@@ -5,6 +5,18 @@ from copy import deepcopy
|
|
5
5
|
|
6
6
|
|
7
7
|
class Parser(ABC):
|
8
|
+
_default_lo_link = {
|
9
|
+
'name': 'lo',
|
10
|
+
'addresses': [
|
11
|
+
'127.0.0.1/8',
|
12
|
+
'::1/128',
|
13
|
+
],
|
14
|
+
'link': {
|
15
|
+
'kind': 'physical',
|
16
|
+
'state': 'up',
|
17
|
+
'mtu': 65536,
|
18
|
+
}
|
19
|
+
}
|
8
20
|
_default_ifstates = {
|
9
21
|
'ignore': {
|
10
22
|
'ipaddr_builtin': [
|
@@ -14,12 +26,15 @@ class Parser(ABC):
|
|
14
26
|
'ifname_builtin': [
|
15
27
|
r'^br-[\da-f]{12}',
|
16
28
|
r'^docker\d+',
|
17
|
-
r'^lo$',
|
18
29
|
r'^ppp\d+$',
|
19
30
|
r'^veth',
|
20
31
|
r'^virbr\d+',
|
21
32
|
r'^vrrp\d*\.\d+$'
|
22
33
|
],
|
34
|
+
'fdb_builtin': [
|
35
|
+
r'^33:33:',
|
36
|
+
r'^01:00:5e:'
|
37
|
+
],
|
23
38
|
'routes_builtin': [
|
24
39
|
{'proto': 1},
|
25
40
|
{'proto': 2},
|
@@ -97,6 +112,14 @@ class Parser(ABC):
|
|
97
112
|
a[key] = b[key]
|
98
113
|
return a
|
99
114
|
|
115
|
+
def _update_lo(self, cfg):
|
116
|
+
if 'interfaces' in cfg:
|
117
|
+
lo = next((idx for idx, iface in enumerate(cfg['interfaces']) if iface['name'] == 'lo'), None)
|
118
|
+
if lo is not None:
|
119
|
+
cfg['interfaces'][lo] = self.merge(deepcopy(Parser._default_lo_link), cfg['interfaces'][lo])
|
120
|
+
else:
|
121
|
+
cfg['interfaces'].append(Parser._default_lo_link)
|
122
|
+
|
100
123
|
def config(self):
|
101
124
|
# merge builtin defaults with config
|
102
125
|
cfg = (self.merge(deepcopy(Parser._default_ifstates), self.ifstates))
|
@@ -107,6 +130,11 @@ class Parser(ABC):
|
|
107
130
|
except TypeError:
|
108
131
|
raise ParserValidationError("$.ignore: is not of type 'object'")
|
109
132
|
|
133
|
+
# add loopback interface defaults
|
134
|
+
self._update_lo(cfg)
|
135
|
+
for namespace in cfg.get('namespaces', {}):
|
136
|
+
self._update_lo(cfg['namespaces'][namespace])
|
137
|
+
|
110
138
|
# merge builtin defaults
|
111
139
|
for k in list(cfg["ignore"]):
|
112
140
|
if k.endswith("_builtin"):
|
libifstate/sysctl/__init__.py
CHANGED
@@ -7,11 +7,15 @@ import os
|
|
7
7
|
class Sysctl():
|
8
8
|
def __init__(self, netns):
|
9
9
|
self.sysctls = {}
|
10
|
+
self.globals = {}
|
10
11
|
self.netns = netns
|
11
12
|
|
12
13
|
def add(self, iface, sysctl):
|
13
14
|
self.sysctls[iface] = sysctl
|
14
15
|
|
16
|
+
def add_global(self, proto, sysctl):
|
17
|
+
self.globals[proto] = sysctl
|
18
|
+
|
15
19
|
def set_sysctl(self, iface_current, iface_config, family, key, val, do_apply):
|
16
20
|
if self.netns.netns is not None:
|
17
21
|
pyroute2.netns.pushns(self.netns.netns)
|
@@ -66,3 +70,14 @@ class Sysctl():
|
|
66
70
|
|
67
71
|
def has_settings(self, iface):
|
68
72
|
return iface in self.sysctls
|
73
|
+
|
74
|
+
def apply_globals(self, do_apply):
|
75
|
+
changes = []
|
76
|
+
for proto, sysctl in self.globals.items():
|
77
|
+
for key, val in sysctl.items():
|
78
|
+
if self.set_sysctl('..', '..', proto, key, val, do_apply):
|
79
|
+
changes.append(proto)
|
80
|
+
return changes
|
81
|
+
|
82
|
+
def has_globals(self):
|
83
|
+
return len(self.globals) > 0
|
libifstate/util.py
CHANGED
@@ -2,7 +2,8 @@ from libifstate.log import logger, IfStateLogging
|
|
2
2
|
from pyroute2 import IPRoute, NetNS, netns
|
3
3
|
|
4
4
|
from pyroute2.netlink.rtnl.tcmsg import tcmsg
|
5
|
-
from pyroute2.netlink.rtnl import RTM_DELTFILTER
|
5
|
+
from pyroute2.netlink.rtnl import RTM_DELTFILTER, RTM_NEWNSID
|
6
|
+
from pyroute2.netlink.rtnl.nsidmsg import nsidmsg
|
6
7
|
from pyroute2.netlink import NLM_F_REQUEST
|
7
8
|
from pyroute2.netlink import NLM_F_ACK
|
8
9
|
from pyroute2.netlink import NLM_F_CREATE
|
@@ -132,13 +133,36 @@ class IPRouteExt(IPRoute):
|
|
132
133
|
return None
|
133
134
|
|
134
135
|
def get_ifname_by_index(self, index):
|
135
|
-
link = next(iter(
|
136
|
+
link = next(iter(self.get_links(index)), None)
|
136
137
|
|
137
138
|
if link is None:
|
138
139
|
return index
|
139
140
|
|
140
141
|
return link.get_attr('IFLA_IFNAME')
|
141
142
|
|
143
|
+
def set_netnsid(self, nsid=None, pid=None, fd=None):
|
144
|
+
'''
|
145
|
+
call pyroute2's set_netnsid if available or use
|
146
|
+
fallback implementation for pyroute2 <=0.79
|
147
|
+
'''
|
148
|
+
if hasattr(super(), 'set_netnsid'):
|
149
|
+
return super().set_netnsid(nsid, pid, fd)
|
150
|
+
else:
|
151
|
+
msg = nsidmsg()
|
152
|
+
|
153
|
+
if nsid is None or nsid < 0:
|
154
|
+
# kernel auto select
|
155
|
+
msg['attrs'].append(('NETNSA_NSID', 4294967295))
|
156
|
+
else:
|
157
|
+
msg['attrs'].append(('NETNSA_NSID', nsid))
|
158
|
+
|
159
|
+
if pid is not None:
|
160
|
+
msg['attrs'].append(('NETNSA_PID', pid))
|
161
|
+
|
162
|
+
if fd is not None:
|
163
|
+
msg['attrs'].append(('NETNSA_FD', fd))
|
164
|
+
|
165
|
+
return self.nlm_request(msg, RTM_NEWNSID, NLM_F_REQUEST | NLM_F_ACK)
|
142
166
|
|
143
167
|
class NetNSExt(NetNS):
|
144
168
|
def __init__(self, *args, **kwargs):
|
@@ -220,13 +244,37 @@ class NetNSExt(NetNS):
|
|
220
244
|
return None
|
221
245
|
|
222
246
|
def get_ifname_by_index(self, index):
|
223
|
-
link = next(iter(
|
247
|
+
link = next(iter(self.get_links(index)), None)
|
224
248
|
|
225
249
|
if link is None:
|
226
250
|
return index
|
227
251
|
|
228
252
|
return link.get_attr('IFLA_IFNAME')
|
229
253
|
|
254
|
+
def set_netnsid(self, nsid=None, pid=None, fd=None):
|
255
|
+
'''
|
256
|
+
call pyroute2's set_netnsid if available or use
|
257
|
+
fallback implementation for pyroute2 <=0.79
|
258
|
+
'''
|
259
|
+
if hasattr(super(), 'set_netnsid'):
|
260
|
+
return super().set_netnsid(nsid, pid, fd)
|
261
|
+
else:
|
262
|
+
msg = nsidmsg()
|
263
|
+
|
264
|
+
if nsid is None or nsid < 0:
|
265
|
+
# kernel auto select
|
266
|
+
msg['attrs'].append(('NETNSA_NSID', 4294967295))
|
267
|
+
else:
|
268
|
+
msg['attrs'].append(('NETNSA_NSID', nsid))
|
269
|
+
|
270
|
+
if pid is not None:
|
271
|
+
msg['attrs'].append(('NETNSA_PID', pid))
|
272
|
+
|
273
|
+
if fd is not None:
|
274
|
+
msg['attrs'].append(('NETNSA_FD', fd))
|
275
|
+
|
276
|
+
return self.nlm_request(msg, RTM_NEWNSID, NLM_F_REQUEST | NLM_F_ACK)
|
277
|
+
|
230
278
|
class LinkDependency:
|
231
279
|
def __init__(self, ifname, netns):
|
232
280
|
self.ifname = ifname
|
@@ -240,14 +288,23 @@ class LinkDependency:
|
|
240
288
|
|
241
289
|
def __lt__(self, obj):
|
242
290
|
if self.netns is None:
|
243
|
-
|
244
|
-
|
245
|
-
|
291
|
+
if obj.netns is not None:
|
292
|
+
return True
|
293
|
+
elif obj.netns is None:
|
246
294
|
return False
|
247
295
|
|
248
296
|
if self.netns != obj.netns:
|
249
297
|
return self.netns < obj.netns
|
250
298
|
|
299
|
+
if self.ifname == obj.ifname:
|
300
|
+
return False
|
301
|
+
|
302
|
+
if self.ifname == 'lo':
|
303
|
+
return True
|
304
|
+
|
305
|
+
if obj.ifname == 'lo':
|
306
|
+
return False
|
307
|
+
|
251
308
|
return self.ifname < obj.ifname
|
252
309
|
|
253
310
|
def __ne__(self, other):
|