ifstate 1.13.6__py3-none-any.whl → 2.0.0__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/ifstate.py +15 -8
- ifstate/vrrp.py +1 -1
- {ifstate-1.13.6.dist-info → ifstate-2.0.0.dist-info}/METADATA +2 -2
- ifstate-2.0.0.dist-info/RECORD +39 -0
- {ifstate-1.13.6.dist-info → ifstate-2.0.0.dist-info}/WHEEL +1 -1
- libifstate/__init__.py +152 -81
- libifstate/address/__init__.py +1 -1
- libifstate/exception.py +6 -12
- libifstate/hook/__init__.py +195 -0
- libifstate/hook/wrapper.sh +50 -0
- libifstate/link/base.py +38 -37
- libifstate/link/dsa.py +1 -1
- libifstate/link/physical.py +3 -3
- libifstate/link/tun.py +3 -3
- libifstate/link/veth.py +2 -2
- libifstate/log.py +7 -4
- libifstate/netns/__init__.py +53 -14
- libifstate/parser/__init__.py +1 -1
- libifstate/parser/base.py +131 -85
- libifstate/routing/__init__.py +234 -114
- libifstate/schema/2/ifstate.conf.schema.json +4442 -0
- libifstate/sysctl/__init__.py +20 -2
- libifstate/tc/__init__.py +1 -1
- libifstate/util.py +153 -147
- libifstate/wireguard/__init__.py +82 -21
- ifstate-1.13.6.dist-info/RECORD +0 -37
- schema/ifstate.conf.schema.json +0 -4259
- {ifstate-1.13.6.dist-info → ifstate-2.0.0.dist-info}/entry_points.txt +0 -0
- {ifstate-1.13.6.dist-info → ifstate-2.0.0.dist-info}/licenses/LICENSE +0 -0
- {ifstate-1.13.6.dist-info → ifstate-2.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,195 @@
|
|
1
|
+
from libifstate.util import get_netns_run_dir, dump_yaml_file, slurp_yaml_file, RUN_BASE_DIR
|
2
|
+
from libifstate.util import logger
|
3
|
+
|
4
|
+
import logging
|
5
|
+
from pathlib import Path
|
6
|
+
import os
|
7
|
+
import pkgutil
|
8
|
+
from shlex import quote
|
9
|
+
import shutil
|
10
|
+
from string import Template
|
11
|
+
import subprocess
|
12
|
+
import tempfile
|
13
|
+
|
14
|
+
|
15
|
+
HOOK_DIR = '/etc/ifstate/hooks'
|
16
|
+
HOOK_WRAPPER = Template(pkgutil.get_data("libifstate", "hook/wrapper.sh").decode("utf-8"))
|
17
|
+
|
18
|
+
RC_OK = 0
|
19
|
+
RC_ERROR = 1
|
20
|
+
RC_STARTED = 2
|
21
|
+
# it is the same value, but there are two constants to make it more convinient
|
22
|
+
RC_STOPPED = RC_STARTED
|
23
|
+
RC_CHANGED = 3
|
24
|
+
|
25
|
+
def _open_perm(path, flags):
|
26
|
+
return os.open(path, flags, 0o600)
|
27
|
+
|
28
|
+
class Hook():
|
29
|
+
def __init__(self, name, script, provides=[], after=[]):
|
30
|
+
self.name = name
|
31
|
+
|
32
|
+
if script[0] == '/':
|
33
|
+
self.script = Path(script).as_posix()
|
34
|
+
else:
|
35
|
+
self.script = Path(HOOK_DIR, script).as_posix()
|
36
|
+
|
37
|
+
self.provides = provides
|
38
|
+
self.after = after
|
39
|
+
|
40
|
+
def start(self, link, args, run_dir, do_apply):
|
41
|
+
wrapper_fn = f"{run_dir}/wrapper.sh"
|
42
|
+
|
43
|
+
with open(wrapper_fn, "w", opener=_open_perm) as fh:
|
44
|
+
template_vars = {
|
45
|
+
'verbose': 1 if logger.getEffectiveLevel() == logging.DEBUG else '',
|
46
|
+
'script': self.script,
|
47
|
+
'ifname': link.settings.get('ifname'),
|
48
|
+
'index': link.idx,
|
49
|
+
'netns': link.netns.netns or '',
|
50
|
+
'vrf': '',
|
51
|
+
'rundir': run_dir,
|
52
|
+
'rc_ok': RC_OK,
|
53
|
+
'rc_error': RC_ERROR,
|
54
|
+
'rc_started': RC_STARTED,
|
55
|
+
'rc_stopped': RC_STOPPED,
|
56
|
+
'rc_changed': RC_CHANGED,
|
57
|
+
}
|
58
|
+
|
59
|
+
if link.get_if_attr('IFLA_INFO_SLAVE_KIND') == 'vrf':
|
60
|
+
template_vars['vrf'] = link.settings.get('master')
|
61
|
+
|
62
|
+
args_list = []
|
63
|
+
for k, v in args.items():
|
64
|
+
args_list.append(f'export IFS_ARG_{k.upper()}={quote(v)}')
|
65
|
+
template_vars['args'] = "\n".join(args_list)
|
66
|
+
|
67
|
+
try:
|
68
|
+
fh.write(HOOK_WRAPPER.substitute(template_vars))
|
69
|
+
except KeyError as ex:
|
70
|
+
logger.error("Failed to prepare wrapper for hook {}: variable {} unknown".format(self.name, str(ex)))
|
71
|
+
return
|
72
|
+
except ValueError as ex:
|
73
|
+
logger.error("Failed to prepare wrapper for hook {}: {}".format(self.name, str(ex)))
|
74
|
+
return
|
75
|
+
|
76
|
+
try:
|
77
|
+
if do_apply:
|
78
|
+
subprocess.run(['/bin/sh', wrapper_fn, "start"], timeout=3, check=True)
|
79
|
+
else:
|
80
|
+
subprocess.run(['/bin/sh', wrapper_fn, "check-start"], timeout=3, check=True)
|
81
|
+
|
82
|
+
return RC_OK
|
83
|
+
except (FileNotFoundError, PermissionError) as ex:
|
84
|
+
logger.error("Failed executing hook {}: {}".format(self.name, str(ex)))
|
85
|
+
return RC_ERROR
|
86
|
+
except subprocess.TimeoutExpired as ex:
|
87
|
+
logger.error("Running hook {} has timed out.".format(self.name))
|
88
|
+
return RC_ERROR
|
89
|
+
except subprocess.CalledProcessError as ex:
|
90
|
+
if ex.returncode < 0:
|
91
|
+
logger.warning("Hook {} got signal {}".format(hook_name, -1 * ex.returncode))
|
92
|
+
return RC_ERROR
|
93
|
+
|
94
|
+
return ex.returncode
|
95
|
+
|
96
|
+
@staticmethod
|
97
|
+
def stop(link, hook_name, run_dir, do_apply):
|
98
|
+
wrapper_fn = f"{run_dir}/wrapper.sh"
|
99
|
+
|
100
|
+
try:
|
101
|
+
if do_apply:
|
102
|
+
subprocess.run(['/bin/sh', wrapper_fn, "stop"], timeout=3, check=True)
|
103
|
+
else:
|
104
|
+
subprocess.run(['/bin/sh', wrapper_fn, "check-stop"], timeout=3, check=True)
|
105
|
+
|
106
|
+
return RC_OK
|
107
|
+
except (FileNotFoundError, PermissionError) as ex:
|
108
|
+
logger.error("Failed executing hook {}: {}".format(hook_name, str(ex)))
|
109
|
+
return RC_ERROR
|
110
|
+
except subprocess.TimeoutExpired as ex:
|
111
|
+
logger.error("Running hook {} has timed out.".format(hook_name))
|
112
|
+
return RC_ERROR
|
113
|
+
except subprocess.CalledProcessError as ex:
|
114
|
+
if ex.returncode < 0:
|
115
|
+
logger.warning("Hook {} got signal {}".format(hook_name, -1 * ex.returncode))
|
116
|
+
return RC_ERROR
|
117
|
+
|
118
|
+
assert(run_dir.startswith(RUN_BASE_DIR))
|
119
|
+
|
120
|
+
try:
|
121
|
+
shutil.rmtree(run_dir)
|
122
|
+
except FileNotFoundError:
|
123
|
+
pass
|
124
|
+
except OSError as err:
|
125
|
+
logger.error("Failed cleanup hook rundir {}: {}".format(run_dir, str(err)))
|
126
|
+
|
127
|
+
return ex.returncode
|
128
|
+
|
129
|
+
class Hooks():
|
130
|
+
def __init__(self, ifstate):
|
131
|
+
self.hooks = {}
|
132
|
+
for hook, opts in ifstate.items():
|
133
|
+
if 'script' in opts:
|
134
|
+
self.hooks[hook] = Hook(hook, **opts)
|
135
|
+
else:
|
136
|
+
self.hooks[hook] = Hook(hook, script=hook, **opts)
|
137
|
+
|
138
|
+
def apply(self, link, do_apply):
|
139
|
+
run_dir = get_netns_run_dir('hooks', link.netns, str(link.idx))
|
140
|
+
|
141
|
+
state_fn = f"{run_dir}/state"
|
142
|
+
old_state = slurp_yaml_file(state_fn, default=[])
|
143
|
+
run_state = []
|
144
|
+
|
145
|
+
# Stop any running hooks which should not run any more or have other
|
146
|
+
# parameters - hooks are identified by all of their settings. Keep
|
147
|
+
# the rundir for any hook already running.
|
148
|
+
for entry in old_state:
|
149
|
+
if not entry.get('hook') in link.hooks:
|
150
|
+
rc = Hook.stop(link, entry["hook"]["name"], entry["rundir"], do_apply)
|
151
|
+
if rc == RC_OK:
|
152
|
+
pass
|
153
|
+
elif rc == RC_STOPPED:
|
154
|
+
logger.log_del('hooks', '- {}'.format(entry["hook"]["name"]))
|
155
|
+
else:
|
156
|
+
run_state.append(entry)
|
157
|
+
logger.log_err('hooks', '! {}'.format(entry["hook"]["name"]))
|
158
|
+
else:
|
159
|
+
hook = next((hook for hook in link.hooks if hook == entry["hook"]), None)
|
160
|
+
# tepmorary keep mapping between running and configured hook dict
|
161
|
+
hook["__rundir"] = entry["rundir"]
|
162
|
+
|
163
|
+
try:
|
164
|
+
if not link.hooks:
|
165
|
+
return
|
166
|
+
|
167
|
+
for hook in link.hooks:
|
168
|
+
if not hook["name"] in self.hooks:
|
169
|
+
logger.warning("Hook {} for {} is unknown!".format(link.settings.get('ifname'), hook))
|
170
|
+
continue
|
171
|
+
|
172
|
+
hook_run_dir = hook.get("__rundir")
|
173
|
+
# hook is not running, yet
|
174
|
+
if hook_run_dir is None:
|
175
|
+
hook_run_dir = tempfile.mkdtemp(prefix="hook_", dir=run_dir)
|
176
|
+
# hook was already running
|
177
|
+
else:
|
178
|
+
del(hook["__rundir"])
|
179
|
+
rc = self.hooks[hook["name"]].start(link, hook.get('args', {}), hook_run_dir, do_apply)
|
180
|
+
|
181
|
+
if rc == RC_OK:
|
182
|
+
logger.log_ok('hooks', '= {}'.format(hook["name"]))
|
183
|
+
elif rc == RC_STARTED:
|
184
|
+
logger.log_add('hooks', '+ {}'.format(hook["name"]))
|
185
|
+
elif rc == RC_CHANGED:
|
186
|
+
logger.log_change('hooks', '~ {}'.format(hook["name"]))
|
187
|
+
else:
|
188
|
+
logger.log_err('hooks', '! {}'.format(hook["name"]))
|
189
|
+
|
190
|
+
run_state.append({
|
191
|
+
"hook": hook,
|
192
|
+
"rundir": hook_run_dir,
|
193
|
+
})
|
194
|
+
finally:
|
195
|
+
dump_yaml_file(state_fn, run_state)
|
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
|
3
|
+
# ifstate: wrapper to run hooks
|
4
|
+
|
5
|
+
# debugging
|
6
|
+
export IFS_VERBOSE=${verbose}
|
7
|
+
if [ "$$IFS_VERBOSE" = 1 ]; then
|
8
|
+
set -x
|
9
|
+
fi
|
10
|
+
|
11
|
+
# return codes
|
12
|
+
export IFS_RC_OK="${rc_ok}"
|
13
|
+
export IFS_RC_ERROR="${rc_error}"
|
14
|
+
export IFS_RC_STARTED="${rc_started}"
|
15
|
+
export IFS_RC_STOPPED="${rc_stopped}"
|
16
|
+
export IFS_RC_CHANGED="${rc_changed}"
|
17
|
+
|
18
|
+
# generic environment variables
|
19
|
+
export IFS_SCRIPT="${script}"
|
20
|
+
export IFS_RUNDIR="${rundir}"
|
21
|
+
|
22
|
+
export IFS_IFNAME="${ifname}"
|
23
|
+
export IFS_INDEX="${index}"
|
24
|
+
export IFS_NETNS="${netns}"
|
25
|
+
export IFS_VRF="${vrf}"
|
26
|
+
|
27
|
+
# hook arguments
|
28
|
+
${args}
|
29
|
+
|
30
|
+
# run hook NetNS and VRF aware
|
31
|
+
if [ -z "$$IFS_NETNS" ]; then
|
32
|
+
if [ -z "$$IFS_VRF" ]; then
|
33
|
+
# just exec the script
|
34
|
+
exec "$$IFS_SCRIPT" "$$@"
|
35
|
+
else
|
36
|
+
# exec in VRF
|
37
|
+
exec ip vrf exec "$$IFS_VRF" "$$IFS_SCRIPT" "$$@"
|
38
|
+
fi
|
39
|
+
else
|
40
|
+
if [ -z "$$IFS_VRF" ]; then
|
41
|
+
# exec in NetNS
|
42
|
+
exec ip netns exec "$$IFS_NETNS" "$$IFS_SCRIPT" "$$@"
|
43
|
+
else
|
44
|
+
# exec in NetNS->VRF
|
45
|
+
exec ip -n "$$IFS_NETNS" vrf exec "$$IFS_VRF" "$$IFS_SCRIPT" "$$@"
|
46
|
+
fi
|
47
|
+
fi
|
48
|
+
|
49
|
+
# somthing gone wrong
|
50
|
+
return $$IFS_RC_ERROR
|
libifstate/link/base.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
from libifstate.util import logger, IfStateLogging, LinkDependency
|
1
|
+
from libifstate.util import logger, format_ether_address, IfStateLogging, LinkDependency, IDENTIFY_LOOKUPS
|
2
2
|
from libifstate.exception import ExceptionCollector, LinkTypeUnknown, NetnsUnknown, netlinkerror_classes
|
3
3
|
from libifstate.brport import BRPort
|
4
4
|
from libifstate.routing import RTLookups
|
@@ -129,7 +129,7 @@ class Link(ABC):
|
|
129
129
|
return super().__new__(GenericLink)
|
130
130
|
#raise LinkTypeUnknown()
|
131
131
|
|
132
|
-
def __init__(self, ifstate, netns, name, link, ethtool, vrrp, brport):
|
132
|
+
def __init__(self, ifstate, netns, name, link, identify, ethtool, hooks, vrrp, brport):
|
133
133
|
self.ifstate = ifstate
|
134
134
|
self.netns = netns
|
135
135
|
self.cap_create = True
|
@@ -139,6 +139,7 @@ class Link(ABC):
|
|
139
139
|
}
|
140
140
|
self.settings.update(link)
|
141
141
|
self.ethtool = None
|
142
|
+
self.hooks = hooks
|
142
143
|
self.vrrp = vrrp
|
143
144
|
if brport:
|
144
145
|
self.brport = BRPort(netns, name, brport)
|
@@ -154,22 +155,17 @@ class Link(ABC):
|
|
154
155
|
self.link_ref = LinkDependency(name, self.netns.netns)
|
155
156
|
|
156
157
|
# prepare link registry search filters
|
157
|
-
if
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
self.settings['permaddr'] = self.settings['permaddr'].lower()
|
166
|
-
self.link_registry_search_args.append({
|
167
|
-
'kind': self.settings['kind'],
|
168
|
-
'permaddr': self.settings['permaddr'],
|
169
|
-
})
|
158
|
+
if identify:
|
159
|
+
search_args = {}
|
160
|
+
for attr in IDENTIFY_LOOKUPS.keys():
|
161
|
+
if attr in identify:
|
162
|
+
search_args[attr] = identify[attr]
|
163
|
+
if search_args:
|
164
|
+
search_args['kind'] = self.settings['kind']
|
165
|
+
self.link_registry_search_args.append(search_args)
|
170
166
|
|
171
167
|
if 'address' in self.settings and self.settings['kind'] == 'physical':
|
172
|
-
self.settings['address'] = self.settings['address']
|
168
|
+
self.settings['address'] = format_ether_address(self.settings['address'])
|
173
169
|
self.link_registry_search_args.append({
|
174
170
|
'kind': self.settings['kind'],
|
175
171
|
'address': self.settings['address'],
|
@@ -249,11 +245,8 @@ class Link(ABC):
|
|
249
245
|
return None
|
250
246
|
|
251
247
|
def get_if_attr(self, key):
|
252
|
-
if key
|
253
|
-
|
254
|
-
return self.iface[key]
|
255
|
-
else:
|
256
|
-
return None
|
248
|
+
if key == "state":
|
249
|
+
return self.iface.get(key)
|
257
250
|
|
258
251
|
if key in self.attr_map:
|
259
252
|
return self._drill_attr(self.iface, self.attr_map[key])
|
@@ -443,7 +436,7 @@ class Link(ABC):
|
|
443
436
|
return self.match_vrrp_select(vrrp_type, vrrp_name) and (vrrp_state in self.vrrp['states'])
|
444
437
|
|
445
438
|
def apply(self, do_apply, sysctl):
|
446
|
-
excpts = ExceptionCollector(self.settings['ifname'])
|
439
|
+
excpts = ExceptionCollector(self.settings['ifname'], self.netns)
|
447
440
|
osettings = copy.deepcopy(self.settings)
|
448
441
|
|
449
442
|
# lookup for attributes requiring a interface index
|
@@ -452,11 +445,20 @@ class Link(ABC):
|
|
452
445
|
netns_attr = "{}_netns".format(attr)
|
453
446
|
netnsid_attr = "{}_netnsid".format(attr)
|
454
447
|
if netns_attr in self.settings:
|
455
|
-
#
|
456
|
-
(
|
457
|
-
self.settings[
|
458
|
-
|
459
|
-
|
448
|
+
# ignore *_netns settings if the netns is the same as the interface's one
|
449
|
+
# (there is no IFLA_LINK_NETNSID attribute in such cases)
|
450
|
+
if self.settings[netns_attr] != self.netns.netns:
|
451
|
+
try:
|
452
|
+
(peer_ipr, peer_nsid) = self.netns.get_netnsid(self.settings[netns_attr])
|
453
|
+
except NetnsUnknown as ex:
|
454
|
+
excpts.add('apply', ex)
|
455
|
+
return excpts
|
456
|
+
self.settings[netnsid_attr] = peer_nsid
|
457
|
+
idx = next(iter(peer_ipr.link_lookup(
|
458
|
+
ifname=self.settings[attr])), None)
|
459
|
+
else:
|
460
|
+
idx = next(iter(self.netns.ipr.link_lookup(
|
461
|
+
ifname=self.settings[attr])), None)
|
460
462
|
|
461
463
|
del(self.settings[netns_attr])
|
462
464
|
else:
|
@@ -516,13 +518,6 @@ class Link(ABC):
|
|
516
518
|
if self.idx is not None:
|
517
519
|
self.iface = item.netns.ipr.get_link(self.idx)
|
518
520
|
if self.idx is not None and self.iface is not None:
|
519
|
-
permaddr = item.netns.ipr.get_permaddr(self.iface.get_attr('IFLA_IFNAME'))
|
520
|
-
if not permaddr is None:
|
521
|
-
self.iface['permaddr'] = permaddr
|
522
|
-
businfo = item.netns.ipr.get_businfo(self.iface.get_attr('IFLA_IFNAME'))
|
523
|
-
if not businfo is None:
|
524
|
-
self.iface['businfo'] = businfo
|
525
|
-
|
526
521
|
# check for ifname collisions
|
527
522
|
idx = next(iter(self.netns.ipr.link_lookup(
|
528
523
|
ifname=self.settings['ifname'])), None)
|
@@ -625,6 +620,9 @@ class Link(ABC):
|
|
625
620
|
if not isinstance(err, netlinkerror_classes):
|
626
621
|
raise
|
627
622
|
excpts.add('set', err, state=state)
|
623
|
+
|
624
|
+
# get kernel state
|
625
|
+
self.iface = self.netns.ipr.get_link(self.idx)
|
628
626
|
except Exception as err:
|
629
627
|
if not isinstance(err, netlinkerror_classes):
|
630
628
|
raise
|
@@ -737,7 +735,7 @@ class Link(ABC):
|
|
737
735
|
if do_apply:
|
738
736
|
# temp. remove special settings
|
739
737
|
skipped_settings = {}
|
740
|
-
for setting in ['state', 'peer', 'kind'
|
738
|
+
for setting in ['state', 'peer', 'kind']:
|
741
739
|
if setting in self.settings:
|
742
740
|
skipped_settings[setting] = self.settings.pop(setting)
|
743
741
|
|
@@ -832,6 +830,9 @@ class Link(ABC):
|
|
832
830
|
else:
|
833
831
|
logger.log_ok('link')
|
834
832
|
|
833
|
+
# update kernel state
|
834
|
+
self.iface = self.netns.ipr.get_link(self.idx)
|
835
|
+
|
835
836
|
def depends(self):
|
836
837
|
deps = []
|
837
838
|
|
@@ -894,5 +895,5 @@ class Link(ABC):
|
|
894
895
|
|
895
896
|
|
896
897
|
class GenericLink(Link):
|
897
|
-
def __init__(self, ifstate, netns, name, link, ethtool, vrrp, brport):
|
898
|
-
super().__init__(ifstate, netns, name, link, ethtool, vrrp, brport)
|
898
|
+
def __init__(self, ifstate, netns, name, link, identify, ethtool, hooks, vrrp, brport):
|
899
|
+
super().__init__(ifstate, netns, name, link, identify, ethtool, hooks, vrrp, brport)
|
libifstate/link/dsa.py
CHANGED
libifstate/link/physical.py
CHANGED
@@ -3,11 +3,11 @@ from libifstate.link.base import Link
|
|
3
3
|
from libifstate.exception import LinkCannotAdd
|
4
4
|
|
5
5
|
class PhysicalLink(Link):
|
6
|
-
def __init__(self, ifstate, netns, name, link, ethtool, vrrp, brport):
|
7
|
-
super().__init__(ifstate, netns, name, link, ethtool, vrrp, brport)
|
6
|
+
def __init__(self, ifstate, netns, name, link, identify, ethtool, hooks, vrrp, brport):
|
7
|
+
super().__init__(ifstate, netns, name, link, identify, ethtool, hooks, vrrp, brport)
|
8
8
|
self.cap_create = False
|
9
9
|
self.cap_ethtool = True
|
10
10
|
self.ethtool = ethtool
|
11
11
|
|
12
12
|
def create(self, do_apply, sysctl, excpts, oper="add"):
|
13
|
-
logger.warning('
|
13
|
+
logger.warning('unable to create physical link', extra={'iface': self.settings.get('ifname'), 'netns': self.netns})
|
libifstate/link/tun.py
CHANGED
@@ -6,18 +6,18 @@ from pwd import getpwnam
|
|
6
6
|
from grp import getgrnam
|
7
7
|
|
8
8
|
class TunLink(Link):
|
9
|
-
def __init__(self, ifstate, netns, name, link, ethtool, vrrp, brport):
|
9
|
+
def __init__(self, ifstate, netns, name, link, identify, ethtool, hooks, vrrp, brport):
|
10
10
|
if 'tun_owner' in link and isinstance(link['tun_owner'], str):
|
11
11
|
link['tun_owner'] = getpwnam(link['tun_owner'])[2]
|
12
12
|
|
13
13
|
if 'tun_group' in link and isinstance(link['tun_group'], str):
|
14
14
|
link['tun_group'] = getgrnam(link['tun_group'])[2]
|
15
15
|
|
16
|
-
super().__init__(ifstate, netns, name, link, ethtool, vrrp, brport)
|
16
|
+
super().__init__(ifstate, netns, name, link, identify, ethtool, hooks, vrrp, brport)
|
17
17
|
self.cap_create = bool(link.get('tun_persist'))
|
18
18
|
|
19
19
|
def create(self, do_apply, sysctl, excpts, oper="add"):
|
20
20
|
if not self.cap_create:
|
21
|
-
logger.warning('
|
21
|
+
logger.warning('unable to create non-persistent tuntap link', extra={'iface': self.settings.get('ifname'), 'netns': self.netns})
|
22
22
|
else:
|
23
23
|
super().create(do_apply, sysctl, excpts, oper)
|
libifstate/link/veth.py
CHANGED
@@ -3,14 +3,14 @@ from libifstate.link.base import Link
|
|
3
3
|
from libifstate.exception import LinkCannotAdd, NetnsUnknown
|
4
4
|
|
5
5
|
class VethLink(Link):
|
6
|
-
def __init__(self, ifstate, netns, name, link, ethtool, vrrp, brport):
|
6
|
+
def __init__(self, ifstate, netns, name, link, identify, ethtool, hooks, vrrp, brport):
|
7
7
|
# use the bind_netns implementation to create the peer in the
|
8
8
|
# target netns
|
9
9
|
if 'peer_netns' in link:
|
10
10
|
link['bind_netns'] = link['peer_netns']
|
11
11
|
del(link['peer_netns'])
|
12
12
|
|
13
|
-
super().__init__(ifstate, netns, name, link, ethtool, vrrp, brport)
|
13
|
+
super().__init__(ifstate, netns, name, link, identify, ethtool, hooks, vrrp, brport)
|
14
14
|
|
15
15
|
def create(self, do_apply, sysctl, excpts, oper="add"):
|
16
16
|
'''
|
libifstate/log.py
CHANGED
@@ -73,13 +73,16 @@ class IfStateLogging:
|
|
73
73
|
return IfStateLogging.ANSI_YELLOW
|
74
74
|
return ""
|
75
75
|
|
76
|
-
def __init__(self, level, handlers=[], action=None, log_stderr=True):
|
76
|
+
def __init__(self, level, global_level, handlers=[], action=None, log_stderr=True):
|
77
77
|
if level != logging.DEBUG:
|
78
78
|
sys.tracebacklimit = 0
|
79
79
|
|
80
|
-
|
81
|
-
|
82
|
-
|
80
|
+
if global_level:
|
81
|
+
logging.basicConfig(
|
82
|
+
level=level,
|
83
|
+
)
|
84
|
+
else:
|
85
|
+
logger.level = level
|
83
86
|
|
84
87
|
if log_stderr:
|
85
88
|
has_stderr = sys.stderr is not None
|
libifstate/netns/__init__.py
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
-
from libifstate.
|
1
|
+
from libifstate.exception import NetnsUnknown
|
2
|
+
from libifstate.util import logger, IfStateLogging, IPRouteExt, NetNSExt, root_ipr, root_iw, IDENTIFY_LOOKUPS
|
2
3
|
from libifstate.sysctl import Sysctl
|
3
4
|
|
4
5
|
import atexit
|
5
6
|
from copy import deepcopy
|
6
7
|
import logging
|
7
8
|
import pyroute2
|
9
|
+
from pyroute2 import NetlinkError
|
8
10
|
import re
|
9
11
|
import secrets
|
10
12
|
import shutil
|
@@ -13,6 +15,7 @@ import subprocess
|
|
13
15
|
netns_name_map = {}
|
14
16
|
netns_name_root = None
|
15
17
|
netns_nsid_map = {}
|
18
|
+
iw_ifindex_phy_map = {}
|
16
19
|
|
17
20
|
@atexit.register
|
18
21
|
def close_netns():
|
@@ -44,12 +47,27 @@ class NetNameSpace():
|
|
44
47
|
|
45
48
|
if name is None:
|
46
49
|
self.ipr = root_ipr
|
50
|
+
self.iw = root_iw
|
47
51
|
self.mount = b''
|
48
52
|
else:
|
49
53
|
self.ipr = NetNSExt(name)
|
50
54
|
netns_name_map[name] = self.ipr
|
55
|
+
|
56
|
+
# check for wireless phys
|
57
|
+
if root_iw:
|
58
|
+
pyroute2.netns.pushns(name)
|
59
|
+
self.iw = pyroute2.IW()
|
60
|
+
pyroute2.netns.popns()
|
61
|
+
else:
|
62
|
+
self.iw = None
|
63
|
+
|
51
64
|
self.mount = name.encode("utf-8")
|
52
65
|
|
66
|
+
if self.iw is not None:
|
67
|
+
for ifname, ifdict in self.iw.get_interfaces_dict().items():
|
68
|
+
# ifIndex => phyIndex
|
69
|
+
iw_ifindex_phy_map[ifdict[0]] = ifdict[3]
|
70
|
+
|
53
71
|
def __deepcopy__(self, memo):
|
54
72
|
'''
|
55
73
|
Add custom deepcopy implementation to keep single IPRoute and NetNS instances.
|
@@ -59,7 +77,10 @@ class NetNameSpace():
|
|
59
77
|
memo[id(self)] = result
|
60
78
|
for k, v in self.__dict__.items():
|
61
79
|
if k == 'ipr':
|
62
|
-
|
80
|
+
if self.netns is None:
|
81
|
+
setattr(result, k, IPRouteExt())
|
82
|
+
else:
|
83
|
+
setattr(result, k, NetNSExt(self.netns))
|
63
84
|
else:
|
64
85
|
setattr(result, k, deepcopy(v, memo))
|
65
86
|
return result
|
@@ -68,9 +89,11 @@ class NetNameSpace():
|
|
68
89
|
if peer_netns_name is None:
|
69
90
|
peer_ipr = root_ipr
|
70
91
|
peer_pid = 1
|
71
|
-
|
92
|
+
elif peer_netns_name in netns_name_map:
|
72
93
|
peer_ipr = netns_name_map[peer_netns_name]
|
73
94
|
peer_pid = peer_ipr.child
|
95
|
+
else:
|
96
|
+
raise NetnsUnknown(peer_netns_name)
|
74
97
|
|
75
98
|
result = self.ipr.get_netnsid(pid=peer_pid)
|
76
99
|
if result['nsid'] == 4294967295:
|
@@ -81,7 +104,7 @@ class NetNameSpace():
|
|
81
104
|
|
82
105
|
return (peer_ipr, peer_nsid)
|
83
106
|
|
84
|
-
def prepare_netns(do_apply, target_netns_list, new_netns_list):
|
107
|
+
def prepare_netns(do_apply, target_netns_list, new_netns_list, ignore_netns):
|
85
108
|
logger.info("configure network namespaces...")
|
86
109
|
|
87
110
|
# get mapping of netns names to lists of pids
|
@@ -94,14 +117,15 @@ def prepare_netns(do_apply, target_netns_list, new_netns_list):
|
|
94
117
|
for name in sorted(names_set):
|
95
118
|
# cleanup orphan netns
|
96
119
|
if name not in target_netns_list:
|
97
|
-
if name in
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
120
|
+
if not any(re.match(regex, name) for regex in ignore_netns):
|
121
|
+
if name in ns_pids:
|
122
|
+
logger.warning(
|
123
|
+
'pids: {}'.format(', '.join((str(x) for x in ns_pids[name]))),
|
124
|
+
extra={'iface': name})
|
125
|
+
logger.log_del(name)
|
102
126
|
|
103
|
-
|
104
|
-
|
127
|
+
if do_apply:
|
128
|
+
pyroute2.netns.remove(name)
|
105
129
|
|
106
130
|
# create missing netns
|
107
131
|
elif name not in current_netns_list or name in new_netns_list:
|
@@ -222,8 +246,16 @@ class LinkRegistryItem():
|
|
222
246
|
self.attributes['kind'] = linkinfo.get_attr('IFLA_INFO_KIND')
|
223
247
|
else:
|
224
248
|
self.attributes['kind'] = "physical"
|
225
|
-
|
226
|
-
|
249
|
+
|
250
|
+
# add iw phy for wireless interfaces
|
251
|
+
if link['index'] in iw_ifindex_phy_map:
|
252
|
+
self.attributes['wiphy'] = iw_ifindex_phy_map[link['index']]
|
253
|
+
|
254
|
+
# add identify attributes
|
255
|
+
for attr, lookup in IDENTIFY_LOOKUPS.items():
|
256
|
+
value = lookup(netns, link)
|
257
|
+
if value is not None:
|
258
|
+
self.attributes[attr] = value
|
227
259
|
|
228
260
|
def __ipr_link(self, command, **kwargs):
|
229
261
|
logger.debug("ip link set netns={} {}".format(
|
@@ -270,7 +302,14 @@ class LinkRegistryItem():
|
|
270
302
|
# ToDo
|
271
303
|
self.update_ifname( self.registry.get_random_name('__netns__') )
|
272
304
|
|
273
|
-
|
305
|
+
if self.attributes.get('wiphy') is not None:
|
306
|
+
# move phy instead of iface for wireless devices
|
307
|
+
if netns.netns is None:
|
308
|
+
self.netns.iw.set_wiphy_netns_by_pid(self.attributes['wiphy'], 1)
|
309
|
+
else:
|
310
|
+
self.netns.iw.set_wiphy_netns_by_pid(self.attributes['wiphy'], netns.ipr.child)
|
311
|
+
else:
|
312
|
+
self.__ipr_link('set', index=self.attributes['index'], net_ns_fd=netns_name)
|
274
313
|
self.netns = netns
|
275
314
|
self.attributes['index'] = next(iter(self.netns.ipr.link_lookup(ifname=self.attributes['ifname'])), None)
|
276
315
|
|
libifstate/parser/__init__.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
from libifstate.parser.base import Parser
|
1
|
+
from libifstate.parser.base import Parser, get_available_hooks
|
2
2
|
from libifstate.parser.yaml import YamlParser
|