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.
@@ -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
@@ -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
- # ToDo: throw exception for unknown netns
459
- (peer_ipr, peer_nsid) = self.netns.get_netnsid(self.settings[netns_attr])
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', 'businfo', 'permaddr']:
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
@@ -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:
@@ -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
- self.attributes['businfo'] = self.netns.ipr.get_businfo(self.attributes['ifname'])
227
- 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
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
- 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)
275
313
  self.netns = netns
276
314
  self.attributes['index'] = next(iter(self.netns.ipr.link_lookup(ifname=self.attributes['ifname'])), None)
277
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