ifstate 1.13.7__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-1.13.7.dist-info → ifstate-2.0.0.dist-info}/METADATA +2 -2
- ifstate-2.0.0.dist-info/RECORD +39 -0
- libifstate/__init__.py +147 -78
- 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 +29 -34
- 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 +44 -6
- libifstate/parser/__init__.py +1 -1
- libifstate/parser/base.py +131 -85
- libifstate/routing/__init__.py +63 -17
- 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.7.dist-info/RECORD +0 -37
- schema/ifstate.conf.schema.json +0 -4259
- {ifstate-1.13.7.dist-info → ifstate-2.0.0.dist-info}/WHEEL +0 -0
- {ifstate-1.13.7.dist-info → ifstate-2.0.0.dist-info}/entry_points.txt +0 -0
- {ifstate-1.13.7.dist-info → ifstate-2.0.0.dist-info}/licenses/LICENSE +0 -0
- {ifstate-1.13.7.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
|
@@ -455,8 +448,11 @@ class Link(ABC):
|
|
455
448
|
# ignore *_netns settings if the netns is the same as the interface's one
|
456
449
|
# (there is no IFLA_LINK_NETNSID attribute in such cases)
|
457
450
|
if self.settings[netns_attr] != self.netns.netns:
|
458
|
-
|
459
|
-
|
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
|
460
456
|
self.settings[netnsid_attr] = peer_nsid
|
461
457
|
idx = next(iter(peer_ipr.link_lookup(
|
462
458
|
ifname=self.settings[attr])), None)
|
@@ -522,13 +518,6 @@ class Link(ABC):
|
|
522
518
|
if self.idx is not None:
|
523
519
|
self.iface = item.netns.ipr.get_link(self.idx)
|
524
520
|
if self.idx is not None and self.iface is not None:
|
525
|
-
permaddr = item.netns.ipr.get_permaddr(self.iface.get_attr('IFLA_IFNAME'))
|
526
|
-
if not permaddr is None:
|
527
|
-
self.iface['permaddr'] = permaddr
|
528
|
-
businfo = item.netns.ipr.get_businfo(self.iface.get_attr('IFLA_IFNAME'))
|
529
|
-
if not businfo is None:
|
530
|
-
self.iface['businfo'] = businfo
|
531
|
-
|
532
521
|
# check for ifname collisions
|
533
522
|
idx = next(iter(self.netns.ipr.link_lookup(
|
534
523
|
ifname=self.settings['ifname'])), None)
|
@@ -631,6 +620,9 @@ class Link(ABC):
|
|
631
620
|
if not isinstance(err, netlinkerror_classes):
|
632
621
|
raise
|
633
622
|
excpts.add('set', err, state=state)
|
623
|
+
|
624
|
+
# get kernel state
|
625
|
+
self.iface = self.netns.ipr.get_link(self.idx)
|
634
626
|
except Exception as err:
|
635
627
|
if not isinstance(err, netlinkerror_classes):
|
636
628
|
raise
|
@@ -743,7 +735,7 @@ class Link(ABC):
|
|
743
735
|
if do_apply:
|
744
736
|
# temp. remove special settings
|
745
737
|
skipped_settings = {}
|
746
|
-
for setting in ['state', 'peer', 'kind'
|
738
|
+
for setting in ['state', 'peer', 'kind']:
|
747
739
|
if setting in self.settings:
|
748
740
|
skipped_settings[setting] = self.settings.pop(setting)
|
749
741
|
|
@@ -838,6 +830,9 @@ class Link(ABC):
|
|
838
830
|
else:
|
839
831
|
logger.log_ok('link')
|
840
832
|
|
833
|
+
# update kernel state
|
834
|
+
self.iface = self.netns.ipr.get_link(self.idx)
|
835
|
+
|
841
836
|
def depends(self):
|
842
837
|
deps = []
|
843
838
|
|
@@ -900,5 +895,5 @@ class Link(ABC):
|
|
900
895
|
|
901
896
|
|
902
897
|
class GenericLink(Link):
|
903
|
-
def __init__(self, ifstate, netns, name, link, ethtool, vrrp, brport):
|
904
|
-
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:
|
@@ -223,8 +246,16 @@ class LinkRegistryItem():
|
|
223
246
|
self.attributes['kind'] = linkinfo.get_attr('IFLA_INFO_KIND')
|
224
247
|
else:
|
225
248
|
self.attributes['kind'] = "physical"
|
226
|
-
|
227
|
-
|
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
|
228
259
|
|
229
260
|
def __ipr_link(self, command, **kwargs):
|
230
261
|
logger.debug("ip link set netns={} {}".format(
|
@@ -271,7 +302,14 @@ class LinkRegistryItem():
|
|
271
302
|
# ToDo
|
272
303
|
self.update_ifname( self.registry.get_random_name('__netns__') )
|
273
304
|
|
274
|
-
|
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)
|
275
313
|
self.netns = netns
|
276
314
|
self.attributes['index'] = next(iter(self.netns.ipr.link_lookup(ifname=self.attributes['ifname'])), None)
|
277
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
|