ifstate 1.13.6__py3-none-any.whl → 1.13.8__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 CHANGED
@@ -128,7 +128,7 @@ def main():
128
128
  group = parser.add_mutually_exclusive_group()
129
129
  parser.add_argument('--version', action='version',
130
130
  version='%(prog)s {version}'.format(version=__version__))
131
- group.add_argument("-v", "--verbose", action="store_true",
131
+ group.add_argument("-v", "--verbose", action="count", default=0,
132
132
  help="be more verbose")
133
133
  group.add_argument("-q", "--quiet", action="store_true",
134
134
  help="be more quiet, print only warnings and errors")
@@ -162,10 +162,10 @@ def main():
162
162
  "name", type=str, help="name of the vrrp group or instance")
163
163
 
164
164
  args = parser.parse_args()
165
- if args.verbose:
165
+ if args.verbose > 0:
166
166
  lvl = logging.DEBUG
167
167
  elif args.quiet:
168
- lvl = logging.ERROR
168
+ lvl = logging.WARNING
169
169
  else:
170
170
  lvl = logging.INFO
171
171
 
@@ -178,7 +178,7 @@ def main():
178
178
  shell()
179
179
  exit(0)
180
180
 
181
- ifslog = IfStateLogging(lvl, action=args.action)
181
+ ifslog = IfStateLogging(lvl, args.verbose > 1, action=args.action)
182
182
 
183
183
  if args.action in [Actions.SHOW, Actions.SHOWALL]:
184
184
  # preserve dict order on python 3.7+
ifstate/vrrp.py CHANGED
@@ -139,7 +139,7 @@ def vrrp_fifo(args_fifo, ifs_config):
139
139
 
140
140
  try:
141
141
  status_pattern = re.compile(
142
- r'(group|instance) "([^"]+)" (' + '|'.join(KEEPALIVED_STATES) + ')( \d+)?$', re.IGNORECASE)
142
+ r'(group|instance) "([^"]+)" (' + '|'.join(KEEPALIVED_STATES) + r')( \d+)?$', re.IGNORECASE)
143
143
 
144
144
  with open(args_fifo) as fifo:
145
145
  logger.debug("entering fifo loop...")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ifstate
3
- Version: 1.13.6
3
+ Version: 1.13.8
4
4
  Summary: Manage host interface settings in a declarative manner
5
5
  Home-page: https://ifstate.net/
6
6
  Author: Thomas Liske
@@ -1,37 +1,37 @@
1
1
  ifstate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- ifstate/ifstate.py,sha256=l6VxiAXWyoqj7T5J495gM5Nhn807f5pHmL8_kh1uJAc,8891
2
+ ifstate/ifstate.py,sha256=am8x-ONL1QziOuTfD3Fmr5wVg_Mt2sH9Pi9DTn6YqYY,8921
3
3
  ifstate/shell.py,sha256=7_JFpi4icr9MijynDzbb0v5mxhFsng6PCC4m3uQ255A,2177
4
- ifstate/vrrp.py,sha256=cXzQZ2v-QS56ZBS8raLM4GyCjPFtTV6tg_9Drkgul1k,5952
5
- ifstate-1.13.6.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
6
- libifstate/__init__.py,sha256=SgY_-99Mn-OJzZsU1B1KEhfQI4EKa7dk0QxMLsGCKTc,30950
7
- libifstate/exception.py,sha256=5i59BZdl56J_sNJbyU9n6uHuUNJEyDOO4FJ-neDn9Ds,2608
8
- libifstate/log.py,sha256=XVoZdwdQoWsjuupFIuG6OP0OrBpXpx7oqyAaUhQ-nJk,4553
4
+ ifstate/vrrp.py,sha256=FJ9b1eJseTtZFfknHU-xV68Qz7cPrRrc5PTcjUVj-fY,5953
5
+ ifstate-1.13.8.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
6
+ libifstate/__init__.py,sha256=9ovYpLf4AVTDNWFru4o4DaR60SQW0tXCrNMXLuoTPpU,31245
7
+ libifstate/exception.py,sha256=QgG4hdP1KYbdVBNfy-bT59aCcYF0IuFdEwZrY3QrwTc,2688
8
+ libifstate/log.py,sha256=rG0ISWyeGGlC_jRREoSpkdySc5EmyhOQIgSfvRqlaZA,4651
9
9
  libifstate/util.py,sha256=WzbGKX78iWUunnZ-v_3OREmxMiYt7vVM_QyCgTcMYow,10802
10
- libifstate/address/__init__.py,sha256=zIGM7UWXSvoijHMD06DfS2CDtiHpiJlqDgJG7Dl1IPE,3222
10
+ libifstate/address/__init__.py,sha256=SobrZ9e2ItrmEidnKPBhUMitRDdTEajbkU9Rkmtx-gQ,3165
11
11
  libifstate/bpf/__init__.py,sha256=NVzaunTmJU2PbIQg9eWBMKpFgLh3EnD3ujNa7Yt6rNc,7699
12
12
  libifstate/bpf/ctypes.py,sha256=kLZJHZlba09Vc-tbsJAcKpDwdoNO2IjlYVLCopawHmA,4274
13
13
  libifstate/bpf/map.py,sha256=cLHNMvRBDNW2yVCEf3z242_oRdU0HqVbFEYVkKXng0w,10818
14
14
  libifstate/brport/__init__.py,sha256=NzdA8F4hr2se1bXKNnyKZbvOFlCWBq_cdjwsL1H0Y-o,2964
15
15
  libifstate/fdb/__init__.py,sha256=9dpL5n8ct3CaA-z8I6ZEkD3yL6yWJQeq3fpIe9pc2zw,6486
16
16
  libifstate/link/__init__.py,sha256=epVw6jY8exNeJZUmmUas91yJoeupfgIY7rthq7SGIIw,142
17
- libifstate/link/base.py,sha256=Si31tqRTRGnHusn1hRPLrfdEmeQqRyNP4aua1DlGc_Q,34319
17
+ libifstate/link/base.py,sha256=PLHJ594UQboe98jEVZVQvaD1Ka_Ah6EySDfcJtYTVH0,34863
18
18
  libifstate/link/dsa.py,sha256=Y3axTtcym6YL1voKblxctx4PoKDZHzpteKQNnEBUrS8,264
19
- libifstate/link/physical.py,sha256=cJiq-MCfy-3XQoU-OxzgfPZZtu_pJ8u2ioJgn9VYdGk,560
20
- libifstate/link/tun.py,sha256=m55o5cwO3h3DCLofUR-68fM4ggLoTKElp6ZJ2LrJSCc,959
19
+ libifstate/link/physical.py,sha256=rmf8ltjDy1s2NEfaQtOwKBTpESlzqjZd53yfcZCfSPo,579
20
+ libifstate/link/tun.py,sha256=YEW09fYNLvmb0IU4Jb1uNh0iCt0bgIii2pPvOYtgw6U,978
21
21
  libifstate/link/veth.py,sha256=V_jmQCizI5Vv8-pcsfldSfMRTn1knR11wZDZI_yXvks,2249
22
22
  libifstate/neighbour/__init__.py,sha256=FJJpbJvqnxvOEii6QDMYzW5jQDEbiEy71GQOEbqaS48,2463
23
- libifstate/netns/__init__.py,sha256=oqUqU22C6RVMDwlgLccntQ3icenf9hiDs2FIWJIgny0,8772
23
+ libifstate/netns/__init__.py,sha256=-xJ5Qa-x2_MveGHWWR2pv--EBmQzLSZUYeC8vebnLUE,9029
24
24
  libifstate/parser/__init__.py,sha256=byz1W0G7UewVc5FFie-ti3UZjGK3-75wHIaOeq0oySQ,88
25
25
  libifstate/parser/base.py,sha256=VFAo05O3tiKtI381LVUMYfsDTseMKoQGTfkgnEkm3H4,4770
26
26
  libifstate/parser/yaml.py,sha256=MC0kmwqt3P45z61fb_wfUqoj0iZyhFYkdPyr0UqMSZA,1415
27
- libifstate/routing/__init__.py,sha256=Hmj-LHBEtVXs5tlRzY8JS_C5qdnN3alD3cIznmzQ-rY,20469
27
+ libifstate/routing/__init__.py,sha256=5kcIzWpM2v-T_ndxFO5qwF8hla-Fqh2yP1QtmohWZl0,22777
28
28
  libifstate/sysctl/__init__.py,sha256=EF52CdOOkVSUFR2t21A99KlG1-PjsD4qOiceQC4eI24,3074
29
- libifstate/tc/__init__.py,sha256=inPdampCOIr_4oKNB3awqMkW0Eh4fpPh9jvSba6sPVg,12092
30
- libifstate/wireguard/__init__.py,sha256=HEmGsrtIX8MEjxtMbqgzP-e2BIUicyfmcywnRE93lRQ,6579
29
+ libifstate/tc/__init__.py,sha256=T8LEBxiHZH4sayPn9lK5hK8eB8dskuw6-kw81W_aMWU,12097
30
+ libifstate/wireguard/__init__.py,sha256=RPJdMdp7Uv3Kk_koGH8bu0lVQaKlJQwkyobGFqtmp0E,6839
31
31
  libifstate/xdp/__init__.py,sha256=X1xhEIGng7R5d5F4KsChykT2g6H-XBRWbWABijoYDQA,7208
32
32
  schema/ifstate.conf.schema.json,sha256=NOPeI8_r1jXvgAAJeBqz92ZACNvxskId3qMykj-sG7M,201292
33
- ifstate-1.13.6.dist-info/METADATA,sha256=uB4WuIOTixV34kGTfJVlhRSQv0j5bRm6lz4J04-aT0E,1598
34
- ifstate-1.13.6.dist-info/WHEEL,sha256=wXxTzcEDnjrTwFYjLPcsW_7_XihufBwmpiBeiXNBGEA,91
35
- ifstate-1.13.6.dist-info/entry_points.txt,sha256=HF6jX7Uu_nF1Ly-J9uEPeiRapOxnM6LuHsb2y6Mt-k4,52
36
- ifstate-1.13.6.dist-info/top_level.txt,sha256=A7peI7aKBaM69fsiSPvMbL3rzTKZZr5qDxKC-pHMGdE,19
37
- ifstate-1.13.6.dist-info/RECORD,,
33
+ ifstate-1.13.8.dist-info/METADATA,sha256=dNYDgkvVdmbpS-A7CdZnwhmz-RXww8btSS8YunJ_WUg,1598
34
+ ifstate-1.13.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
35
+ ifstate-1.13.8.dist-info/entry_points.txt,sha256=HF6jX7Uu_nF1Ly-J9uEPeiRapOxnM6LuHsb2y6Mt-k4,52
36
+ ifstate-1.13.8.dist-info/top_level.txt,sha256=A7peI7aKBaM69fsiSPvMbL3rzTKZZr5qDxKC-pHMGdE,19
37
+ ifstate-1.13.8.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.1.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
libifstate/__init__.py CHANGED
@@ -48,7 +48,7 @@ import json
48
48
  import errno
49
49
  import logging
50
50
 
51
- __version__ = "1.13.6"
51
+ __version__ = "1.13.8"
52
52
 
53
53
 
54
54
  class IfState():
@@ -347,8 +347,8 @@ class IfState():
347
347
  # ignore if the link is already gone, this might happen
348
348
  # when removing veth link peers
349
349
  if err.code != errno.ENODEV:
350
- logger.warning('removing link {} failed: {}'.format(
351
- ifname, err.args[1]), extra={'netns': item.netns})
350
+ logger.warning('removing failed: {}'.format(
351
+ err.args[1]), extra={'iface': ifname, 'netns': item.netns})
352
352
  return True
353
353
  else:
354
354
  # shutdown physical interfaces
@@ -362,8 +362,8 @@ class IfState():
362
362
  except Exception as err:
363
363
  if not isinstance(err, netlinkerror_classes):
364
364
  raise
365
- logger.warning('updating link {} failed: {}'.format(
366
- ifname, err.args[1]), extra={'netns': item.netns})
365
+ logger.warning('updating failed: {}'.format(
366
+ err.args[1]), extra={'iface': ifname, 'netns': item.netns})
367
367
  return False
368
368
 
369
369
  def _dependencies(self, netns):
@@ -455,7 +455,7 @@ class IfState():
455
455
 
456
456
  # create and destroy namespaces to match config
457
457
  if not by_vrrp and self.namespaces is not None:
458
- prepare_netns(do_apply, self.namespaces.keys(), self.new_namespaces)
458
+ prepare_netns(do_apply, self.namespaces.keys(), self.new_namespaces, self.ignore.get('netns', []))
459
459
  logger.info("")
460
460
 
461
461
  # get link dependency tree
@@ -467,8 +467,10 @@ class IfState():
467
467
  cleanup_items = []
468
468
  for item in self.link_registry.registry:
469
469
  ifname = item.attributes['ifname']
470
- # items without a link are orphan - keep them if they match the ignore regex list...
471
- if item.link is None and not any(re.match(regex, ifname) for regex in self.ignore.get('ifname', [])):
470
+ # items without a link are orphan - keep them if they match the ignore (ifname|netns) regex list...
471
+ if item.link is None and \
472
+ not any(re.match(regex, ifname) for regex in self.ignore.get('ifname', [])) and \
473
+ not any(re.match(regex, item.netns.netns or '') for regex in self.ignore.get('netns', [])):
472
474
  # ...or are in a netns namespace while the config has no `namespaces` setting
473
475
  if self.namespaces is not None or item.netns.netns is None:
474
476
  if not had_cleanup:
@@ -514,9 +516,8 @@ class IfState():
514
516
  if link_dep.netns is None:
515
517
  self._apply_iface(do_apply, self.root_netns, link_dep, by_vrrp, vrrp_type, vrrp_name, vrrp_state)
516
518
  else:
517
- if link_dep.netns not in self.namespaces:
518
- logger.warning("add link {} failed: netns '{}' is unknown".format(link_dep.ifname, link_dep.netns))
519
- return
519
+ if self.namespaces is None or link_dep.netns not in self.namespaces:
520
+ logger.warning("apply link {} failed: netns '{}' is unknown".format(link_dep.ifname, link_dep.netns))
520
521
  else:
521
522
  self._apply_iface(do_apply, self.namespaces[link_dep.netns], link_dep, by_vrrp, vrrp_type, vrrp_name, vrrp_state)
522
523
 
@@ -555,8 +556,10 @@ class IfState():
555
556
 
556
557
  def _apply_iface(self, do_apply, netns, link_dep, by_vrrp, vrrp_type, vrrp_name, vrrp_state):
557
558
  ifname = link_dep.ifname
558
- if ifname in netns.links:
559
- link = netns.links[ifname]
559
+ if not ifname in netns.links:
560
+ logger.warning('no link settings found', extra={'iface': ifname, 'netns': netns})
561
+ return
562
+ link = netns.links[ifname]
560
563
 
561
564
  # check for vrrp mode:
562
565
  # disable: vrrp type & name matches, but vrrp state not
@@ -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
@@ -12,8 +12,9 @@ except ModuleNotFoundError:
12
12
  pass
13
13
 
14
14
  class ExceptionCollector():
15
- def __init__(self, ifname):
15
+ def __init__(self, ifname, netns):
16
16
  self.ifname = ifname
17
+ self.netns = netns
17
18
  self.reset()
18
19
 
19
20
  def reset(self):
@@ -27,8 +28,9 @@ class ExceptionCollector():
27
28
  'args': kwargs,
28
29
  })
29
30
  if not self.quiet:
30
- logger.warning('{} link {} failed: {}'.format(
31
- op, self.ifname, excpt.args[1]))
31
+ logger.warning('{} failed: {}'.format(
32
+ op, excpt.args[1]),
33
+ extra={'iface': self.ifname, 'netns': self.netns})
32
34
 
33
35
  def has_op(self, op):
34
36
  for e in self.excpts:
libifstate/link/base.py CHANGED
@@ -443,7 +443,7 @@ class Link(ABC):
443
443
  return self.match_vrrp_select(vrrp_type, vrrp_name) and (vrrp_state in self.vrrp['states'])
444
444
 
445
445
  def apply(self, do_apply, sysctl):
446
- excpts = ExceptionCollector(self.settings['ifname'])
446
+ excpts = ExceptionCollector(self.settings['ifname'], self.netns)
447
447
  osettings = copy.deepcopy(self.settings)
448
448
 
449
449
  # lookup for attributes requiring a interface index
@@ -452,11 +452,20 @@ class Link(ABC):
452
452
  netns_attr = "{}_netns".format(attr)
453
453
  netnsid_attr = "{}_netnsid".format(attr)
454
454
  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)
455
+ # ignore *_netns settings if the netns is the same as the interface's one
456
+ # (there is no IFLA_LINK_NETNSID attribute in such cases)
457
+ if self.settings[netns_attr] != self.netns.netns:
458
+ try:
459
+ (peer_ipr, peer_nsid) = self.netns.get_netnsid(self.settings[netns_attr])
460
+ except NetnsUnknown as ex:
461
+ excpts.add('apply', ex)
462
+ return excpts
463
+ self.settings[netnsid_attr] = peer_nsid
464
+ idx = next(iter(peer_ipr.link_lookup(
465
+ ifname=self.settings[attr])), None)
466
+ else:
467
+ idx = next(iter(self.netns.ipr.link_lookup(
468
+ ifname=self.settings[attr])), None)
460
469
 
461
470
  del(self.settings[netns_attr])
462
471
  else:
@@ -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)
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,3 +1,4 @@
1
+ from libifstate.exception import NetnsUnknown
1
2
  from libifstate.util import logger, IfStateLogging, IPRouteExt, NetNSExt, root_ipr
2
3
  from libifstate.sysctl import Sysctl
3
4
 
@@ -68,9 +69,11 @@ class NetNameSpace():
68
69
  if peer_netns_name is None:
69
70
  peer_ipr = root_ipr
70
71
  peer_pid = 1
71
- else:
72
+ elif peer_netns_name in netns_name_map:
72
73
  peer_ipr = netns_name_map[peer_netns_name]
73
74
  peer_pid = peer_ipr.child
75
+ else:
76
+ raise NetnsUnknown(peer_netns_name)
74
77
 
75
78
  result = self.ipr.get_netnsid(pid=peer_pid)
76
79
  if result['nsid'] == 4294967295:
@@ -81,7 +84,7 @@ class NetNameSpace():
81
84
 
82
85
  return (peer_ipr, peer_nsid)
83
86
 
84
- def prepare_netns(do_apply, target_netns_list, new_netns_list):
87
+ def prepare_netns(do_apply, target_netns_list, new_netns_list, ignore_netns):
85
88
  logger.info("configure network namespaces...")
86
89
 
87
90
  # get mapping of netns names to lists of pids
@@ -94,14 +97,15 @@ def prepare_netns(do_apply, target_netns_list, new_netns_list):
94
97
  for name in sorted(names_set):
95
98
  # cleanup orphan netns
96
99
  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)
102
-
103
- if do_apply:
104
- pyroute2.netns.remove(name)
100
+ if not any(re.match(regex, name) for regex in ignore_netns):
101
+ if name in ns_pids:
102
+ logger.warning(
103
+ 'pids: {}'.format(', '.join((str(x) for x in ns_pids[name]))),
104
+ extra={'iface': name})
105
+ logger.log_del(name)
106
+
107
+ if do_apply:
108
+ pyroute2.netns.remove(name)
105
109
 
106
110
  # create missing netns
107
111
  elif name not in current_netns_list or name in new_netns_list:
@@ -1,6 +1,6 @@
1
1
  from libifstate.util import logger, IfStateLogging
2
2
  from libifstate.exception import RouteDuplicate, netlinkerror_classes
3
- from ipaddress import ip_address, ip_network, IPv6Network
3
+ from ipaddress import ip_address, ip_interface, ip_network, IPv6Network
4
4
  from pyroute2.netlink.rtnl.fibmsg import FR_ACT_VALUES
5
5
  from pyroute2.netlink.rtnl import rt_type
6
6
  import collections.abc
@@ -15,6 +15,147 @@ from socket import AF_INET, AF_INET6
15
15
  def route_matches(r1, r2, fields=('dst', 'priority', 'proto'), indent=None):
16
16
  return _matches(r1, r2, fields, indent)
17
17
 
18
+ def parse_route(route, implicit_defaults=True):
19
+ rt = {}
20
+
21
+ if implicit_defaults:
22
+ rt['type'] = route.get('type', 'unicast')
23
+
24
+ if 'to' in route:
25
+ dst = ip_network(route['to'])
26
+ rt['dst'] = dst.with_prefixlen
27
+ has_to = True
28
+ else:
29
+ has_to = False
30
+
31
+ for key, lookup in RT_LOOKUPS_DICT.items():
32
+ try:
33
+ if implicit_defaults:
34
+ rt[key] = route.get(key, RT_LOOKUPS_DEFAULTS[key])
35
+ rt[key] = lookup.lookup_id(rt[key])
36
+ else:
37
+ if key in route:
38
+ rt[key] = route[key]
39
+ rt[key] = lookup.lookup_id(rt[key])
40
+ except KeyError as err:
41
+ # mapping not available - catch exception and skip it
42
+ logger.warning('ignoring unknown %s "%s"', key, rt[key],
43
+ extra={'iface': rt['dst']})
44
+ rt[key] = RT_LOOKUPS_DEFAULTS[key]
45
+
46
+ if 'type' in rt and type(rt['type']) == str:
47
+ rt['type'] = rt_type[rt['type']]
48
+
49
+ if 'dev' in route:
50
+ rt['oif'] = route['dev']
51
+
52
+ if 'via' in route:
53
+ via = ip_address(route['via'])
54
+
55
+ if not has_to or via.version == dst.version:
56
+ rt['gateway'] = str(via)
57
+ else:
58
+ if via.version == 4:
59
+ rt['via'] = {
60
+ 'family': int(AF_INET),
61
+ 'addr': str(via),
62
+ }
63
+ else:
64
+ rt['via'] = {
65
+ 'family': int(AF_INET6),
66
+ 'addr': str(via),
67
+ }
68
+
69
+ if 'src' in route:
70
+ rt['prefsrc'] = route['src']
71
+
72
+ if 'preference' in route:
73
+ rt['priority'] = route['preference']
74
+ elif has_to and implicit_defaults:
75
+ if isinstance(dst, IPv6Network):
76
+ rt['priority'] = 1024
77
+ else:
78
+ rt['priority'] = 0
79
+
80
+ if 'vrrp' in route:
81
+ rt['_vrrp'] = route['vrrp']
82
+
83
+ return rt
84
+
85
+ def parse_routes(routes, implicit_defaults=True):
86
+ return [parse_route(route, implicit_defaults) for route in routes]
87
+
88
+ def parse_rule(rule, implicit_defaults=True):
89
+ ru = {}
90
+
91
+ if implicit_defaults:
92
+ ru['table'] = RTLookups.tables.lookup_id(rule.get('table', 254))
93
+ ru['protocol'] = RTLookups.protos.lookup_id(rule.get('proto', 0))
94
+ ru['tos'] = rule.get('tos', 0)
95
+ else:
96
+ if 'table' in rule:
97
+ ru['table'] = RTLookups.tables.lookup_id(rule['table'])
98
+ if 'proto' in rule:
99
+ ru['protocol'] = RTLookups.protos.lookup_id(rule['proto'])
100
+ if 'tos' in rule:
101
+ ru['tos'] = rule['tos']
102
+
103
+ if 'action' in rule and type(rule['action']) == str:
104
+ ru['action'] = {
105
+ "to_tbl": "FR_ACT_TO_TBL",
106
+ "unicast": "FR_ACT_UNICAST",
107
+ "blackhole": "FR_ACT_BLACKHOLE",
108
+ "unreachable": "FR_ACT_UNREACHABLE",
109
+ "prohibit": "FR_ACT_PROHIBIT",
110
+ "nat": "FR_ACT_NAT",
111
+ }.get(rule['action'], rule['action'])
112
+ elif implicit_defaults:
113
+ ru['action'] = "FR_ACT_TO_TBL"
114
+
115
+ if 'fwmark' in rule:
116
+ ru['fwmark'] = rule['fwmark']
117
+
118
+ if 'ipproto' in rule:
119
+ if type(rule['ipproto']) == str:
120
+ ru['ip_proto'] = socket.getprotobyname(rule['ipproto'])
121
+ else:
122
+ ru['ip_proto'] = rule['ipproto']
123
+
124
+ if 'to' in rule:
125
+ ru['dst'] = str(ip_network(rule['to']).network_address)
126
+ ru['dst_len'] = ip_network(rule['to']).prefixlen
127
+
128
+ if 'from' in rule:
129
+ if rule['from'] != "all":
130
+ ru['src'] = str(ip_interface(rule['from']).ip)
131
+ ru['src_len'] = ip_network(ip_interface(rule['from']).network).prefixlen
132
+ else:
133
+ ru['src'] = "0.0.0.0"
134
+ ru['src_len'] = 0
135
+
136
+ if 'iif' in rule:
137
+ ru['iifname'] = rule['iif']
138
+
139
+ if 'oif' in rule:
140
+ ru['oifname'] = rule['oif']
141
+
142
+ if 'priority' in rule:
143
+ ru['priority'] = rule['priority']
144
+
145
+ if 'vrrp' in rule:
146
+ ru['_vrrp'] = rule['vrrp']
147
+
148
+ if 'family' in rule:
149
+ assert rule['family'] in ("AF_INET", "inet", 2, "AF_INET6", "inet6", 10), "rule['family'] doesn't have a valid value. This should have already been rejected by the schema validation."
150
+ if rule['family'] in ("AF_INET", "inet", 2):
151
+ ru['family'] = AF_INET
152
+ elif rule['family'] in ("AF_INET6", "inet6", 10):
153
+ ru['family'] = AF_INET6
154
+
155
+ return ru
156
+
157
+ def parse_rules(rules, implicit_defaults=True):
158
+ return [parse_rule(rule, implicit_defaults) for rule in rules]
18
159
 
19
160
  def rule_matches(r1, r2, fields=('priority', 'iif', 'oif', 'dst', 'metric', 'protocol'), indent=None):
20
161
  return _matches(r1, r2, fields, indent)
@@ -133,64 +274,36 @@ class Tables(collections.abc.Mapping):
133
274
  def __len__(self):
134
275
  return len(self.tables)
135
276
 
136
- def add(self, route):
137
- dst = ip_network(route['to'])
138
- rt = {
139
- 'type': route.get('type', 'unicast'),
140
- 'dst': dst.with_prefixlen,
141
- }
142
-
143
- for key, lookup in RT_LOOKUPS_DICT.items():
144
- try:
145
- rt[key] = route.get(key, RT_LOOKUPS_DEFAULTS[key])
146
- rt[key] = lookup.lookup_id(rt[key])
147
- except KeyError as err:
148
- # mapping not available - catch exception and skip it
149
- logger.warning('ignoring unknown %s "%s"', key, rt[key],
150
- extra={'iface': rt['dst'], 'netns': self.netns})
151
- rt[key] = RT_LOOKUPS_DEFAULTS[key]
152
-
153
- if type(rt['type']) == str:
154
- rt['type'] = rt_type[rt['type']]
155
-
156
- if 'dev' in route:
157
- rt['oif'] = route['dev']
158
-
159
- if 'via' in route:
160
- via = ip_address(route['via'])
161
-
162
- if via.version == dst.version:
163
- rt['gateway'] = str(via)
164
- else:
165
- if via.version == 4:
166
- rt['via'] = {
167
- 'family': int(AF_INET),
168
- 'addr': str(via),
169
- }
170
- else:
171
- rt['via'] = {
172
- 'family': int(AF_INET6),
173
- 'addr': str(via),
174
- }
175
-
176
- if 'src' in route:
177
- rt['prefsrc'] = route['src']
277
+ def parse_ignores(self, ignores):
278
+ parsed = []
279
+ for ignore in ignores:
280
+ ignore = parse_route(ignore, False)
281
+
282
+ for attr in ['dev', 'oif', 'iif']:
283
+ if attr in ignore and type(ignore[attr]) == str:
284
+ idx = next(
285
+ iter(self.netns.ipr.link_lookup(ifname=ignore[attr])), None)
286
+ if idx is None:
287
+ logger.debug("{} '{}' is unknown, skipping ignore".format(attr, ignore[attr]))
288
+ continue
289
+ else:
290
+ ignore[attr] = idx
291
+ parsed.append(ignore)
178
292
 
179
- if 'preference' in route:
180
- rt['priority'] = route['preference']
181
- elif isinstance(dst, IPv6Network):
182
- rt['priority'] = 1024
183
- else:
184
- rt['priority'] = 0
293
+ return parsed
185
294
 
186
- if 'vrrp' in route:
187
- rt['_vrrp'] = route['vrrp']
295
+ def add(self, route):
296
+ # convert config dict into IFLA keys and values
297
+ rt = parse_route(route)
188
298
 
299
+ # track route in the corresponding table
189
300
  if not rt['table'] in self.tables:
190
301
  self.tables[rt['table']] = []
191
302
  self.tables[rt['table']].append(rt)
192
303
 
193
304
  def show_routes(self, ignores):
305
+ ignores = self.parse_ignores(ignores)
306
+
194
307
  routes = []
195
308
  for route in self.netns.ipr.get_routes(family=AF_INET) + self.netns.ipr.get_routes(family=AF_INET6):
196
309
  # skip routes from local table
@@ -322,6 +435,8 @@ class Tables(collections.abc.Mapping):
322
435
  return routes
323
436
 
324
437
  def apply(self, ignores, do_apply, by_vrrp, vrrp_type, vrrp_name, vrrp_state):
438
+ ignores = self.parse_ignores(ignores)
439
+
325
440
  for table, croutes in self.tables.items():
326
441
  log_str = RTLookups.tables.lookup_str(table)
327
442
  if self.netns.netns != None:
@@ -413,53 +528,10 @@ class Rules():
413
528
  self.rules = []
414
529
 
415
530
  def add(self, rule):
416
- ru = {
417
- 'table': RTLookups.tables.lookup_id(rule.get('table', 254)),
418
- 'protocol': RTLookups.protos.lookup_id(rule.get('proto', 0)),
419
- 'tos': rule.get('tos', 0),
420
- }
421
-
422
- if 'action' in rule and type(rule['action']) == str:
423
- ru['action'] = {
424
- "to_tbl": "FR_ACT_TO_TBL",
425
- "unicast": "FR_ACT_UNICAST",
426
- "blackhole": "FR_ACT_BLACKHOLE",
427
- "unreachable": "FR_ACT_UNREACHABLE",
428
- "prohibit": "FR_ACT_PROHIBIT",
429
- "nat": "FR_ACT_NAT",
430
- }.get(rule['action'], rule['action'])
431
- else:
432
- ru['action'] = rule.get('action'.lower(), "FR_ACT_TO_TBL")
433
-
434
- if 'fwmark' in rule:
435
- ru['fwmark'] = rule['fwmark']
436
-
437
- if 'ipproto' in rule:
438
- if type(rule['ipproto']) == str:
439
- ru['ip_proto'] = socket.getprotobyname(rule['ipproto'])
440
- else:
441
- ru['ip_proto'] = rule['ipproto']
442
-
443
- if 'to' in rule:
444
- ru['dst'] = str(ip_network(rule['to']).network_address)
445
- ru['dst_len'] = ip_network(rule['to']).prefixlen
446
-
447
- if 'from' in rule:
448
- ru['src'] = str(ip_network(rule['from']).network_address)
449
- ru['src_len'] = ip_network(rule['from']).prefixlen
450
-
451
- if 'iif' in rule:
452
- ru['iifname'] = rule['iif']
453
-
454
- if 'oif' in rule:
455
- ru['oifname'] = rule['oif']
456
-
457
- if 'priority' in rule:
458
- ru['priority'] = rule['priority']
459
-
460
- if 'vrrp' in rule:
461
- ru['_vrrp'] = rule['vrrp']
531
+ # convert config dict into IFLA keys and values
532
+ ru = parse_rule(rule)
462
533
 
534
+ # track rule
463
535
  self.rules.append(ru)
464
536
 
465
537
  def kernel_rules(self):
@@ -491,14 +563,13 @@ class Rules():
491
563
  return rules
492
564
 
493
565
  def show_rules(self, ignores):
566
+ ignores = parse_rules(ignores, implicit_defaults=False)
567
+
494
568
  rules = []
495
569
  for rule in self.kernel_rules():
496
570
  # skip ignored routes
497
571
  ignore = False
498
572
  for irule in ignores:
499
- if 'proto' in irule:
500
- irule['protocol'] = irule['proto']
501
- del irule['proto']
502
573
  if rule_matches(rule, irule, irule.keys()):
503
574
  ignore = True
504
575
  break
@@ -534,6 +605,9 @@ class Rules():
534
605
  return rules
535
606
 
536
607
  def apply(self, ignores, do_apply, by_vrrp, vrrp_type, vrrp_name, vrrp_state):
608
+ ignores = parse_rules(ignores, implicit_defaults=False)
609
+
610
+ # get rules that are currently in the kernels list
537
611
  krules = self.kernel_rules()
538
612
  for rule in self.rules:
539
613
  log_str = '#{}'.format(rule['priority'])
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)
@@ -4,7 +4,9 @@ from wgnlpy import WireGuard as WG
4
4
  from ipaddress import ip_network
5
5
  import collections
6
6
  from copy import deepcopy
7
+ import os
7
8
  import pyroute2.netns
9
+ from pyroute2 import NetlinkError
8
10
  import socket
9
11
 
10
12
  class WireGuard():
@@ -56,6 +58,9 @@ class WireGuard():
56
58
  try:
57
59
  state = self.wg.get_interface(
58
60
  self.iface, spill_private_key=True, spill_preshared_keys=True)
61
+ except NetlinkError as err:
62
+ logger.warning('query wireguard details failed: {}'.format(os.strerror(err.code)), extra={'iface': self.iface, 'netns': self.netns})
63
+ return
59
64
  except TypeError as err:
60
65
  # wgnlpy 0.1.5 can triggger a TypeError exception
61
66
  # if the WGPEER_A_LAST_HANDSHAKE_TIME NLA does not
@@ -113,10 +118,10 @@ class WireGuard():
113
118
  for setting in peer.keys():
114
119
  attr = getattr(peers[pubkey], setting)
115
120
  if setting == 'allowedips':
116
- attr = set(attr)
121
+ attr = [str(ip) for ip in attr]
117
122
  logger.debug(' peer.%s: %s => %s', setting, attr,
118
123
  peer[setting], extra={'iface': self.iface})
119
- if type(attr) == set:
124
+ if type(attr) == list:
120
125
  pchange |= not (attr == peer[setting])
121
126
  else:
122
127
  pchange |= str(peer[setting]) != str(getattr(