ifstate 1.13.6__py3-none-any.whl → 2.0.0rc1__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.
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])
@@ -452,11 +445,17 @@ 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
+ # ToDo: throw exception for unknown netns
452
+ (peer_ipr, peer_nsid) = self.netns.get_netnsid(self.settings[netns_attr])
453
+ self.settings[netnsid_attr] = peer_nsid
454
+ idx = next(iter(peer_ipr.link_lookup(
455
+ ifname=self.settings[attr])), None)
456
+ else:
457
+ idx = next(iter(self.netns.ipr.link_lookup(
458
+ ifname=self.settings[attr])), None)
460
459
 
461
460
  del(self.settings[netns_attr])
462
461
  else:
@@ -516,13 +515,6 @@ class Link(ABC):
516
515
  if self.idx is not None:
517
516
  self.iface = item.netns.ipr.get_link(self.idx)
518
517
  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
518
  # check for ifname collisions
527
519
  idx = next(iter(self.netns.ipr.link_lookup(
528
520
  ifname=self.settings['ifname'])), None)
@@ -625,6 +617,9 @@ class Link(ABC):
625
617
  if not isinstance(err, netlinkerror_classes):
626
618
  raise
627
619
  excpts.add('set', err, state=state)
620
+
621
+ # get kernel state
622
+ self.iface = self.netns.ipr.get_link(self.idx)
628
623
  except Exception as err:
629
624
  if not isinstance(err, netlinkerror_classes):
630
625
  raise
@@ -737,7 +732,7 @@ class Link(ABC):
737
732
  if do_apply:
738
733
  # temp. remove special settings
739
734
  skipped_settings = {}
740
- for setting in ['state', 'peer', 'kind', 'businfo', 'permaddr']:
735
+ for setting in ['state', 'peer', 'kind']:
741
736
  if setting in self.settings:
742
737
  skipped_settings[setting] = self.settings.pop(setting)
743
738
 
@@ -832,6 +827,9 @@ class Link(ABC):
832
827
  else:
833
828
  logger.log_ok('link')
834
829
 
830
+ # update kernel state
831
+ self.iface = self.netns.ipr.get_link(self.idx)
832
+
835
833
  def depends(self):
836
834
  deps = []
837
835
 
@@ -894,5 +892,5 @@ class Link(ABC):
894
892
 
895
893
 
896
894
  class GenericLink(Link):
897
- def __init__(self, ifstate, netns, name, link, ethtool, vrrp, brport):
898
- super().__init__(ifstate, netns, name, link, ethtool, vrrp, brport)
895
+ def __init__(self, ifstate, netns, name, link, identify, ethtool, hooks, vrrp, brport):
896
+ super().__init__(ifstate, netns, name, link, identify, ethtool, hooks, vrrp, brport)
@@ -3,8 +3,8 @@ 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
libifstate/link/tun.py CHANGED
@@ -6,14 +6,14 @@ 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"):
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
  '''
@@ -1,10 +1,11 @@
1
- from libifstate.util import logger, IfStateLogging, IPRouteExt, NetNSExt, root_ipr
1
+ from libifstate.util import logger, IfStateLogging, IPRouteExt, NetNSExt, root_ipr, root_iw, IDENTIFY_LOOKUPS
2
2
  from libifstate.sysctl import Sysctl
3
3
 
4
4
  import atexit
5
5
  from copy import deepcopy
6
6
  import logging
7
7
  import pyroute2
8
+ from pyroute2.netlink.exceptions import NetlinkError
8
9
  import re
9
10
  import secrets
10
11
  import shutil
@@ -13,6 +14,7 @@ import subprocess
13
14
  netns_name_map = {}
14
15
  netns_name_root = None
15
16
  netns_nsid_map = {}
17
+ iw_ifindex_phy_map = {}
16
18
 
17
19
  @atexit.register
18
20
  def close_netns():
@@ -44,12 +46,27 @@ class NetNameSpace():
44
46
 
45
47
  if name is None:
46
48
  self.ipr = root_ipr
49
+ self.iw = root_iw
47
50
  self.mount = b''
48
51
  else:
49
52
  self.ipr = NetNSExt(name)
50
53
  netns_name_map[name] = self.ipr
54
+
55
+ # check for wireless phys
56
+ if root_iw:
57
+ pyroute2.netns.pushns(name)
58
+ self.iw = pyroute2.IW()
59
+ pyroute2.netns.popns()
60
+ else:
61
+ self.iw = None
62
+
51
63
  self.mount = name.encode("utf-8")
52
64
 
65
+ if self.iw is not None:
66
+ for ifname, ifdict in self.iw.get_interfaces_dict().items():
67
+ # ifIndex => phyIndex
68
+ iw_ifindex_phy_map[ifdict[0]] = ifdict[3]
69
+
53
70
  def __deepcopy__(self, memo):
54
71
  '''
55
72
  Add custom deepcopy implementation to keep single IPRoute and NetNS instances.
@@ -59,7 +76,10 @@ class NetNameSpace():
59
76
  memo[id(self)] = result
60
77
  for k, v in self.__dict__.items():
61
78
  if k == 'ipr':
62
- setattr(result, k, v)
79
+ if self.netns is None:
80
+ setattr(result, k, IPRouteExt())
81
+ else:
82
+ setattr(result, k, NetNSExt(self.netns))
63
83
  else:
64
84
  setattr(result, k, deepcopy(v, memo))
65
85
  return result
@@ -81,7 +101,7 @@ class NetNameSpace():
81
101
 
82
102
  return (peer_ipr, peer_nsid)
83
103
 
84
- def prepare_netns(do_apply, target_netns_list, new_netns_list):
104
+ def prepare_netns(do_apply, target_netns_list, new_netns_list, ignore_netns):
85
105
  logger.info("configure network namespaces...")
86
106
 
87
107
  # get mapping of netns names to lists of pids
@@ -94,14 +114,15 @@ def prepare_netns(do_apply, target_netns_list, new_netns_list):
94
114
  for name in sorted(names_set):
95
115
  # cleanup orphan netns
96
116
  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)
117
+ if not any(re.match(regex, name) for regex in ignore_netns):
118
+ if name in ns_pids:
119
+ logger.warning(
120
+ 'pids: {}'.format(', '.join((str(x) for x in ns_pids[name]))),
121
+ extra={'iface': name})
122
+ logger.log_del(name)
102
123
 
103
- if do_apply:
104
- pyroute2.netns.remove(name)
124
+ if do_apply:
125
+ pyroute2.netns.remove(name)
105
126
 
106
127
  # create missing netns
107
128
  elif name not in current_netns_list or name in new_netns_list:
@@ -222,8 +243,16 @@ class LinkRegistryItem():
222
243
  self.attributes['kind'] = linkinfo.get_attr('IFLA_INFO_KIND')
223
244
  else:
224
245
  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')
246
+
247
+ # add iw phy for wireless interfaces
248
+ if link['index'] in iw_ifindex_phy_map:
249
+ self.attributes['wiphy'] = iw_ifindex_phy_map[link['index']]
250
+
251
+ # add identify attributes
252
+ for attr, lookup in IDENTIFY_LOOKUPS.items():
253
+ value = lookup(netns, link)
254
+ if value is not None:
255
+ self.attributes[attr] = value
227
256
 
228
257
  def __ipr_link(self, command, **kwargs):
229
258
  logger.debug("ip link set netns={} {}".format(
@@ -270,7 +299,14 @@ class LinkRegistryItem():
270
299
  # ToDo
271
300
  self.update_ifname( self.registry.get_random_name('__netns__') )
272
301
 
273
- self.__ipr_link('set', index=self.attributes['index'], net_ns_fd=netns_name)
302
+ if self.attributes.get('wiphy') is not None:
303
+ # move phy instead of iface for wireless devices
304
+ if netns.netns is None:
305
+ self.netns.iw.set_wiphy_netns_by_pid(self.attributes['wiphy'], 1)
306
+ else:
307
+ self.netns.iw.set_wiphy_netns_by_pid(self.attributes['wiphy'], netns.ipr.child)
308
+ else:
309
+ self.__ipr_link('set', index=self.attributes['index'], net_ns_fd=netns_name)
274
310
  self.netns = netns
275
311
  self.attributes['index'] = next(iter(self.netns.ipr.link_lookup(ifname=self.attributes['ifname'])), None)
276
312
 
libifstate/parser/base.py CHANGED
@@ -6,7 +6,6 @@ from copy import deepcopy
6
6
 
7
7
  class Parser(ABC):
8
8
  _default_lo_link = {
9
- 'name': 'lo',
10
9
  'addresses': [
11
10
  '127.0.0.1/8',
12
11
  '::1/128',
@@ -18,79 +17,96 @@ class Parser(ABC):
18
17
  }
19
18
  }
20
19
  _default_ifstates = {
21
- 'ignore': {
22
- 'ipaddr_builtin': [
23
- 'fe80::/10'
24
- ],
25
- 'ipaddr_dynamic': True,
26
- 'ifname_builtin': [
27
- r'^br-[\da-f]{12}',
28
- r'^docker\d+',
29
- r'^ppp\d+$',
30
- r'^veth',
31
- r'^virbr\d+',
32
- r'^vrrp\d*\.\d+$'
33
- ],
34
- 'fdb_builtin': [
35
- r'^33:33:',
36
- r'^01:00:5e:'
37
- ],
38
- 'routes_builtin': [
39
- {'proto': 1},
40
- {'proto': 2},
41
- {'proto': 8},
42
- {'proto': 9},
43
- {'proto': 10},
44
- {'proto': 11},
45
- {'proto': 12},
46
- {'proto': 13},
47
- {'proto': 14},
48
- {'proto': 15},
49
- {'proto': 16},
50
- {'proto': 18},
51
- {'proto': 42},
52
- {'proto': 186},
53
- {'proto': 187},
54
- {'proto': 188},
55
- {'proto': 189},
56
- {'proto': 192},
57
- {'to': 'ff00::/8'},
58
- ],
59
- 'rules_builtin': [
60
- {'proto': 1},
61
- {'proto': 2},
62
- {'proto': 8},
63
- {'proto': 9},
64
- {'proto': 10},
65
- {'proto': 11},
66
- {'proto': 12},
67
- {'proto': 13},
68
- {'proto': 14},
69
- {'proto': 15},
70
- {'proto': 16},
71
- {'proto': 18},
72
- {'proto': 42},
73
- {'proto': 186},
74
- {'proto': 187},
75
- {'proto': 188},
76
- {'proto': 189},
77
- {'proto': 192},
78
- ],
79
- },
80
- 'cshaper': {
81
- 'default': {
82
- 'egress_qdisc': {
83
- 'kind': 'cake',
84
- 'handle': '1:',
85
- },
86
- 'ingress_qdisc': {
87
- 'kind': 'cake',
88
- 'handle': '1:',
89
- },
90
- 'ingress_ifname': {
91
- 'search': r'^\D{1,3}',
92
- 'replace': 'ifb',
20
+ 'parameters': {
21
+ 'cshaper': {
22
+ 'default': {
23
+ 'egress_qdisc': {
24
+ 'kind': 'cake',
25
+ 'handle': '1:',
26
+ },
27
+ 'ingress_qdisc': {
28
+ 'kind': 'cake',
29
+ 'handle': '1:',
30
+ },
31
+ 'ingress_ifname': {
32
+ 'search': r'^\D{1,3}',
33
+ 'replace': 'ifb',
34
+ }
35
+ }
36
+ },
37
+ 'defaults_builtin': [
38
+ {
39
+ 'match': [
40
+ {'ifname': ''}
41
+ ],
42
+ 'clear_addresses': True,
43
+ 'clear_fdb': True,
44
+ 'clear_neighbours': True,
45
+ 'clear_tc': True,
46
+ 'link': {
47
+ 'state': 'down',
48
+ 'master': None
49
+ }
93
50
  }
51
+ ],
52
+ 'ignore': {
53
+ 'ipaddr_builtin': [
54
+ 'fe80::/10'
55
+ ],
56
+ 'ipaddr_dynamic': True,
57
+ 'ifname_builtin': [
58
+ r'^br-[\da-f]{12}',
59
+ r'^docker\d+',
60
+ r'^ppp\d+$',
61
+ r'^veth',
62
+ r'^virbr\d+',
63
+ r'^vrrp\d*\.\d+$'
64
+ ],
65
+ 'fdb_builtin': [
66
+ r'^33:33:',
67
+ r'^01:00:5e:'
68
+ ],
69
+ 'routes_builtin': [
70
+ {'proto': 1},
71
+ {'proto': 2},
72
+ {'proto': 8},
73
+ {'proto': 9},
74
+ {'proto': 10},
75
+ {'proto': 11},
76
+ {'proto': 12},
77
+ {'proto': 13},
78
+ {'proto': 14},
79
+ {'proto': 15},
80
+ {'proto': 16},
81
+ {'proto': 18},
82
+ {'proto': 42},
83
+ {'proto': 186},
84
+ {'proto': 187},
85
+ {'proto': 188},
86
+ {'proto': 189},
87
+ {'proto': 192},
88
+ {'to': 'ff00::/8'},
89
+ ],
90
+ 'rules_builtin': [
91
+ {'proto': 1},
92
+ {'proto': 2},
93
+ {'proto': 8},
94
+ {'proto': 9},
95
+ {'proto': 10},
96
+ {'proto': 11},
97
+ {'proto': 12},
98
+ {'proto': 13},
99
+ {'proto': 14},
100
+ {'proto': 15},
101
+ {'proto': 16},
102
+ {'proto': 18},
103
+ {'proto': 42},
104
+ {'proto': 186},
105
+ {'proto': 187},
106
+ {'proto': 188},
107
+ {'proto': 189},
108
+ {'proto': 192},
109
+ ],
94
110
  }
95
111
  }
96
112
  }
@@ -114,11 +130,11 @@ class Parser(ABC):
114
130
 
115
131
  def _update_lo(self, cfg):
116
132
  if 'interfaces' in cfg:
117
- lo = next((idx for idx, iface in enumerate(cfg['interfaces']) if iface['name'] == 'lo'), None)
118
- if lo is not None:
119
- cfg['interfaces'][lo] = self.merge(deepcopy(Parser._default_lo_link), cfg['interfaces'][lo])
133
+ if 'lo' in cfg['interfaces']:
134
+ cfg['interfaces']['lo'] = self.merge(
135
+ deepcopy(Parser._default_lo_link), cfg['interfaces']['lo'])
120
136
  else:
121
- cfg['interfaces'].append(Parser._default_lo_link)
137
+ cfg['interfaces']['lo'] = Parser._default_lo_link
122
138
 
123
139
  def config(self):
124
140
  # merge builtin defaults with config
@@ -126,7 +142,7 @@ class Parser(ABC):
126
142
 
127
143
  # 'ignore' should still be an object
128
144
  try:
129
- iter(cfg["ignore"])
145
+ iter(cfg["parameters"]["ignore"])
130
146
  except TypeError:
131
147
  raise ParserValidationError("$.ignore: is not of type 'object'")
132
148
 
@@ -136,18 +152,25 @@ class Parser(ABC):
136
152
  self._update_lo(cfg['namespaces'][namespace])
137
153
 
138
154
  # merge builtin defaults
139
- for k in list(cfg["ignore"]):
155
+ if "defaults" in cfg["parameters"]:
156
+ cfg["parameters"]["defaults"].extend(cfg["parameters"]["defaults_builtin"])
157
+ else:
158
+ cfg["parameters"]["defaults"] = cfg["parameters"]["defaults_builtin"]
159
+ del cfg["parameters"]["defaults_builtin"]
160
+
161
+ # merge builtin ignore lists
162
+ for k in list(cfg["parameters"]["ignore"]):
140
163
  if k.endswith("_builtin"):
141
164
  n = k[:-8]
142
- if n in cfg["ignore"]:
165
+ if n in cfg["parameters"]["ignore"]:
143
166
  try:
144
- cfg["ignore"][n] += cfg["ignore"][k]
167
+ cfg["parameters"]["ignore"][n] += cfg["parameters"]["ignore"][k]
145
168
  except TypeError:
146
169
  raise ParserValidationError("$.ignore.{}: is not of type '{}'".format(
147
- n, type(cfg["ignore"][k]).__name__))
170
+ n, type(cfg["parameters"]["ignore"][k]).__name__))
148
171
  else:
149
- cfg["ignore"][n] = cfg["ignore"][k]
150
- del(cfg["ignore"][k])
172
+ cfg["parameters"]["ignore"][n] = cfg["parameters"]["ignore"][k]
173
+ del (cfg["parameters"]["ignore"][k])
151
174
 
152
175
  return cfg
153
176