ifstate 1.13.7__py3-none-any.whl → 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ifstate
3
- Version: 1.13.7
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,,
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.7"
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):
@@ -516,9 +527,8 @@ class IfState():
516
527
  if link_dep.netns is None:
517
528
  self._apply_iface(do_apply, self.root_netns, link_dep, by_vrrp, vrrp_type, vrrp_name, vrrp_state)
518
529
  else:
519
- if link_dep.netns not in self.namespaces:
520
- logger.warning("add link {} failed: netns '{}' is unknown".format(link_dep.ifname, link_dep.netns))
521
- 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))
522
532
  else:
523
533
  self._apply_iface(do_apply, self.namespaces[link_dep.netns], link_dep, by_vrrp, vrrp_type, vrrp_name, vrrp_state)
524
534
 
@@ -557,8 +567,10 @@ class IfState():
557
567
 
558
568
  def _apply_iface(self, do_apply, netns, link_dep, by_vrrp, vrrp_type, vrrp_name, vrrp_state):
559
569
  ifname = link_dep.ifname
560
- if ifname in netns.links:
561
- 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]
562
574
 
563
575
  # check for vrrp mode:
564
576
  # disable: vrrp type & name matches, but vrrp state not
@@ -611,6 +623,8 @@ class IfState():
611
623
  if ifname in netns.wireguard:
612
624
  netns.wireguard[ifname].apply(do_apply)
613
625
 
626
+ self.hooks.apply(link, do_apply)
627
+
614
628
  def _apply_routing(self, do_apply, netns, by_vrrp, vrrp_type, vrrp_name, vrrp_state):
615
629
  if not netns.tables is None:
616
630
  netns.tables.apply(self.ignore.get('routes', []), do_apply, by_vrrp, vrrp_type, vrrp_name, vrrp_state)
@@ -618,36 +632,86 @@ class IfState():
618
632
  if not netns.rules is None:
619
633
  netns.rules.apply(self.ignore.get('rules', []), do_apply, by_vrrp, vrrp_type, vrrp_name, vrrp_state)
620
634
 
621
- 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):
622
683
  if showall:
623
684
  defaults = deepcopy(Parser._default_ifstates)
685
+
686
+ hooks = get_available_hooks()
687
+ if hooks:
688
+ defaults['parameters']['hooks'] = hooks
624
689
  else:
625
690
  defaults = {}
626
691
 
627
692
  ipaddr_ignore = []
628
- for ip in Parser._default_ifstates['ignore']['ipaddr_builtin']:
693
+ for ip in Parser._default_ifstates['parameters']['ignore']['ipaddr_builtin']:
629
694
  ipaddr_ignore.append(ip_network(ip))
630
695
 
631
- 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)
632
697
  netns_instances = get_netns_instances()
633
698
  if len(netns_instances) > 0:
634
699
  netns_configs = {}
635
700
  for netns in netns_instances:
636
- 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)
637
702
 
638
703
  return {**defaults, **root_config, 'namespaces': netns_configs}
639
704
 
640
705
 
641
706
  return {**defaults, **root_config}
642
707
 
643
- def _show_netns(self, netns, showall, ipaddr_ignore):
644
- ifs_links = []
708
+ def _show_netns(self, netns, showall, show_secrets, ipaddr_ignore):
709
+ ifs_links = {}
645
710
  for ipr_link in netns.ipr.get_links():
646
711
  name = ipr_link.get_attr('IFLA_IFNAME')
647
712
  # skip links on ignore list
648
- 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']):
649
714
  ifs_link = {
650
- 'name': name,
651
715
  'addresses': [],
652
716
  'link': {
653
717
  'state': ipr_link['state'],
@@ -661,12 +725,16 @@ class IfState():
661
725
  if not any(ip in net for net in ipaddr_ignore):
662
726
  ifs_link['addresses'].append(ip.with_prefixlen)
663
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
+
664
732
  info = ipr_link.get_attr('IFLA_LINKINFO')
665
733
  if info is None:
666
734
  kind = None
667
735
  else:
668
736
  kind = info.get_attr('IFLA_INFO_KIND')
669
- if kind is not None:
737
+ if not kind_has_identify(kind):
670
738
  ifs_link['link']['kind'] = kind
671
739
 
672
740
  data = info.get_attr('IFLA_INFO_DATA')
@@ -687,15 +755,13 @@ class IfState():
687
755
  addr = ipr_link.get_attr('IFLA_ADDRESS')
688
756
  if not addr is None:
689
757
  ifs_link['link']['address'] = addr
690
- permaddr = netns.ipr.get_permaddr(name)
691
- if not permaddr is None:
692
- if addr is None:
693
- ifs_link['link']['addr'] = permaddr
694
- elif addr != permaddr:
695
- ifs_link['link']['permaddr'] = permaddr
696
- businfo = netns.ipr.get_businfo(name)
697
- if not businfo is None:
698
- 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
699
765
 
700
766
  # add device group if not 0
701
767
  group = ipr_link.get_attr('IFLA_GROUP')
@@ -722,6 +788,9 @@ class IfState():
722
788
 
723
789
  brport.BRPort.show(netns.ipr, showall, ipr_link['index'], ifs_link)
724
790
 
791
+ if ifs_link['link']['kind'] == 'wireguard':
792
+ wireguard.WireGuard.show(netns, showall, show_secrets, name, ifs_link)
793
+
725
794
  if name == 'lo':
726
795
  if ifs_link['addresses'] == Parser._default_lo_link['addresses']:
727
796
  del(ifs_link['addresses'])
@@ -730,13 +799,13 @@ class IfState():
730
799
  del(ifs_link['link'])
731
800
 
732
801
  if len(ifs_link) > 1:
733
- ifs_links.append(ifs_link)
802
+ ifs_links['lo'] = ifs_link
734
803
  else:
735
- ifs_links.append(ifs_link)
804
+ ifs_links[name] = ifs_link
736
805
 
737
806
  routing = {
738
- 'routes': Tables(netns).show_routes(Parser._default_ifstates['ignore']['routes_builtin']),
739
- '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']),
740
809
  }
741
810
 
742
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: