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.
ifstate/ifstate.py CHANGED
@@ -21,6 +21,7 @@ import yaml
21
21
  class Actions():
22
22
  CHECK = "check"
23
23
  APPLY = "apply"
24
+ IDENTIFY = "identify"
24
25
  SHOW = "show"
25
26
  SHOWALL = "showall"
26
27
  VRRP = "vrrp"
@@ -31,6 +32,7 @@ class Actions():
31
32
  ACTIONS_HELP = {
32
33
  "CHECK" : "dry run update the network config",
33
34
  "APPLY" : "update the network config",
35
+ "IDENTIFY" : "show interface attributes available for identification",
34
36
  "SHOW" : "show running network config",
35
37
  "SHOWALL" : "show running network config (more settings)",
36
38
  "VRRP" : "run as keepalived notify script",
@@ -128,14 +130,16 @@ def main():
128
130
  group = parser.add_mutually_exclusive_group()
129
131
  parser.add_argument('--version', action='version',
130
132
  version='%(prog)s {version}'.format(version=__version__))
131
- group.add_argument("-v", "--verbose", action="store_true",
132
- help="be more verbose")
133
+ group.add_argument("-v", "--verbose", action="count", default=0,
134
+ help="be more verbose (twice for very verbose)")
133
135
  group.add_argument("-q", "--quiet", action="store_true",
134
136
  help="be more quiet, print only warnings and errors")
135
137
  parser.add_argument("-s", "--soft-schema", action="store_true",
136
138
  help="ignore schema validation errors, expect ifstatecli to trigger internal exceptions")
139
+ parser.add_argument("-S", "--show-secrets", action="store_true",
140
+ help="show secrets when dumping config")
137
141
  parser.add_argument("-c", "--config", type=str,
138
- default="/etc/ifstate/config.yml", help="configuration YaML filename")
142
+ default="/etc/ifstate/ifstate.yaml", help="configuration YaML filename")
139
143
  subparsers = parser.add_subparsers(
140
144
  dest='action', required=True, help="specifies the action to perform")
141
145
 
@@ -162,10 +166,10 @@ def main():
162
166
  "name", type=str, help="name of the vrrp group or instance")
163
167
 
164
168
  args = parser.parse_args()
165
- if args.verbose:
169
+ if args.verbose > 0:
166
170
  lvl = logging.DEBUG
167
171
  elif args.quiet:
168
- lvl = logging.ERROR
172
+ lvl = logging.WARNING
169
173
  else:
170
174
  lvl = logging.INFO
171
175
 
@@ -178,15 +182,18 @@ def main():
178
182
  shell()
179
183
  exit(0)
180
184
 
181
- ifslog = IfStateLogging(lvl, action=args.action)
185
+ ifslog = IfStateLogging(lvl, args.verbose > 1, action=args.action)
182
186
 
183
- if args.action in [Actions.SHOW, Actions.SHOWALL]:
187
+ if args.action in [Actions.IDENTIFY, Actions.SHOW, Actions.SHOWALL]:
184
188
  # preserve dict order on python 3.7+
185
189
  if sys.version_info >= (3, 7):
186
190
  yaml.add_representer(
187
191
  dict, lambda self, data: yaml.representer.SafeRepresenter.represent_dict(self, data.items()))
188
192
  ifs = IfState()
189
- print(yaml.dump(ifs.show(args.action == Actions.SHOWALL)))
193
+ if args.action == Actions.IDENTIFY:
194
+ print(yaml.dump(ifs.identify()))
195
+ else:
196
+ print(yaml.dump(ifs.show(args.action == Actions.SHOWALL, args.show_secrets)))
190
197
 
191
198
  ifslog.quit()
192
199
  exit(0)
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: 2.0.0
4
4
  Summary: Manage host interface settings in a declarative manner
5
5
  Home-page: https://ifstate.net/
6
6
  Author: Thomas Liske
@@ -9,7 +9,7 @@ License: GPL3+
9
9
  Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
11
  Requires-Dist: jsonschema
12
- Requires-Dist: pyroute2
12
+ Requires-Dist: pyroute2!=0.9.3
13
13
  Requires-Dist: pyyaml
14
14
  Requires-Dist: setproctitle
15
15
  Provides-Extra: shell
@@ -0,0 +1,39 @@
1
+ ifstate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ ifstate/ifstate.py,sha256=Vot_Bo0MGT_5MUKD5-pt2xB-fLLkjwvJo24KgtETtR8,9329
3
+ ifstate/shell.py,sha256=7_JFpi4icr9MijynDzbb0v5mxhFsng6PCC4m3uQ255A,2177
4
+ ifstate/vrrp.py,sha256=FJ9b1eJseTtZFfknHU-xV68Qz7cPrRrc5PTcjUVj-fY,5953
5
+ ifstate-2.0.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
6
+ libifstate/__init__.py,sha256=HFLB_cWQSAUarRzPCDdTFuL5EB1WjV1Fn0q_ut0RVvo,34053
7
+ libifstate/exception.py,sha256=NmKqD9O8dRWBgO__Pt0_wGSR5GWZ1RHXZZEUI3ArVLo,2360
8
+ libifstate/log.py,sha256=rG0ISWyeGGlC_jRREoSpkdySc5EmyhOQIgSfvRqlaZA,4651
9
+ libifstate/util.py,sha256=DqYyFzNqRceJxxGqTg94gT8OTHQQ2gZxQOo5Gzx6-6s,11555
10
+ libifstate/address/__init__.py,sha256=SobrZ9e2ItrmEidnKPBhUMitRDdTEajbkU9Rkmtx-gQ,3165
11
+ libifstate/bpf/__init__.py,sha256=NVzaunTmJU2PbIQg9eWBMKpFgLh3EnD3ujNa7Yt6rNc,7699
12
+ libifstate/bpf/ctypes.py,sha256=kLZJHZlba09Vc-tbsJAcKpDwdoNO2IjlYVLCopawHmA,4274
13
+ libifstate/bpf/map.py,sha256=cLHNMvRBDNW2yVCEf3z242_oRdU0HqVbFEYVkKXng0w,10818
14
+ libifstate/brport/__init__.py,sha256=NzdA8F4hr2se1bXKNnyKZbvOFlCWBq_cdjwsL1H0Y-o,2964
15
+ libifstate/fdb/__init__.py,sha256=9dpL5n8ct3CaA-z8I6ZEkD3yL6yWJQeq3fpIe9pc2zw,6486
16
+ libifstate/hook/__init__.py,sha256=012SIm1aULlXG4SoOYFV46u9k-4pPgtIENaUlRWPpe8,7375
17
+ libifstate/hook/wrapper.sh,sha256=ipCmvcadJbXTT6oUR7BIhT5uglITnHfiAdm44vydZuw,1101
18
+ libifstate/link/__init__.py,sha256=epVw6jY8exNeJZUmmUas91yJoeupfgIY7rthq7SGIIw,142
19
+ libifstate/link/base.py,sha256=zsitL1kSPLvjzZqyUIyEoCVlE_dhX2z5acNL1iZ2EAE,34508
20
+ libifstate/link/dsa.py,sha256=zOdatHE9_TLVVqP2H87V3d_M6Y3fRke2FqHj0u_Su2c,260
21
+ libifstate/link/physical.py,sha256=IG1OXG2q25QmvhxEDhyT-bf7guwIyXl0OzSJgQOqXzY,613
22
+ libifstate/link/tun.py,sha256=Rzn3ysE0cVuQGi3naxs9QXYBrLwVQTaVrFv2dtR1D60,1012
23
+ libifstate/link/veth.py,sha256=Sv5WZMMsefYFz9I0BQSZyKytCQYxv0vjwAnB7FYHR1A,2283
24
+ libifstate/neighbour/__init__.py,sha256=FJJpbJvqnxvOEii6QDMYzW5jQDEbiEy71GQOEbqaS48,2463
25
+ libifstate/netns/__init__.py,sha256=5Ak4vHU5UvdSYPFIQ8i8VMba2luyUeSpYAtRXxdTGPE,10287
26
+ libifstate/parser/__init__.py,sha256=uzv5U-6RPy-SSIyxR6H_F0SvAOQQF8x5ygErpUhsa7Y,109
27
+ libifstate/parser/base.py,sha256=v0S_R_aMZBdHJPI3zdelv5G0Xha-pUV2DRdmxhD7390,6624
28
+ libifstate/parser/yaml.py,sha256=MC0kmwqt3P45z61fb_wfUqoj0iZyhFYkdPyr0UqMSZA,1415
29
+ libifstate/routing/__init__.py,sha256=O4lbaCJvLgx-iQUa0WDgRmSPR4AKvveqPgxxb4HwYQ4,25305
30
+ libifstate/schema/2/ifstate.conf.schema.json,sha256=hVf0fduVV09uw3ZphWWwGpeQ7nL5z1aLTHUEemn34wc,221185
31
+ libifstate/sysctl/__init__.py,sha256=_QNmH0CirbBy8VAupTm6UhIhB88fGWqUy9TnfmS0j5I,3637
32
+ libifstate/tc/__init__.py,sha256=T8LEBxiHZH4sayPn9lK5hK8eB8dskuw6-kw81W_aMWU,12097
33
+ libifstate/wireguard/__init__.py,sha256=VSn3PzQXwZeRV1qf5nOc_RbeePQlXu8FUh2Lg356-ZA,8869
34
+ libifstate/xdp/__init__.py,sha256=X1xhEIGng7R5d5F4KsChykT2g6H-XBRWbWABijoYDQA,7208
35
+ ifstate-2.0.0.dist-info/METADATA,sha256=f8JoMVgocYw7ow9r6dny6Hrpk1UQRUkrjEN0ASmYkwE,1604
36
+ ifstate-2.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
37
+ ifstate-2.0.0.dist-info/entry_points.txt,sha256=HF6jX7Uu_nF1Ly-J9uEPeiRapOxnM6LuHsb2y6Mt-k4,52
38
+ ifstate-2.0.0.dist-info/top_level.txt,sha256=A7peI7aKBaM69fsiSPvMbL3rzTKZZr5qDxKC-pHMGdE,19
39
+ ifstate-2.0.0.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
@@ -2,9 +2,10 @@ from libifstate.exception import LinkDuplicate, NetnsUnknown
2
2
  from libifstate.link.base import ethtool_path, Link
3
3
  from libifstate.address import Addresses
4
4
  from libifstate.fdb import FDB
5
+ from libifstate.hook import Hooks
5
6
  from libifstate.neighbour import Neighbours
6
7
  from libifstate.routing import Tables, Rules, RTLookups
7
- from libifstate.parser import Parser
8
+ from libifstate.parser import Parser, get_available_hooks
8
9
  from libifstate.tc import TC
9
10
  from libifstate.exception import netlinkerror_classes
10
11
  import bisect
@@ -34,11 +35,16 @@ except ModuleNotFoundError:
34
35
  # ignore missing plugin
35
36
  pass
36
37
 
38
+ try:
39
+ from jsonschema import validate, ValidationError, FormatChecker
40
+ except ModuleNotFoundError:
41
+ # fail open and ignore missing jsonschema dependency
42
+ pass
43
+
37
44
  from libifstate.netns import NetNameSpace, prepare_netns, LinkRegistry, get_netns_instances
38
- from libifstate.util import logger, IfStateLogging, LinkDependency
45
+ from libifstate.util import logger, root_iw, kind_has_identify, IfStateLogging, LinkDependency, IDENTIFY_LOOKUPS
39
46
  from libifstate.exception import FeatureMissingError, LinkCircularLinked, LinkNoConfigFound, ParserValidationError
40
47
  from ipaddress import ip_network, ip_interface
41
- from jsonschema import validate, ValidationError, FormatChecker
42
48
  from copy import deepcopy
43
49
  import os
44
50
  import pkgutil
@@ -48,8 +54,7 @@ import json
48
54
  import errno
49
55
  import logging
50
56
 
51
- __version__ = "1.13.6"
52
-
57
+ __version__ = "2.0.0"
53
58
 
54
59
  class IfState():
55
60
  def __init__(self):
@@ -61,9 +66,12 @@ class IfState():
61
66
  self.ignore = {}
62
67
  self.features = {
63
68
  'brport': True,
69
+ 'devicetree': os.access('/sys/firmware/devicetree', os.R_OK),
70
+ 'iw': root_iw is not None,
64
71
  'link': True,
65
72
  'sysctl': os.access('/proc/sys/net', os.R_OK),
66
73
  'ethtool': not ethtool_path is None,
74
+ 'schema': not globals().get("validate") is None,
67
75
  'tc': True,
68
76
  'wireguard': not globals().get("WireGuard") is None,
69
77
  'bpf': not globals().get("libbpf") is None,
@@ -79,35 +87,38 @@ class IfState():
79
87
  libbpf.libbpf_set_print(0)
80
88
 
81
89
  def update(self, ifstates, soft_schema):
82
- # check config schema
83
- schema = json.loads(pkgutil.get_data(
84
- "libifstate", "../schema/ifstate.conf.schema.json"))
85
- try:
86
- validate(ifstates, schema, format_checker=FormatChecker())
87
- except ValidationError as ex:
88
- if len(ex.path) > 0:
89
- path = ["$"]
90
- for i, p in enumerate(ex.absolute_path):
91
- if type(p) == int:
92
- path.append("[{}]".format(p))
93
- else:
94
- path.append(".")
95
- 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)
96
105
 
97
- detail = "{}: {}".format("".join(path), ex.message)
98
- else:
99
- detail = ex.message
100
- if soft_schema:
101
- logger.error("Config validation failed for {}".format(detail))
102
- else:
103
- 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.')
104
115
 
105
116
  # add interface defaults
106
117
  if 'defaults' in ifstates:
107
- self.defaults = ifstates['defaults']
118
+ self.defaults = ifstates['parameters']['defaults']
108
119
 
109
120
  # add ignore list items
110
- self.ignore.update(ifstates['ignore'])
121
+ self.ignore.update(ifstates['parameters']['ignore'])
111
122
  self.ipaddr_ignore = set()
112
123
  for ip in self.ignore.get('ipaddr', []):
113
124
  self.ipaddr_ignore.add(ip_network(ip))
@@ -116,7 +127,10 @@ class IfState():
116
127
  self.fdb_ignore = re.compile('|'.join(self.ignore.get('fdb')))
117
128
 
118
129
  # save cshaper profiles
119
- self.cshaper_profiles = ifstates['cshaper']
130
+ self.cshaper_profiles = ifstates['parameters']['cshaper']
131
+
132
+ # prepare hooks
133
+ self.hooks = Hooks(ifstates['parameters'].get('hooks', {}))
120
134
 
121
135
  # build link registry over all named netns
122
136
  self.link_registry = LinkRegistry(self.ignore.get('ifname', []), self.root_netns)
@@ -134,17 +148,15 @@ class IfState():
134
148
  self._update(self.namespaces[netns_name], netns_ifstates)
135
149
 
136
150
  def _update(self, netns, ifstates):
137
- # parse options
138
- if 'options' in ifstates:
139
- # parse global sysctl settings
140
- if 'sysctl' in ifstates['options']:
141
- for proto in ifstates['options']['sysctl'].keys():
142
- if proto in ['all', 'default']:
143
- netns.sysctl.add(
144
- proto, ifstates['options']['sysctl'][proto])
145
- else:
146
- netns.sysctl.add_global(
147
- proto, ifstates['options']['sysctl'][proto])
151
+ # parse network sysctl settings
152
+ if 'sysctl' in ifstates:
153
+ for proto in ifstates['sysctl'].keys():
154
+ if proto in ['all', 'default']:
155
+ netns.sysctl.add(
156
+ proto, ifstates['sysctl'][proto])
157
+ else:
158
+ netns.sysctl.add_global(
159
+ proto, ifstates['sysctl'][proto])
148
160
 
149
161
  # load BPF programs
150
162
  if 'bpf' in ifstates:
@@ -157,8 +169,7 @@ class IfState():
157
169
  netns.bpf_progs.add(name, config)
158
170
 
159
171
  # add interfaces from config
160
- for ifstate in ifstates['interfaces']:
161
- name = ifstate['name']
172
+ for name, ifstate in ifstates['interfaces'].items():
162
173
  kind = ifstate['link']['kind']
163
174
  defaults = self.get_defaults(
164
175
  ifname=name,
@@ -189,7 +200,7 @@ class IfState():
189
200
  link.update(ifstate['link'])
190
201
  if link:
191
202
  netns.links[name] = Link(self,
192
- netns, name, link, ethtool, ifstate.get('vrrp'), ifstate.get('brport'))
203
+ netns, name, link, ifstate.get('identify', {}), ethtool, ifstate.get('hooks', []), ifstate.get('vrrp'), ifstate.get('brport'))
193
204
  else:
194
205
  netns.links[name] = None
195
206
 
@@ -224,7 +235,7 @@ class IfState():
224
235
  netns.sysctl.add(name, ifstate['sysctl'])
225
236
 
226
237
  if 'cshaper' in ifstate:
227
- profile_name = ifstate['cshaper'].get(
238
+ profile_name = ifstate['parameters']['cshaper'].get(
228
239
  'profile', 'default')
229
240
  logger.debug('cshaper profile {} enabled'.format(profile_name),
230
241
  extra={'iface': name, 'netns': netns})
@@ -246,7 +257,7 @@ class IfState():
246
257
  'qdisc': cshaper_profile['ingress_qdisc'],
247
258
  }
248
259
  }
249
- ifb_state['tc']['qdisc']['bandwidth'] = ifstate['cshaper'].get(
260
+ ifb_state['tc']['qdisc']['bandwidth'] = ifstate['parameters']['cshaper'].get(
250
261
  'ingress', 'unlimited')
251
262
 
252
263
  if 'vrrp' in ifstate:
@@ -279,10 +290,10 @@ class IfState():
279
290
  ]
280
291
  }
281
292
 
282
- ifstate['tc']['qdisc']['bandwidth'] = ifstate['cshaper'].get(
293
+ ifstate['tc']['qdisc']['bandwidth'] = ifstate['parameters']['cshaper'].get(
283
294
  'egress', 'unlimited')
284
295
 
285
- del ifstate['cshaper']
296
+ del ifstate['parameters']['cshaper']
286
297
 
287
298
  if 'tc' in ifstate:
288
299
  netns.tc[name] = TC(
@@ -347,8 +358,8 @@ class IfState():
347
358
  # ignore if the link is already gone, this might happen
348
359
  # when removing veth link peers
349
360
  if err.code != errno.ENODEV:
350
- logger.warning('removing link {} failed: {}'.format(
351
- ifname, err.args[1]), extra={'netns': item.netns})
361
+ logger.warning('removing failed: {}'.format(
362
+ err.args[1]), extra={'iface': ifname, 'netns': item.netns})
352
363
  return True
353
364
  else:
354
365
  # shutdown physical interfaces
@@ -362,8 +373,8 @@ class IfState():
362
373
  except Exception as err:
363
374
  if not isinstance(err, netlinkerror_classes):
364
375
  raise
365
- logger.warning('updating link {} failed: {}'.format(
366
- ifname, err.args[1]), extra={'netns': item.netns})
376
+ logger.warning('updating failed: {}'.format(
377
+ err.args[1]), extra={'iface': ifname, 'netns': item.netns})
367
378
  return False
368
379
 
369
380
  def _dependencies(self, netns):
@@ -455,7 +466,7 @@ class IfState():
455
466
 
456
467
  # create and destroy namespaces to match config
457
468
  if not by_vrrp and self.namespaces is not None:
458
- prepare_netns(do_apply, self.namespaces.keys(), self.new_namespaces)
469
+ prepare_netns(do_apply, self.namespaces.keys(), self.new_namespaces, self.ignore.get('netns', []))
459
470
  logger.info("")
460
471
 
461
472
  # get link dependency tree
@@ -467,8 +478,10 @@ class IfState():
467
478
  cleanup_items = []
468
479
  for item in self.link_registry.registry:
469
480
  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', [])):
481
+ # items without a link are orphan - keep them if they match the ignore (ifname|netns) regex list...
482
+ if item.link is None and \
483
+ not any(re.match(regex, ifname) for regex in self.ignore.get('ifname', [])) and \
484
+ not any(re.match(regex, item.netns.netns or '') for regex in self.ignore.get('netns', [])):
472
485
  # ...or are in a netns namespace while the config has no `namespaces` setting
473
486
  if self.namespaces is not None or item.netns.netns is None:
474
487
  if not had_cleanup:
@@ -514,9 +527,8 @@ class IfState():
514
527
  if link_dep.netns is None:
515
528
  self._apply_iface(do_apply, self.root_netns, link_dep, by_vrrp, vrrp_type, vrrp_name, vrrp_state)
516
529
  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
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))
520
532
  else:
521
533
  self._apply_iface(do_apply, self.namespaces[link_dep.netns], link_dep, by_vrrp, vrrp_type, vrrp_name, vrrp_state)
522
534
 
@@ -555,8 +567,10 @@ class IfState():
555
567
 
556
568
  def _apply_iface(self, do_apply, netns, link_dep, by_vrrp, vrrp_type, vrrp_name, vrrp_state):
557
569
  ifname = link_dep.ifname
558
- if ifname in netns.links:
559
- 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]
560
574
 
561
575
  # check for vrrp mode:
562
576
  # disable: vrrp type & name matches, but vrrp state not
@@ -609,6 +623,8 @@ class IfState():
609
623
  if ifname in netns.wireguard:
610
624
  netns.wireguard[ifname].apply(do_apply)
611
625
 
626
+ self.hooks.apply(link, do_apply)
627
+
612
628
  def _apply_routing(self, do_apply, netns, by_vrrp, vrrp_type, vrrp_name, vrrp_state):
613
629
  if not netns.tables is None:
614
630
  netns.tables.apply(self.ignore.get('routes', []), do_apply, by_vrrp, vrrp_type, vrrp_name, vrrp_state)
@@ -616,36 +632,86 @@ class IfState():
616
632
  if not netns.rules is None:
617
633
  netns.rules.apply(self.ignore.get('rules', []), do_apply, by_vrrp, vrrp_type, vrrp_name, vrrp_state)
618
634
 
619
- def show(self, showall=False):
635
+ def identify(self):
636
+ root_config = self._identify_netns(self.root_netns)
637
+ netns_instances = get_netns_instances()
638
+ if len(netns_instances) > 0:
639
+ netns_identifies = {}
640
+ for netns in netns_instances:
641
+ netns_identify = self._identify_netns(netns)
642
+ if netns_identify is not None:
643
+ netns_identifies[netns.netns] = netns_identify
644
+
645
+ if netns_identifies:
646
+ return {**root_config, 'namespaces': netns_identifies}
647
+
648
+ return {**root_config}
649
+
650
+ def _identify_netns(self, netns):
651
+ ifs_links = {}
652
+ for ipr_link in netns.ipr.get_links():
653
+ name = ipr_link.get_attr('IFLA_IFNAME')
654
+ # skip links on ignore list
655
+ if name != 'lo' and not any(re.match(regex, name) for regex in Parser._default_ifstates['parameters']['ignore']['ifname_builtin']):
656
+ info = ipr_link.get_attr('IFLA_LINKINFO')
657
+ if info is None:
658
+ kind = None
659
+ else:
660
+ kind = info.get_attr('IFLA_INFO_KIND')
661
+
662
+ if not kind_has_identify(kind):
663
+ continue
664
+
665
+ ifs_link = {
666
+ 'identify': {
667
+ },
668
+ }
669
+
670
+ for attr, lookup in IDENTIFY_LOOKUPS.items():
671
+ value = lookup(netns, ipr_link)
672
+ if value is not None:
673
+ ifs_link['identify'][attr] = value
674
+
675
+ ifs_links[name] = ifs_link
676
+
677
+ if ifs_links:
678
+ return {**{'interfaces': ifs_links}}
679
+ else:
680
+ return None
681
+
682
+ def show(self, showall=False, show_secrets=False):
620
683
  if showall:
621
684
  defaults = deepcopy(Parser._default_ifstates)
685
+
686
+ hooks = get_available_hooks()
687
+ if hooks:
688
+ defaults['parameters']['hooks'] = hooks
622
689
  else:
623
690
  defaults = {}
624
691
 
625
692
  ipaddr_ignore = []
626
- for ip in Parser._default_ifstates['ignore']['ipaddr_builtin']:
693
+ for ip in Parser._default_ifstates['parameters']['ignore']['ipaddr_builtin']:
627
694
  ipaddr_ignore.append(ip_network(ip))
628
695
 
629
- root_config = self._show_netns(self.root_netns, showall, ipaddr_ignore)
696
+ root_config = self._show_netns(self.root_netns, showall, show_secrets, ipaddr_ignore)
630
697
  netns_instances = get_netns_instances()
631
698
  if len(netns_instances) > 0:
632
699
  netns_configs = {}
633
700
  for netns in netns_instances:
634
- netns_configs[netns.netns] = self._show_netns(netns, showall, ipaddr_ignore)
701
+ netns_configs[netns.netns] = self._show_netns(netns, showall, show_secrets, ipaddr_ignore)
635
702
 
636
703
  return {**defaults, **root_config, 'namespaces': netns_configs}
637
704
 
638
705
 
639
706
  return {**defaults, **root_config}
640
707
 
641
- def _show_netns(self, netns, showall, ipaddr_ignore):
642
- ifs_links = []
708
+ def _show_netns(self, netns, showall, show_secrets, ipaddr_ignore):
709
+ ifs_links = {}
643
710
  for ipr_link in netns.ipr.get_links():
644
711
  name = ipr_link.get_attr('IFLA_IFNAME')
645
712
  # skip links on ignore list
646
- if name != 'lo' and not any(re.match(regex, name) for regex in Parser._default_ifstates['ignore']['ifname_builtin']):
713
+ if name != 'lo' and not any(re.match(regex, name) for regex in Parser._default_ifstates['parameters']['ignore']['ifname_builtin']):
647
714
  ifs_link = {
648
- 'name': name,
649
715
  'addresses': [],
650
716
  'link': {
651
717
  'state': ipr_link['state'],
@@ -659,12 +725,16 @@ class IfState():
659
725
  if not any(ip in net for net in ipaddr_ignore):
660
726
  ifs_link['addresses'].append(ip.with_prefixlen)
661
727
 
728
+ # drop empty ip addresses list, they are cleaned up by default
729
+ if not ifs_link['addresses']:
730
+ del(ifs_link['addresses'])
731
+
662
732
  info = ipr_link.get_attr('IFLA_LINKINFO')
663
733
  if info is None:
664
734
  kind = None
665
735
  else:
666
736
  kind = info.get_attr('IFLA_INFO_KIND')
667
- if kind is not None:
737
+ if not kind_has_identify(kind):
668
738
  ifs_link['link']['kind'] = kind
669
739
 
670
740
  data = info.get_attr('IFLA_INFO_DATA')
@@ -685,15 +755,13 @@ class IfState():
685
755
  addr = ipr_link.get_attr('IFLA_ADDRESS')
686
756
  if not addr is None:
687
757
  ifs_link['link']['address'] = addr
688
- permaddr = netns.ipr.get_permaddr(name)
689
- if not permaddr is None:
690
- if addr is None:
691
- ifs_link['link']['addr'] = permaddr
692
- elif addr != permaddr:
693
- ifs_link['link']['permaddr'] = permaddr
694
- businfo = netns.ipr.get_businfo(name)
695
- if not businfo is None:
696
- ifs_link['link']['businfo'] = businfo
758
+
759
+ # add identify section for physical links
760
+ ifs_link['identify'] = {}
761
+ for attr, func in IDENTIFY_LOOKUPS.items():
762
+ value = func(netns, ipr_link)
763
+ if value:
764
+ ifs_link['identify'][attr] = value
697
765
 
698
766
  # add device group if not 0
699
767
  group = ipr_link.get_attr('IFLA_GROUP')
@@ -720,6 +788,9 @@ class IfState():
720
788
 
721
789
  brport.BRPort.show(netns.ipr, showall, ipr_link['index'], ifs_link)
722
790
 
791
+ if ifs_link['link']['kind'] == 'wireguard':
792
+ wireguard.WireGuard.show(netns, showall, show_secrets, name, ifs_link)
793
+
723
794
  if name == 'lo':
724
795
  if ifs_link['addresses'] == Parser._default_lo_link['addresses']:
725
796
  del(ifs_link['addresses'])
@@ -728,13 +799,13 @@ class IfState():
728
799
  del(ifs_link['link'])
729
800
 
730
801
  if len(ifs_link) > 1:
731
- ifs_links.append(ifs_link)
802
+ ifs_links['lo'] = ifs_link
732
803
  else:
733
- ifs_links.append(ifs_link)
804
+ ifs_links[name] = ifs_link
734
805
 
735
806
  routing = {
736
- 'routes': Tables(netns).show_routes(Parser._default_ifstates['ignore']['routes_builtin']),
737
- 'rules': Rules(netns).show_rules(Parser._default_ifstates['ignore']['rules_builtin']),
807
+ 'routes': Tables(netns).show_routes(Parser._default_ifstates['parameters']['ignore']['routes_builtin']),
808
+ 'rules': Rules(netns).show_rules(Parser._default_ifstates['parameters']['ignore']['rules_builtin']),
738
809
  }
739
810
 
740
811
  return {**{'interfaces': ifs_links, 'routing': routing}}
@@ -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
@@ -1,19 +1,12 @@
1
- from pyroute2.netlink.exceptions import NetlinkError
1
+ from pyroute2 import NetlinkError
2
2
  from libifstate.util import logger
3
3
 
4
- # pyroute2.minimal exceptions might be broken
5
- # => workaround for pyroute2 #845 #847
6
4
  netlinkerror_classes = (NetlinkError)
7
- try:
8
- from pr2modules.netlink.exceptions import NetlinkError as Pr2mNetlinkError
9
- netlinkerror_classes = (NetlinkError, Pr2mNetlinkError)
10
- except ModuleNotFoundError:
11
- # required for pyroute2 before 0.6.0
12
- pass
13
5
 
14
6
  class ExceptionCollector():
15
- def __init__(self, ifname):
7
+ def __init__(self, ifname, netns):
16
8
  self.ifname = ifname
9
+ self.netns = netns
17
10
  self.reset()
18
11
 
19
12
  def reset(self):
@@ -27,8 +20,9 @@ class ExceptionCollector():
27
20
  'args': kwargs,
28
21
  })
29
22
  if not self.quiet:
30
- logger.warning('{} link {} failed: {}'.format(
31
- op, self.ifname, excpt.args[1]))
23
+ logger.warning('{} failed: {}'.format(
24
+ op, excpt.args[1]),
25
+ extra={'iface': self.ifname, 'netns': self.netns})
32
26
 
33
27
  def has_op(self, op):
34
28
  for e in self.excpts: