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 +15 -8
- ifstate/vrrp.py +1 -1
- {ifstate-1.13.6.dist-info → ifstate-2.0.0.dist-info}/METADATA +2 -2
- ifstate-2.0.0.dist-info/RECORD +39 -0
- {ifstate-1.13.6.dist-info → ifstate-2.0.0.dist-info}/WHEEL +1 -1
- libifstate/__init__.py +152 -81
- libifstate/address/__init__.py +1 -1
- libifstate/exception.py +6 -12
- libifstate/hook/__init__.py +195 -0
- libifstate/hook/wrapper.sh +50 -0
- libifstate/link/base.py +38 -37
- libifstate/link/dsa.py +1 -1
- libifstate/link/physical.py +3 -3
- libifstate/link/tun.py +3 -3
- libifstate/link/veth.py +2 -2
- libifstate/log.py +7 -4
- libifstate/netns/__init__.py +53 -14
- libifstate/parser/__init__.py +1 -1
- libifstate/parser/base.py +131 -85
- libifstate/routing/__init__.py +234 -114
- libifstate/schema/2/ifstate.conf.schema.json +4442 -0
- libifstate/sysctl/__init__.py +20 -2
- libifstate/tc/__init__.py +1 -1
- libifstate/util.py +153 -147
- libifstate/wireguard/__init__.py +82 -21
- ifstate-1.13.6.dist-info/RECORD +0 -37
- schema/ifstate.conf.schema.json +0 -4259
- {ifstate-1.13.6.dist-info → ifstate-2.0.0.dist-info}/entry_points.txt +0 -0
- {ifstate-1.13.6.dist-info → ifstate-2.0.0.dist-info}/licenses/LICENSE +0 -0
- {ifstate-1.13.6.dist-info → ifstate-2.0.0.dist-info}/top_level.txt +0 -0
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="
|
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/
|
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.
|
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
|
-
|
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:
|
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__ = "
|
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
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
path
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
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
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
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
|
138
|
-
if '
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
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
|
351
|
-
|
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
|
366
|
-
|
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
|
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("
|
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
|
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
|
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
|
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
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
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
|
802
|
+
ifs_links['lo'] = ifs_link
|
732
803
|
else:
|
733
|
-
ifs_links
|
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}}
|
libifstate/address/__init__.py
CHANGED
@@ -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
|
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('{}
|
31
|
-
op,
|
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:
|