ifstate 2.0.0rc3__py3-none-any.whl → 2.0.0rc4__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ifstate
3
- Version: 2.0.0rc3
3
+ Version: 2.0.0rc4
4
4
  Summary: Manage host interface settings in a declarative manner
5
5
  Home-page: https://ifstate.net/
6
6
  Author: Thomas Liske
@@ -3,12 +3,12 @@ ifstate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  ifstate/ifstate.py,sha256=ioiM9xgVWdrpTpK_UxJdaJFlmpFM9R-2_d5pq9_Hepo,9272
4
4
  ifstate/shell.py,sha256=7_JFpi4icr9MijynDzbb0v5mxhFsng6PCC4m3uQ255A,2177
5
5
  ifstate/vrrp.py,sha256=FJ9b1eJseTtZFfknHU-xV68Qz7cPrRrc5PTcjUVj-fY,5953
6
- ifstate-2.0.0rc3.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
7
- libifstate/__init__.py,sha256=iDV2VI11gPAIDrTXKqkWbo4B9bRcokiY3Fzyu440ayg,33194
8
- libifstate/exception.py,sha256=jhLN46mqlyGNEz7lmc0K2d3SoZJtKzEVyPduPh22oC0,2280
6
+ ifstate-2.0.0rc4.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
7
+ libifstate/__init__.py,sha256=twHhETYpEtBxqnWKOGYwOyOV1Ahgqi_ybAk2rodpre4,33845
8
+ libifstate/exception.py,sha256=NmKqD9O8dRWBgO__Pt0_wGSR5GWZ1RHXZZEUI3ArVLo,2360
9
9
  libifstate/log.py,sha256=XVoZdwdQoWsjuupFIuG6OP0OrBpXpx7oqyAaUhQ-nJk,4553
10
10
  libifstate/util.py,sha256=raYAaRy15JQNUbU3_4GchYV_PkaCvWR_8xjt7NTzMYw,11335
11
- libifstate/address/__init__.py,sha256=zIGM7UWXSvoijHMD06DfS2CDtiHpiJlqDgJG7Dl1IPE,3222
11
+ libifstate/address/__init__.py,sha256=SobrZ9e2ItrmEidnKPBhUMitRDdTEajbkU9Rkmtx-gQ,3165
12
12
  libifstate/bpf/__init__.py,sha256=NVzaunTmJU2PbIQg9eWBMKpFgLh3EnD3ujNa7Yt6rNc,7699
13
13
  libifstate/bpf/ctypes.py,sha256=kLZJHZlba09Vc-tbsJAcKpDwdoNO2IjlYVLCopawHmA,4274
14
14
  libifstate/bpf/map.py,sha256=cLHNMvRBDNW2yVCEf3z242_oRdU0HqVbFEYVkKXng0w,10818
@@ -16,23 +16,23 @@ libifstate/brport/__init__.py,sha256=NzdA8F4hr2se1bXKNnyKZbvOFlCWBq_cdjwsL1H0Y-o
16
16
  libifstate/fdb/__init__.py,sha256=9dpL5n8ct3CaA-z8I6ZEkD3yL6yWJQeq3fpIe9pc2zw,6486
17
17
  libifstate/hook/__init__.py,sha256=OPUyhrr-eT1UNp8ryYoJyvi4WWqL2Ql33szir673Td4,7379
18
18
  libifstate/link/__init__.py,sha256=QZggoC-bIscqwVedqVycaSqS1CmXB3Bx3m2FZei8Q_4,115
19
- libifstate/link/base.py,sha256=dazZVPRMyXms-ICiL7VOCUXwZnqNr1X6R2NjTlm2nEU,34384
20
- libifstate/link/physical.py,sha256=ectyXVchCHqWZR8qsjAD9667wjQbGW1OUKLQheTUFys,594
21
- libifstate/link/tun.py,sha256=SWkscSAgIk3DWFPWHo9Cmokhj88rO1_sR7Z0Qlg2xGA,993
19
+ libifstate/link/base.py,sha256=zsitL1kSPLvjzZqyUIyEoCVlE_dhX2z5acNL1iZ2EAE,34508
20
+ libifstate/link/physical.py,sha256=IG1OXG2q25QmvhxEDhyT-bf7guwIyXl0OzSJgQOqXzY,613
21
+ libifstate/link/tun.py,sha256=Rzn3ysE0cVuQGi3naxs9QXYBrLwVQTaVrFv2dtR1D60,1012
22
22
  libifstate/link/veth.py,sha256=Sv5WZMMsefYFz9I0BQSZyKytCQYxv0vjwAnB7FYHR1A,2283
23
23
  libifstate/neighbour/__init__.py,sha256=FJJpbJvqnxvOEii6QDMYzW5jQDEbiEy71GQOEbqaS48,2463
24
- libifstate/netns/__init__.py,sha256=ptSIVld_BNXPE6f4SiIPVdYOE1sgprf5-ZhR-L_3BPE,10145
25
- libifstate/parser/__init__.py,sha256=byz1W0G7UewVc5FFie-ti3UZjGK3-75wHIaOeq0oySQ,88
26
- libifstate/parser/base.py,sha256=EV7uJYdVke3a2ghkUZ6ZeaIeVQAPVS4rFnYhebXeQm0,5932
24
+ libifstate/netns/__init__.py,sha256=5Ak4vHU5UvdSYPFIQ8i8VMba2luyUeSpYAtRXxdTGPE,10287
25
+ libifstate/parser/__init__.py,sha256=uzv5U-6RPy-SSIyxR6H_F0SvAOQQF8x5ygErpUhsa7Y,109
26
+ libifstate/parser/base.py,sha256=PPUXWzZ1iMWseZzeV2uWmFIInmi8xxGDgul6afmVjd4,6584
27
27
  libifstate/parser/yaml.py,sha256=MC0kmwqt3P45z61fb_wfUqoj0iZyhFYkdPyr0UqMSZA,1415
28
28
  libifstate/routing/__init__.py,sha256=O4lbaCJvLgx-iQUa0WDgRmSPR4AKvveqPgxxb4HwYQ4,25305
29
- libifstate/sysctl/__init__.py,sha256=u1VJ1aPXeGCl3rwj3g0bDbz6k5k9YS5hQIsgFOBokpk,3202
30
- libifstate/tc/__init__.py,sha256=inPdampCOIr_4oKNB3awqMkW0Eh4fpPh9jvSba6sPVg,12092
29
+ libifstate/sysctl/__init__.py,sha256=VLzz1YcNEclRMVvwZuJgkYxPTjErl-7-rwSOKiEG6_M,3544
30
+ libifstate/tc/__init__.py,sha256=T8LEBxiHZH4sayPn9lK5hK8eB8dskuw6-kw81W_aMWU,12097
31
31
  libifstate/wireguard/__init__.py,sha256=dXILLIuahBCkX2ONhre28SzJrVNksbjOICs-cSGGaRg,8625
32
32
  libifstate/xdp/__init__.py,sha256=X1xhEIGng7R5d5F4KsChykT2g6H-XBRWbWABijoYDQA,7208
33
33
  schema/2/ifstate.conf.schema.json,sha256=ukCafLPfHCXpk44u5RMMhWIh9JoIAt2j_3idysAVHaQ,218763
34
- ifstate-2.0.0rc3.dist-info/METADATA,sha256=hdygdnfWvpggPIMQXwGSHPovg8Xt8VzsE-gAlPf1y-Y,1607
35
- ifstate-2.0.0rc3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
- ifstate-2.0.0rc3.dist-info/entry_points.txt,sha256=HF6jX7Uu_nF1Ly-J9uEPeiRapOxnM6LuHsb2y6Mt-k4,52
37
- ifstate-2.0.0rc3.dist-info/top_level.txt,sha256=A7peI7aKBaM69fsiSPvMbL3rzTKZZr5qDxKC-pHMGdE,19
38
- ifstate-2.0.0rc3.dist-info/RECORD,,
34
+ ifstate-2.0.0rc4.dist-info/METADATA,sha256=l7BY5YH_qpCDHZnyMRjS8sa9p4dZn-TyxaVPqCtBpbM,1607
35
+ ifstate-2.0.0rc4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
+ ifstate-2.0.0rc4.dist-info/entry_points.txt,sha256=HF6jX7Uu_nF1Ly-J9uEPeiRapOxnM6LuHsb2y6Mt-k4,52
37
+ ifstate-2.0.0rc4.dist-info/top_level.txt,sha256=A7peI7aKBaM69fsiSPvMbL3rzTKZZr5qDxKC-pHMGdE,19
38
+ ifstate-2.0.0rc4.dist-info/RECORD,,
libifstate/__init__.py CHANGED
@@ -5,7 +5,7 @@ from libifstate.fdb import FDB
5
5
  from libifstate.hook import Hooks
6
6
  from libifstate.neighbour import Neighbours
7
7
  from libifstate.routing import Tables, Rules, RTLookups
8
- from libifstate.parser import Parser
8
+ from libifstate.parser import Parser, get_available_hooks
9
9
  from libifstate.tc import TC
10
10
  from libifstate.exception import netlinkerror_classes
11
11
  import bisect
@@ -35,11 +35,16 @@ except ModuleNotFoundError:
35
35
  # ignore missing plugin
36
36
  pass
37
37
 
38
+ try:
39
+ from jsonschema import validate, ValidationError, FormatChecker
40
+ except ModuleNotFoundError:
41
+ # fail open and ignore missing jsonschema dependency
42
+ pass
43
+
38
44
  from libifstate.netns import NetNameSpace, prepare_netns, LinkRegistry, get_netns_instances
39
45
  from libifstate.util import logger, root_iw, IfStateLogging, LinkDependency, IDENTIFY_LOOKUPS
40
46
  from libifstate.exception import FeatureMissingError, LinkCircularLinked, LinkNoConfigFound, ParserValidationError
41
47
  from ipaddress import ip_network, ip_interface
42
- from jsonschema import validate, ValidationError, FormatChecker
43
48
  from copy import deepcopy
44
49
  import os
45
50
  import pkgutil
@@ -49,7 +54,7 @@ import json
49
54
  import errno
50
55
  import logging
51
56
 
52
- __version__ = "2.0.0rc3"
57
+ __version__ = "2.0.0rc4"
53
58
 
54
59
  class IfState():
55
60
  def __init__(self):
@@ -66,6 +71,7 @@ class IfState():
66
71
  'link': True,
67
72
  'sysctl': os.access('/proc/sys/net', os.R_OK),
68
73
  'ethtool': not ethtool_path is None,
74
+ 'schema': not globals().get("validate") is None,
69
75
  'tc': True,
70
76
  'wireguard': not globals().get("WireGuard") is None,
71
77
  'bpf': not globals().get("libbpf") is None,
@@ -81,28 +87,31 @@ class IfState():
81
87
  libbpf.libbpf_set_print(0)
82
88
 
83
89
  def update(self, ifstates, soft_schema):
84
- # check config schema
85
- schema = json.loads(pkgutil.get_data(
86
- "libifstate", "../schema/{}/ifstate.conf.schema.json".format(__version__.split('.')[0])))
87
- try:
88
- validate(ifstates, schema, format_checker=FormatChecker())
89
- except ValidationError as ex:
90
- if len(ex.path) > 0:
91
- path = ["$"]
92
- for i, p in enumerate(ex.absolute_path):
93
- if type(p) == int:
94
- path.append("[{}]".format(p))
95
- else:
96
- path.append(".")
97
- path.append(p)
90
+ if self.features['schema']:
91
+ # check config schema
92
+ schema = json.loads(pkgutil.get_data(
93
+ "libifstate", "../schema/{}/ifstate.conf.schema.json".format(__version__.split('.')[0])))
94
+ try:
95
+ validate(ifstates, schema, format_checker=FormatChecker())
96
+ except ValidationError as ex:
97
+ if len(ex.path) > 0:
98
+ path = ["$"]
99
+ for i, p in enumerate(ex.absolute_path):
100
+ if type(p) == int:
101
+ path.append("[{}]".format(p))
102
+ else:
103
+ path.append(".")
104
+ path.append(p)
98
105
 
99
- detail = "{}: {}".format("".join(path), ex.message)
100
- else:
101
- detail = ex.message
102
- if soft_schema:
103
- logger.error("Config validation failed for {}".format(detail))
104
- else:
105
- raise ParserValidationError(detail)
106
+ detail = "{}: {}".format("".join(path), ex.message)
107
+ else:
108
+ detail = ex.message
109
+ if soft_schema:
110
+ logger.error("Config validation failed for {}".format(detail))
111
+ else:
112
+ raise ParserValidationError(detail)
113
+ else:
114
+ logger.debug('Python package jsonschema not available, skipping config validation.')
106
115
 
107
116
  # add interface defaults
108
117
  if 'defaults' in ifstates:
@@ -349,8 +358,8 @@ class IfState():
349
358
  # ignore if the link is already gone, this might happen
350
359
  # when removing veth link peers
351
360
  if err.code != errno.ENODEV:
352
- logger.warning('removing link {} failed: {}'.format(
353
- ifname, err.args[1]), extra={'netns': item.netns})
361
+ logger.warning('removing failed: {}'.format(
362
+ err.args[1]), extra={'iface': ifname, 'netns': item.netns})
354
363
  return True
355
364
  else:
356
365
  # shutdown physical interfaces
@@ -364,8 +373,8 @@ class IfState():
364
373
  except Exception as err:
365
374
  if not isinstance(err, netlinkerror_classes):
366
375
  raise
367
- logger.warning('updating link {} failed: {}'.format(
368
- ifname, err.args[1]), extra={'netns': item.netns})
376
+ logger.warning('updating failed: {}'.format(
377
+ err.args[1]), extra={'iface': ifname, 'netns': item.netns})
369
378
  return False
370
379
 
371
380
  def _dependencies(self, netns):
@@ -518,9 +527,8 @@ class IfState():
518
527
  if link_dep.netns is None:
519
528
  self._apply_iface(do_apply, self.root_netns, link_dep, by_vrrp, vrrp_type, vrrp_name, vrrp_state)
520
529
  else:
521
- if link_dep.netns not in self.namespaces:
522
- logger.warning("add link {} failed: netns '{}' is unknown".format(link_dep.ifname, link_dep.netns))
523
- return
530
+ if self.namespaces is None or link_dep.netns not in self.namespaces:
531
+ logger.warning("apply link {} failed: netns '{}' is unknown".format(link_dep.ifname, link_dep.netns))
524
532
  else:
525
533
  self._apply_iface(do_apply, self.namespaces[link_dep.netns], link_dep, by_vrrp, vrrp_type, vrrp_name, vrrp_state)
526
534
 
@@ -559,8 +567,10 @@ class IfState():
559
567
 
560
568
  def _apply_iface(self, do_apply, netns, link_dep, by_vrrp, vrrp_type, vrrp_name, vrrp_state):
561
569
  ifname = link_dep.ifname
562
- if ifname in netns.links:
563
- link = netns.links[ifname]
570
+ if not ifname in netns.links:
571
+ logger.warning('no link settings found', extra={'iface': ifname, 'netns': netns})
572
+ return
573
+ link = netns.links[ifname]
564
574
 
565
575
  # check for vrrp mode:
566
576
  # disable: vrrp type & name matches, but vrrp state not
@@ -672,6 +682,10 @@ class IfState():
672
682
  def show(self, showall=False, show_secrets=False):
673
683
  if showall:
674
684
  defaults = deepcopy(Parser._default_ifstates)
685
+
686
+ hooks = get_available_hooks()
687
+ if hooks:
688
+ defaults['parameters']['hooks'] = hooks
675
689
  else:
676
690
  defaults = {}
677
691
 
@@ -18,8 +18,8 @@ class Addresses():
18
18
  # get ifindex
19
19
  idx = next(iter(self.netns.ipr.link_lookup(ifname=self.iface)), None)
20
20
 
21
+ # check if interface exists
21
22
  if idx == None:
22
- logger.warning('link missing', extra={'iface': self.iface, 'netns': self.netns})
23
23
  return
24
24
 
25
25
  # get active ip addresses
libifstate/exception.py CHANGED
@@ -4,8 +4,9 @@ from libifstate.util import logger
4
4
  netlinkerror_classes = (NetlinkError)
5
5
 
6
6
  class ExceptionCollector():
7
- def __init__(self, ifname):
7
+ def __init__(self, ifname, netns):
8
8
  self.ifname = ifname
9
+ self.netns = netns
9
10
  self.reset()
10
11
 
11
12
  def reset(self):
@@ -19,8 +20,9 @@ class ExceptionCollector():
19
20
  'args': kwargs,
20
21
  })
21
22
  if not self.quiet:
22
- logger.warning('{} link {} failed: {}'.format(
23
- op, self.ifname, excpt.args[1]))
23
+ logger.warning('{} failed: {}'.format(
24
+ op, excpt.args[1]),
25
+ extra={'iface': self.ifname, 'netns': self.netns})
24
26
 
25
27
  def has_op(self, op):
26
28
  for e in self.excpts:
libifstate/link/base.py CHANGED
@@ -436,7 +436,7 @@ class Link(ABC):
436
436
  return self.match_vrrp_select(vrrp_type, vrrp_name) and (vrrp_state in self.vrrp['states'])
437
437
 
438
438
  def apply(self, do_apply, sysctl):
439
- excpts = ExceptionCollector(self.settings['ifname'])
439
+ excpts = ExceptionCollector(self.settings['ifname'], self.netns)
440
440
  osettings = copy.deepcopy(self.settings)
441
441
 
442
442
  # lookup for attributes requiring a interface index
@@ -448,8 +448,11 @@ class Link(ABC):
448
448
  # ignore *_netns settings if the netns is the same as the interface's one
449
449
  # (there is no IFLA_LINK_NETNSID attribute in such cases)
450
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])
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
453
456
  self.settings[netnsid_attr] = peer_nsid
454
457
  idx = next(iter(peer_ipr.link_lookup(
455
458
  ifname=self.settings[attr])), None)
@@ -10,4 +10,4 @@ class PhysicalLink(Link):
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
@@ -18,6 +18,6 @@ class TunLink(Link):
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)
@@ -1,3 +1,4 @@
1
+ from libifstate.exception import NetnsUnknown
1
2
  from libifstate.util import logger, IfStateLogging, IPRouteExt, NetNSExt, root_ipr, root_iw, IDENTIFY_LOOKUPS
2
3
  from libifstate.sysctl import Sysctl
3
4
 
@@ -88,9 +89,11 @@ class NetNameSpace():
88
89
  if peer_netns_name is None:
89
90
  peer_ipr = root_ipr
90
91
  peer_pid = 1
91
- else:
92
+ elif peer_netns_name in netns_name_map:
92
93
  peer_ipr = netns_name_map[peer_netns_name]
93
94
  peer_pid = peer_ipr.child
95
+ else:
96
+ raise NetnsUnknown(peer_netns_name)
94
97
 
95
98
  result = self.ipr.get_netnsid(pid=peer_pid)
96
99
  if result['nsid'] == 4294967295:
@@ -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
libifstate/parser/base.py CHANGED
@@ -1,7 +1,22 @@
1
+ from libifstate.hook import HOOK_DIR
1
2
  from libifstate.util import logger
2
3
  from libifstate.exception import ParserValidationError
3
4
  from abc import ABC, abstractmethod
4
5
  from copy import deepcopy
6
+ import os
7
+
8
+
9
+ def get_available_hooks():
10
+ # check if there is a hooks dir before trying to scan it
11
+ if not os.path.isdir(HOOK_DIR):
12
+ return {}
13
+
14
+ hooks = {}
15
+ with os.scandir(HOOK_DIR) as it:
16
+ for dentry in it:
17
+ if dentry.is_file():
18
+ hooks[dentry.name] = {}
19
+ return hooks
5
20
 
6
21
 
7
22
  class Parser(ABC):
@@ -151,6 +166,14 @@ class Parser(ABC):
151
166
  for namespace in cfg.get('namespaces', {}):
152
167
  self._update_lo(cfg['namespaces'][namespace])
153
168
 
169
+ # add available hooks
170
+ if "hooks" in cfg["parameters"]:
171
+ hooks = get_available_hooks()
172
+ hooks.update(cfg["parameters"]["hooks"])
173
+ cfg["parameters"]["hooks"] = hooks
174
+ else:
175
+ cfg["parameters"]["hooks"] = get_available_hooks()
176
+
154
177
  # merge builtin defaults
155
178
  if "defaults" in cfg["parameters"]:
156
179
  cfg["parameters"]["defaults"].extend(cfg["parameters"]["defaults_builtin"])
@@ -3,6 +3,14 @@ import pyroute2.netns
3
3
 
4
4
  import os
5
5
 
6
+ def deep_update(d, u):
7
+ """update the nested dict `d` with values from `u`"""
8
+ for k, v in u.items():
9
+ if isinstance(v, dict):
10
+ d[k] = deep_update(d.get(k, {}), v)
11
+ else:
12
+ d[k] = v
13
+ return d
6
14
 
7
15
  class Sysctl():
8
16
  def __init__(self, netns):
@@ -17,7 +25,8 @@ class Sysctl():
17
25
  self.netns = netns
18
26
 
19
27
  def add(self, iface, sysctl):
20
- self.sysctls[iface] = sysctl
28
+ # deeply apply changes to the default/previously defined values
29
+ self.sysctls[iface] = deep_update(self.sysctls[iface], sysctl)
21
30
 
22
31
  def add_global(self, proto, sysctl):
23
32
  self.globals[proto] = sysctl
libifstate/tc/__init__.py CHANGED
@@ -254,7 +254,7 @@ class TC():
254
254
  return changes
255
255
 
256
256
  def apply(self, do_apply):
257
- excpts = ExceptionCollector(ifname=self.iface)
257
+ excpts = ExceptionCollector(self.iface, self.netns)
258
258
 
259
259
  # get ifindex
260
260
  self.idx = next(iter(self.netns.ipr.link_lookup(ifname=self.iface)), None)