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.
@@ -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 'businfo' in self.settings:
158
- self.settings['businfo'] = self.settings['businfo'].lower()
159
- self.link_registry_search_args.append({
160
- 'kind': self.settings['kind'],
161
- 'businfo': self.settings['businfo'],
162
- })
163
-
164
- if 'permaddr' in self.settings:
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'].lower()
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 in ["state", "permaddr", "businfo"]:
253
- if key in self.iface:
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
- # ToDo: throw exception for unknown netns
456
- (peer_ipr, peer_nsid) = self.netns.get_netnsid(self.settings[netns_attr])
457
- self.settings[netnsid_attr] = peer_nsid
458
- idx = next(iter(peer_ipr.link_lookup(
459
- ifname=self.settings[attr])), None)
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', 'businfo', 'permaddr']:
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
@@ -4,7 +4,7 @@ from libifstate.link.physical import PhysicalLink
4
4
  class DsaLink(PhysicalLink):
5
5
  """
6
6
  Distributed Switch Architecture (DSA) user interface
7
-
7
+
8
8
  https://docs.kernel.org/networking/dsa/configuration.html
9
9
  """
10
10
  pass
@@ -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('Unable to create missing physical link: {}'.format(self.settings.get('ifname')))
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('Unable to create missing non-persistent tuntap link: {}'.format(self.settings.get('ifname')))
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
- logging.basicConfig(
81
- level=level,
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
@@ -1,10 +1,12 @@
1
- from libifstate.util import logger, IfStateLogging, IPRouteExt, NetNSExt, root_ipr
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
- setattr(result, k, v)
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
- else:
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 ns_pids:
98
- logger.warning(
99
- 'pids: {}'.format(', '.join((str(x) for x in ns_pids[name]))),
100
- extra={'iface': name})
101
- logger.log_del(name)
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
- if do_apply:
104
- pyroute2.netns.remove(name)
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
- self.attributes['businfo'] = self.netns.ipr.get_businfo(self.attributes['ifname'])
226
- self.attributes['permaddr'] = link.get_attr('IFLA_PERM_ADDRESS')
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
- self.__ipr_link('set', index=self.attributes['index'], net_ns_fd=netns_name)
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
 
@@ -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