ifstate 1.12.0__py3-none-any.whl → 1.13.1__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 +39 -27
- ifstate/vrrp.py +93 -40
- {ifstate-1.12.0.dist-info → ifstate-1.13.1.dist-info}/METADATA +1 -1
- {ifstate-1.12.0.dist-info → ifstate-1.13.1.dist-info}/RECORD +15 -15
- {ifstate-1.12.0.dist-info → ifstate-1.13.1.dist-info}/WHEEL +1 -1
- libifstate/__init__.py +2 -10
- libifstate/link/base.py +1 -0
- libifstate/parser/base.py +7 -0
- libifstate/parser/yaml.py +3 -0
- libifstate/routing/__init__.py +25 -3
- libifstate/util.py +19 -1
- schema/ifstate.conf.schema.json +71 -12
- {ifstate-1.12.0.dist-info → ifstate-1.13.1.dist-info}/LICENSE +0 -0
- {ifstate-1.12.0.dist-info → ifstate-1.13.1.dist-info}/entry_points.txt +0 -0
- {ifstate-1.12.0.dist-info → ifstate-1.13.1.dist-info}/top_level.txt +0 -0
ifstate/ifstate.py
CHANGED
@@ -24,16 +24,18 @@ class Actions():
|
|
24
24
|
SHOWALL = "showall"
|
25
25
|
VRRP = "vrrp"
|
26
26
|
VRRP_FIFO = "vrrp-fifo"
|
27
|
+
VRRP_WORKER = "vrrp-worker"
|
27
28
|
SHELL = "shell"
|
28
29
|
|
29
30
|
ACTIONS_HELP = {
|
30
|
-
"CHECK"
|
31
|
-
"APPLY"
|
32
|
-
"SHOW"
|
33
|
-
"SHOWALL"
|
34
|
-
"VRRP"
|
35
|
-
"VRRP_FIFO": "run as keepalived notify_fifo_script",
|
36
|
-
"
|
31
|
+
"CHECK" : "dry run update the network config",
|
32
|
+
"APPLY" : "update the network config",
|
33
|
+
"SHOW" : "show running network config",
|
34
|
+
"SHOWALL" : "show running network config (more settings)",
|
35
|
+
"VRRP" : "run as keepalived notify script",
|
36
|
+
"VRRP_FIFO" : "run as keepalived notify_fifo_script",
|
37
|
+
"VRRP_WORKER": "worker process for vrrp-fifo",
|
38
|
+
"SHELL" : "launch interactive python shell (pyroute2)",
|
37
39
|
}
|
38
40
|
|
39
41
|
class IfsConfigHandler():
|
@@ -41,23 +43,22 @@ class IfsConfigHandler():
|
|
41
43
|
self.fn = fn
|
42
44
|
self.soft_schema = soft_schema
|
43
45
|
|
46
|
+
# require to be called from the root netns
|
47
|
+
try:
|
48
|
+
# assume init runs in the root netns
|
49
|
+
if os.readlink('/proc/1/ns/net') != os.readlink(f'/proc/{os.getpid()}/ns/net'):
|
50
|
+
logger.error("Must not be run from inside a netns!")
|
51
|
+
raise NetNSNotRoot()
|
52
|
+
except OSError as ex:
|
53
|
+
logger.debug(f'root netns test: {ex}')
|
54
|
+
pass
|
55
|
+
|
44
56
|
self.ifs = self.load_config()
|
45
57
|
self.cb = None
|
46
58
|
|
47
|
-
def set_callback(self, cb):
|
48
|
-
self.cb = cb
|
49
|
-
|
50
|
-
def sighup_handler(self, signum, frame):
|
51
|
-
logger.info("SIGHUP: reloading configuration")
|
52
|
-
try:
|
53
|
-
self.ifs = self.load_config()
|
54
|
-
self.cb(self.ifs)
|
55
|
-
except:
|
56
|
-
logger.exception("failed to reload configuration")
|
57
|
-
|
58
59
|
def load_config(self):
|
59
60
|
try:
|
60
|
-
parser = YamlParser(self.fn)
|
61
|
+
self.parser = YamlParser(self.fn)
|
61
62
|
except ParserOpenError as ex:
|
62
63
|
logger.error(
|
63
64
|
"Config loading from {} failed: {}".format(ex.fn, ex.msg))
|
@@ -71,10 +72,8 @@ class IfsConfigHandler():
|
|
71
72
|
raise ex
|
72
73
|
|
73
74
|
try:
|
74
|
-
ifstates = parser.config()
|
75
|
-
|
76
75
|
ifs = IfState()
|
77
|
-
ifs.update(
|
76
|
+
ifs.update(self.parser.config(), self.soft_schema)
|
78
77
|
return ifs
|
79
78
|
except ParserValidationError as ex:
|
80
79
|
logger.error("Config validation failed for {}".format(ex.detail))
|
@@ -83,10 +82,14 @@ class IfsConfigHandler():
|
|
83
82
|
logger.error(
|
84
83
|
"Config uses unavailable feature: {}".format(ex.feature))
|
85
84
|
raise ex
|
86
|
-
except NetNSNotRoot as ex:
|
87
|
-
logger.error("Must not be run from inside a netns!")
|
88
|
-
raise ex
|
89
85
|
|
86
|
+
def dump_config(self, fn):
|
87
|
+
umask = os.umask(0o077)
|
88
|
+
try:
|
89
|
+
with open(fn, "w") as fh:
|
90
|
+
self.parser.dump(fh)
|
91
|
+
finally:
|
92
|
+
os.umask(umask)
|
90
93
|
|
91
94
|
def shell():
|
92
95
|
from ifstate.shell import IfStateConsole
|
@@ -151,6 +154,12 @@ def main():
|
|
151
154
|
action_parsers[Actions.VRRP_FIFO].add_argument(
|
152
155
|
"fifo", type=str, help="named FIFO to read state changes from")
|
153
156
|
|
157
|
+
# Parameters for the vrrp-worker action
|
158
|
+
action_parsers[Actions.VRRP_WORKER].add_argument(
|
159
|
+
"type", type=str.lower, choices=["group", "instance"], help="type of vrrp notification")
|
160
|
+
action_parsers[Actions.VRRP_WORKER].add_argument(
|
161
|
+
"name", type=str, help="name of the vrrp group or instance")
|
162
|
+
|
154
163
|
args = parser.parse_args()
|
155
164
|
if args.verbose:
|
156
165
|
lvl = logging.DEBUG
|
@@ -181,7 +190,7 @@ def main():
|
|
181
190
|
ifslog.quit()
|
182
191
|
exit(0)
|
183
192
|
|
184
|
-
if args.action in [Actions.CHECK, Actions.APPLY, Actions.VRRP, Actions.VRRP_FIFO]:
|
193
|
+
if args.action in [Actions.CHECK, Actions.APPLY, Actions.VRRP, Actions.VRRP_FIFO, Actions.VRRP_WORKER]:
|
185
194
|
try:
|
186
195
|
ifs_config = IfsConfigHandler(args.config, args.soft_schema)
|
187
196
|
except (ParserOpenError,
|
@@ -203,7 +212,10 @@ def main():
|
|
203
212
|
exit(ex.exit_code())
|
204
213
|
elif args.action == Actions.VRRP_FIFO:
|
205
214
|
from ifstate.vrrp import vrrp_fifo
|
206
|
-
vrrp_fifo(args.fifo, ifs_config
|
215
|
+
vrrp_fifo(args.fifo, ifs_config)
|
216
|
+
elif args.action == Actions.VRRP_WORKER:
|
217
|
+
from ifstate.vrrp import vrrp_worker
|
218
|
+
vrrp_worker(args.type, args.name, ifs_config)
|
207
219
|
else:
|
208
220
|
# ignore some well-known signals to prevent interruptions (i.e. due to ssh connection loss)
|
209
221
|
signal.signal(signal.SIGHUP, signal.SIG_IGN)
|
ifstate/vrrp.py
CHANGED
@@ -1,51 +1,74 @@
|
|
1
1
|
from libifstate.util import logger, IfStateLogging
|
2
2
|
|
3
|
+
import atexit
|
3
4
|
from copy import deepcopy
|
4
5
|
import logging
|
5
6
|
import multiprocessing as mp
|
7
|
+
import threading
|
6
8
|
import os
|
7
9
|
import re
|
8
10
|
from setproctitle import setproctitle
|
9
11
|
import signal
|
12
|
+
import subprocess
|
13
|
+
import sys
|
10
14
|
|
11
|
-
class VrrpFifoProcess(
|
15
|
+
class VrrpFifoProcess():
|
12
16
|
'''
|
13
17
|
Process for vrrp group/instance configuration.
|
14
18
|
'''
|
15
|
-
def __init__(self,
|
16
|
-
self.
|
17
|
-
self.
|
18
|
-
self.
|
19
|
-
self.
|
20
|
-
|
21
|
-
|
19
|
+
def __init__(self, worker_args):
|
20
|
+
self.worker_args = worker_args
|
21
|
+
self.logger_extra = {'iface': f'{worker_args[-2]} "{worker_args[-1]}"'}
|
22
|
+
self.state_queue = mp.Queue()
|
23
|
+
self.worker_proc = None
|
24
|
+
|
25
|
+
worker_io = threading.Thread(target=self.dequeue)
|
26
|
+
worker_io.start()
|
22
27
|
|
23
28
|
def vrrp_update(self, vrrp_state):
|
24
|
-
self.
|
29
|
+
self.state_queue.put(vrrp_state)
|
25
30
|
|
26
|
-
def
|
27
|
-
instance_title = "vrrp-{}-{}".format(self.vrrp_type, self.vrrp_name)
|
28
|
-
setproctitle("ifstate-{}".format(instance_title))
|
29
|
-
ifslog = IfStateLogging(self.log_level, action=instance_title, log_stderr=False)
|
30
|
-
logger.info('worker spawned')
|
31
|
+
def dequeue(self):
|
31
32
|
while True:
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
state = self.state_queue.get()
|
34
|
+
|
35
|
+
# Should we terminate?
|
36
|
+
if state is None:
|
37
|
+
self.worker_proc.stdin.close()
|
38
|
+
self.worker_proc.wait()
|
35
39
|
return
|
36
|
-
|
37
|
-
|
38
|
-
|
40
|
+
|
41
|
+
# Restart ifstate vrrp-worker if not alive alive?
|
42
|
+
if self.worker_proc.poll() is not None:
|
43
|
+
logger.warning("worker died", extra=self.logger_extra)
|
44
|
+
self.start()
|
45
|
+
|
46
|
+
logger.info(f'state => {state}', extra=self.logger_extra)
|
47
|
+
self.worker_proc.stdin.write(f'{state}\n')
|
48
|
+
self.worker_proc.stdin.flush()
|
49
|
+
logger.warning("dequeue terminated")
|
50
|
+
|
51
|
+
def start(self):
|
52
|
+
logger.info("spawning worker", extra=self.logger_extra)
|
53
|
+
self.worker_proc = subprocess.Popen(self.worker_args, stdin=subprocess.PIPE, stderr=sys.stderr, text=True)
|
54
|
+
|
39
55
|
|
40
56
|
class VrrpStates():
|
41
57
|
'''
|
42
58
|
Tracks processes and states for vrrp groups/instances.
|
43
59
|
'''
|
44
|
-
def __init__(self,
|
45
|
-
self.
|
46
|
-
self.log_level = log_level
|
60
|
+
def __init__(self, ifs_config):
|
61
|
+
self.ifs_config = ifs_config
|
47
62
|
self.processes = {}
|
48
63
|
self.states = {}
|
64
|
+
self.pid_file = f"/run/libifstate/vrrp/{os.getpid()}.pid"
|
65
|
+
self.cfg_file = f"/run/libifstate/vrrp/{os.getpid()}.cfg"
|
66
|
+
self.worker_args = (
|
67
|
+
sys.argv[0],
|
68
|
+
'-c', self.cfg_file,
|
69
|
+
'vrrp-worker')
|
70
|
+
if logger.level == logging.DEBUG:
|
71
|
+
self.worker_args.append('-v')
|
49
72
|
|
50
73
|
def update(self, vrrp_type, vrrp_name, vrrp_state):
|
51
74
|
'''
|
@@ -54,44 +77,65 @@ class VrrpStates():
|
|
54
77
|
'''
|
55
78
|
key = (vrrp_type, vrrp_name)
|
56
79
|
if not key in self.processes:
|
57
|
-
|
80
|
+
worker_args = self.worker_args + key
|
81
|
+
self.processes[key] = VrrpFifoProcess(worker_args)
|
58
82
|
self.processes[key].start()
|
59
83
|
|
60
84
|
self.states[key] = vrrp_state
|
61
85
|
self.processes[key].vrrp_update(vrrp_state)
|
62
86
|
|
63
|
-
def reconfigure(self,
|
87
|
+
def reconfigure(self, *argv):
|
64
88
|
'''
|
65
89
|
Reconfigure all known groups/instances using their last known state by spawning
|
66
90
|
new worker Processes.
|
67
91
|
'''
|
68
|
-
|
92
|
+
|
93
|
+
# save new config to temp file
|
94
|
+
try:
|
95
|
+
self.ifs_config.load_config()
|
96
|
+
self.ifs_config.dump_config(self.cfg_file)
|
97
|
+
except Exception as ex:
|
98
|
+
logger.error(f'failed to reload config: {ex}')
|
99
|
+
return
|
100
|
+
|
69
101
|
for key, state in self.states.items():
|
102
|
+
worker_args = self.worker_args + key
|
70
103
|
self.processes[key].vrrp_update(None)
|
71
|
-
self.processes[key] = VrrpFifoProcess(
|
104
|
+
self.processes[key] = VrrpFifoProcess(worker_args)
|
72
105
|
self.processes[key].start()
|
73
106
|
self.processes[key].vrrp_update(self.states[key])
|
74
107
|
|
75
|
-
def
|
76
|
-
|
77
|
-
|
108
|
+
def cleanup_run(self, *argv):
|
109
|
+
"""
|
110
|
+
"""
|
111
|
+
for file in [self.pid_file, self.cfg_file]:
|
112
|
+
try:
|
113
|
+
os.unlink(file)
|
114
|
+
except OSError as ex:
|
115
|
+
if ex.errno != 2:
|
116
|
+
logger.warning(f"cannot cleanup {file}: {ex}")
|
117
|
+
|
118
|
+
def vrrp_fifo(args_fifo, ifs_config):
|
119
|
+
vrrp_states = VrrpStates(ifs_config)
|
78
120
|
|
79
|
-
signal.signal(signal.SIGHUP,
|
121
|
+
signal.signal(signal.SIGHUP, vrrp_states.reconfigure)
|
80
122
|
signal.signal(signal.SIGPIPE, signal.SIG_IGN)
|
81
|
-
signal.signal(signal.SIGTERM,
|
123
|
+
signal.signal(signal.SIGTERM, vrrp_states.cleanup_run)
|
82
124
|
|
83
|
-
pid_file = f"/run/libifstate/vrrp/{os.getpid()}.pid"
|
84
125
|
try:
|
85
126
|
os.makedirs("/run/libifstate/vrrp", exist_ok=True)
|
86
127
|
|
87
|
-
with open(pid_file, "w", encoding="utf-8") as fh:
|
128
|
+
with open(vrrp_states.pid_file, "w", encoding="utf-8") as fh:
|
88
129
|
fh.write(args_fifo)
|
130
|
+
atexit.register(vrrp_states.cleanup_run)
|
131
|
+
|
132
|
+
ifs_config.dump_config(vrrp_states.cfg_file)
|
89
133
|
except IOError as err:
|
90
|
-
logger.exception(f'failed to write pid file {pid_file}: {err}')
|
134
|
+
logger.exception(f'failed to write pid file {vrrp_states.pid_file}: {err}')
|
91
135
|
|
92
136
|
try:
|
93
137
|
status_pattern = re.compile(
|
94
|
-
r'(group|instance) "([^"]+)" (unknown|fault|backup|master)( \d+)?$', re.IGNORECASE)
|
138
|
+
r'(group|instance) "([^"]+)" (unknown|fault|backup|master|stop)( \d+)?$', re.IGNORECASE)
|
95
139
|
|
96
140
|
with open(args_fifo) as fifo:
|
97
141
|
logger.debug("entering fifo loop...")
|
@@ -107,7 +151,16 @@ def vrrp_fifo(args_fifo, ifs_config, log_level):
|
|
107
151
|
logger.warning(f'failed to parse fifo input: {line.strip()}')
|
108
152
|
mp.active_children()
|
109
153
|
finally:
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
154
|
+
vrrp_states.cleanup_run()
|
155
|
+
|
156
|
+
def vrrp_worker(vrrp_type, vrrp_name, ifs_config):
|
157
|
+
instance_title = "vrrp-{}-{}".format(vrrp_type, vrrp_name)
|
158
|
+
setproctitle("ifstate-{}".format(instance_title))
|
159
|
+
|
160
|
+
logger.info('worker is alive')
|
161
|
+
for state in sys.stdin:
|
162
|
+
vrrp_state = state.strip()
|
163
|
+
|
164
|
+
ifstate = deepcopy(ifs_config.ifs)
|
165
|
+
ifstate.apply(vrrp_type, vrrp_name, vrrp_state)
|
166
|
+
logger.info('terminating')
|
@@ -1,11 +1,11 @@
|
|
1
1
|
ifstate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
ifstate/ifstate.py,sha256=
|
2
|
+
ifstate/ifstate.py,sha256=jNWVc-XPZOKwD--xSJvAXZP-DEomljmGXtMf8SAdmmU,8770
|
3
3
|
ifstate/shell.py,sha256=7_JFpi4icr9MijynDzbb0v5mxhFsng6PCC4m3uQ255A,2177
|
4
|
-
ifstate/vrrp.py,sha256=
|
5
|
-
libifstate/__init__.py,sha256=
|
4
|
+
ifstate/vrrp.py,sha256=m-vkm_Yx82qx5RmTnouYceWpa1agziBUHr87yK9tBro,5542
|
5
|
+
libifstate/__init__.py,sha256=0v7ECPwNFfreOiy5YidN9pHMmEol3gCIcEDFZ85I94w,30792
|
6
6
|
libifstate/exception.py,sha256=5i59BZdl56J_sNJbyU9n6uHuUNJEyDOO4FJ-neDn9Ds,2608
|
7
7
|
libifstate/log.py,sha256=XVoZdwdQoWsjuupFIuG6OP0OrBpXpx7oqyAaUhQ-nJk,4553
|
8
|
-
libifstate/util.py,sha256=
|
8
|
+
libifstate/util.py,sha256=vqNaa67QZ2G07bQNAisTyUKbVmp0JGkvEGgbKVTtmYA,10579
|
9
9
|
libifstate/address/__init__.py,sha256=zIGM7UWXSvoijHMD06DfS2CDtiHpiJlqDgJG7Dl1IPE,3222
|
10
10
|
libifstate/bpf/__init__.py,sha256=NVzaunTmJU2PbIQg9eWBMKpFgLh3EnD3ujNa7Yt6rNc,7699
|
11
11
|
libifstate/bpf/ctypes.py,sha256=kLZJHZlba09Vc-tbsJAcKpDwdoNO2IjlYVLCopawHmA,4274
|
@@ -13,7 +13,7 @@ libifstate/bpf/map.py,sha256=cLHNMvRBDNW2yVCEf3z242_oRdU0HqVbFEYVkKXng0w,10818
|
|
13
13
|
libifstate/brport/__init__.py,sha256=NzdA8F4hr2se1bXKNnyKZbvOFlCWBq_cdjwsL1H0Y-o,2964
|
14
14
|
libifstate/fdb/__init__.py,sha256=jMplRZZQKkgwFQT2L7Ua4YCdLKwOzkd43_6OFtet2No,6262
|
15
15
|
libifstate/link/__init__.py,sha256=epVw6jY8exNeJZUmmUas91yJoeupfgIY7rthq7SGIIw,142
|
16
|
-
libifstate/link/base.py,sha256=
|
16
|
+
libifstate/link/base.py,sha256=UX_g8R5Hm9SbtezM2Hc6ED8rGk-LlchfNCOHmoE_OG0,33201
|
17
17
|
libifstate/link/dsa.py,sha256=Y3axTtcym6YL1voKblxctx4PoKDZHzpteKQNnEBUrS8,264
|
18
18
|
libifstate/link/physical.py,sha256=cJiq-MCfy-3XQoU-OxzgfPZZtu_pJ8u2ioJgn9VYdGk,560
|
19
19
|
libifstate/link/tun.py,sha256=m55o5cwO3h3DCLofUR-68fM4ggLoTKElp6ZJ2LrJSCc,959
|
@@ -21,17 +21,17 @@ libifstate/link/veth.py,sha256=dEnkhg1AU-CFOFJN-U68G6FJ74CNW9G4RXsa5vLs-qY,2114
|
|
21
21
|
libifstate/neighbour/__init__.py,sha256=FJJpbJvqnxvOEii6QDMYzW5jQDEbiEy71GQOEbqaS48,2463
|
22
22
|
libifstate/netns/__init__.py,sha256=lKv43oEcpVUmL494MNOOG_Uh4ght50uu8n8nb9TIwsA,9077
|
23
23
|
libifstate/parser/__init__.py,sha256=byz1W0G7UewVc5FFie-ti3UZjGK3-75wHIaOeq0oySQ,88
|
24
|
-
libifstate/parser/base.py,sha256=
|
25
|
-
libifstate/parser/yaml.py,sha256=
|
26
|
-
libifstate/routing/__init__.py,sha256
|
24
|
+
libifstate/parser/base.py,sha256=VFAo05O3tiKtI381LVUMYfsDTseMKoQGTfkgnEkm3H4,4770
|
25
|
+
libifstate/parser/yaml.py,sha256=MC0kmwqt3P45z61fb_wfUqoj0iZyhFYkdPyr0UqMSZA,1415
|
26
|
+
libifstate/routing/__init__.py,sha256=Hmj-LHBEtVXs5tlRzY8JS_C5qdnN3alD3cIznmzQ-rY,20469
|
27
27
|
libifstate/sysctl/__init__.py,sha256=L2gkoLnac_HM6RbCaKmsEuDTNj2m7U4Rjw42TEti0kg,3074
|
28
28
|
libifstate/tc/__init__.py,sha256=inPdampCOIr_4oKNB3awqMkW0Eh4fpPh9jvSba6sPVg,12092
|
29
29
|
libifstate/wireguard/__init__.py,sha256=vM7vxioV9vQ3gq9SE_URf97ZfGhLsxNiam16y_gXZ5E,6225
|
30
30
|
libifstate/xdp/__init__.py,sha256=X1xhEIGng7R5d5F4KsChykT2g6H-XBRWbWABijoYDQA,7208
|
31
|
-
schema/ifstate.conf.schema.json,sha256=
|
32
|
-
ifstate-1.
|
33
|
-
ifstate-1.
|
34
|
-
ifstate-1.
|
35
|
-
ifstate-1.
|
36
|
-
ifstate-1.
|
37
|
-
ifstate-1.
|
31
|
+
schema/ifstate.conf.schema.json,sha256=oDCBqgOI5uFbSjWUz9RcVmK-3i_90m3AMKA2uzGclRg,201291
|
32
|
+
ifstate-1.13.1.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
33
|
+
ifstate-1.13.1.dist-info/METADATA,sha256=1O1GTUtL-q0T6_QZctL8KntC-zC5RhHz7pnQHDaBlGw,1385
|
34
|
+
ifstate-1.13.1.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
35
|
+
ifstate-1.13.1.dist-info/entry_points.txt,sha256=HF6jX7Uu_nF1Ly-J9uEPeiRapOxnM6LuHsb2y6Mt-k4,52
|
36
|
+
ifstate-1.13.1.dist-info/top_level.txt,sha256=A7peI7aKBaM69fsiSPvMbL3rzTKZZr5qDxKC-pHMGdE,19
|
37
|
+
ifstate-1.13.1.dist-info/RECORD,,
|
libifstate/__init__.py
CHANGED
@@ -6,7 +6,7 @@ from libifstate.neighbour import Neighbours
|
|
6
6
|
from libifstate.routing import Tables, Rules, RTLookups
|
7
7
|
from libifstate.parser import Parser
|
8
8
|
from libifstate.tc import TC
|
9
|
-
from libifstate.exception import netlinkerror_classes
|
9
|
+
from libifstate.exception import netlinkerror_classes
|
10
10
|
import bisect
|
11
11
|
import os
|
12
12
|
import pyroute2
|
@@ -48,7 +48,7 @@ import json
|
|
48
48
|
import errno
|
49
49
|
import logging
|
50
50
|
|
51
|
-
__version__ = "1.
|
51
|
+
__version__ = "1.13.1"
|
52
52
|
|
53
53
|
|
54
54
|
class IfState():
|
@@ -120,14 +120,6 @@ class IfState():
|
|
120
120
|
|
121
121
|
self._update(self.root_netns, ifstates)
|
122
122
|
if 'namespaces' in ifstates:
|
123
|
-
# require to called from the root netns
|
124
|
-
try:
|
125
|
-
# assume init runs in the root netns
|
126
|
-
if os.readlink('/proc/1/ns/net') != os.readlink(f'/proc/{os.getpid()}/ns/net'):
|
127
|
-
raise NetNSNotRoot()
|
128
|
-
except OSError as ex:
|
129
|
-
logger.debug(f'root netns test: {ex}')
|
130
|
-
pass
|
131
123
|
self.namespaces = {}
|
132
124
|
self.new_namespaces = []
|
133
125
|
for netns_name, netns_ifstates in ifstates['namespaces'].items():
|
libifstate/link/base.py
CHANGED
libifstate/parser/base.py
CHANGED
libifstate/parser/yaml.py
CHANGED
libifstate/routing/__init__.py
CHANGED
@@ -61,7 +61,7 @@ class RTLookup():
|
|
61
61
|
self.str2id = {}
|
62
62
|
self.id2str = {}
|
63
63
|
|
64
|
-
for basedir in ['/usr/lib/iproute2', '/etc/iproute2']:
|
64
|
+
for basedir in ['/usr/share/iproute2', '/usr/lib/iproute2', '/etc/iproute2']:
|
65
65
|
fn = os.path.join(basedir, name)
|
66
66
|
try:
|
67
67
|
with open(fn, 'r') as fp:
|
@@ -157,7 +157,21 @@ class Tables(collections.abc.Mapping):
|
|
157
157
|
rt['oif'] = route['dev']
|
158
158
|
|
159
159
|
if 'via' in route:
|
160
|
-
|
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
|
+
}
|
161
175
|
|
162
176
|
if 'src' in route:
|
163
177
|
rt['prefsrc'] = route['src']
|
@@ -219,6 +233,10 @@ class Tables(collections.abc.Mapping):
|
|
219
233
|
via = route.get_attr('RTA_GATEWAY')
|
220
234
|
if via:
|
221
235
|
rt['via'] = via
|
236
|
+
else:
|
237
|
+
via = route.get_attr('RTA_VIA')
|
238
|
+
if via and 'addr' in via:
|
239
|
+
rt['via'] = via['addr']
|
222
240
|
|
223
241
|
realm = route.get_attr('RTA_FLOW')
|
224
242
|
if realm:
|
@@ -280,6 +298,10 @@ class Tables(collections.abc.Mapping):
|
|
280
298
|
if not gateway is None:
|
281
299
|
rt['gateway'] = str(ip_address(gateway))
|
282
300
|
|
301
|
+
via = route.get_attr('RTA_VIA')
|
302
|
+
if not via is None:
|
303
|
+
rt['via'] = via
|
304
|
+
|
283
305
|
metric = route.get_attr('RTA_PRIORITY')
|
284
306
|
if not metric is None:
|
285
307
|
rt['metric'] = metric
|
@@ -307,7 +329,7 @@ class Tables(collections.abc.Mapping):
|
|
307
329
|
|
308
330
|
kroutes = self.kernel_routes(table)
|
309
331
|
|
310
|
-
for route in croutes:
|
332
|
+
for route in sorted(croutes, key=lambda x: [str(x.get('gateway', x.get('via', ''))), x['dst']]):
|
311
333
|
if 'oif' in route and type(route['oif']) == str:
|
312
334
|
oif = next(
|
313
335
|
iter(self.netns.ipr.link_lookup(ifname=route['oif'])), None)
|
libifstate/util.py
CHANGED
@@ -23,6 +23,7 @@ import struct
|
|
23
23
|
import array
|
24
24
|
import struct
|
25
25
|
import typing
|
26
|
+
import os
|
26
27
|
|
27
28
|
# ethtool helper
|
28
29
|
ETHTOOL_GDRVINFO = 0x00000003 # Get driver info
|
@@ -64,6 +65,23 @@ class IPRouteExt(IPRoute):
|
|
64
65
|
|
65
66
|
self.__sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
66
67
|
|
68
|
+
|
69
|
+
|
70
|
+
# def fork_before():
|
71
|
+
# import sys
|
72
|
+
# print(f"FORK[{os.getpid()}] before", file=sys.stderr)
|
73
|
+
|
74
|
+
# def fork_parent():
|
75
|
+
# import sys
|
76
|
+
# print(f"FORK[{os.getpid()}] parent", file=sys.stderr)
|
77
|
+
|
78
|
+
# def fork_child():
|
79
|
+
# import sys
|
80
|
+
# print(f"FORK[{os.getpid()}] after", file=sys.stderr)
|
81
|
+
|
82
|
+
# os.register_at_fork(before=fork_before, after_in_parent=fork_parent, after_in_child=fork_child)
|
83
|
+
|
84
|
+
|
67
85
|
def del_filter_by_info(self, index=0, handle=0, info=0, parent=0):
|
68
86
|
msg = tcmsg()
|
69
87
|
msg['index'] = index
|
@@ -173,7 +191,7 @@ class IPRouteExt(IPRoute):
|
|
173
191
|
try:
|
174
192
|
return next(iter(self.get_links(*argv, **kwarg)), None)
|
175
193
|
except Exception as err:
|
176
|
-
if not isinstance(err, netlinkerror_classes):
|
194
|
+
if not isinstance(err, libifstate.exception.netlinkerror_classes):
|
177
195
|
raise
|
178
196
|
|
179
197
|
return None
|
schema/ifstate.conf.schema.json
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
"$id": "https://ifstate.net/schema/ifstate.conf.schema.json",
|
3
3
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
4
4
|
"title": "ifstate.conf",
|
5
|
-
"description": "IfState 1.
|
5
|
+
"description": "IfState 1.13 Configuration Schema",
|
6
6
|
"type": "object",
|
7
7
|
"required": [
|
8
8
|
"interfaces"
|
@@ -2318,8 +2318,10 @@
|
|
2318
2318
|
{
|
2319
2319
|
"description": "Virtual Routing and Forwarding device",
|
2320
2320
|
"required": [
|
2321
|
-
"kind"
|
2321
|
+
"kind",
|
2322
|
+
"vrf_table"
|
2322
2323
|
],
|
2324
|
+
"additionalProperties": false,
|
2323
2325
|
"properties": {
|
2324
2326
|
"kind": {
|
2325
2327
|
"const": "vrf",
|
@@ -2343,6 +2345,15 @@
|
|
2343
2345
|
"txqlen": {
|
2344
2346
|
"$ref": "#/$defs/iface-link_txqlen"
|
2345
2347
|
},
|
2348
|
+
"vrf_table": {
|
2349
|
+
"description": "routing table associated with the VRF device",
|
2350
|
+
"type": [
|
2351
|
+
"integer",
|
2352
|
+
"string"
|
2353
|
+
],
|
2354
|
+
"minimum": 1,
|
2355
|
+
"maximum": 4294967295
|
2356
|
+
},
|
2346
2357
|
"ifalias": {
|
2347
2358
|
"$ref": "#/$defs/iface-link_ifalias"
|
2348
2359
|
}
|
@@ -3015,6 +3026,32 @@
|
|
3015
3026
|
"description": "specifies the maximum number of FDB entries (0: none)",
|
3016
3027
|
"default": 0
|
3017
3028
|
},
|
3029
|
+
"vxlan_port": {
|
3030
|
+
"description": "specifies the UDP destination port to communicate to the remote VXLAN tunnel endpoint",
|
3031
|
+
"type": "integer",
|
3032
|
+
"minimum": 0,
|
3033
|
+
"maximum": 65535
|
3034
|
+
},
|
3035
|
+
"vxlan_port_range": {
|
3036
|
+
"description": "specifies the range of port numbers to use as UDP source ports to communicate to the remote VXLAN tunnel endpoint",
|
3037
|
+
"required": [
|
3038
|
+
"low",
|
3039
|
+
"high"
|
3040
|
+
],
|
3041
|
+
"additionalProperties": false,
|
3042
|
+
"properties": {
|
3043
|
+
"low": {
|
3044
|
+
"type": "integer",
|
3045
|
+
"minimum": 0,
|
3046
|
+
"maximum": 65535
|
3047
|
+
},
|
3048
|
+
"high": {
|
3049
|
+
"type": "integer",
|
3050
|
+
"minimum": 0,
|
3051
|
+
"maximum": 65535
|
3052
|
+
}
|
3053
|
+
}
|
3054
|
+
},
|
3018
3055
|
"vxlan_proxy": {
|
3019
3056
|
"type": "integer",
|
3020
3057
|
"minimum": 0,
|
@@ -3055,15 +3092,13 @@
|
|
3055
3092
|
},
|
3056
3093
|
"vxlan_local": {
|
3057
3094
|
"type": "string",
|
3058
|
-
"description": "tunnel source
|
3059
|
-
"
|
3060
|
-
|
3061
|
-
|
3062
|
-
|
3063
|
-
|
3064
|
-
|
3065
|
-
}
|
3066
|
-
]
|
3095
|
+
"description": "tunnel source IPv4 address",
|
3096
|
+
"format": "ipv4"
|
3097
|
+
},
|
3098
|
+
"vxlan_local6": {
|
3099
|
+
"type": "string",
|
3100
|
+
"description": "tunnel source IPv6 address",
|
3101
|
+
"format": "ipv6"
|
3067
3102
|
},
|
3068
3103
|
"vxlan_group": {
|
3069
3104
|
"type": "string",
|
@@ -3103,10 +3138,32 @@
|
|
3103
3138
|
"vxlan_group"
|
3104
3139
|
]
|
3105
3140
|
},
|
3141
|
+
{
|
3142
|
+
"required": [
|
3143
|
+
"vxlan_local"
|
3144
|
+
]
|
3145
|
+
},
|
3146
|
+
{
|
3147
|
+
"required": [
|
3148
|
+
"vxlan_local",
|
3149
|
+
"vxlan_group"
|
3150
|
+
]
|
3151
|
+
},
|
3106
3152
|
{
|
3107
3153
|
"required": [
|
3108
3154
|
"vxlan_group6"
|
3109
3155
|
]
|
3156
|
+
},
|
3157
|
+
{
|
3158
|
+
"required": [
|
3159
|
+
"vxlan_local6"
|
3160
|
+
]
|
3161
|
+
},
|
3162
|
+
{
|
3163
|
+
"required": [
|
3164
|
+
"vxlan_group6",
|
3165
|
+
"vxlan_local6"
|
3166
|
+
]
|
3110
3167
|
}
|
3111
3168
|
]
|
3112
3169
|
},
|
@@ -3114,7 +3171,9 @@
|
|
3114
3171
|
"not": {
|
3115
3172
|
"required": [
|
3116
3173
|
"vxlan_group",
|
3117
|
-
"vxlan_group6"
|
3174
|
+
"vxlan_group6",
|
3175
|
+
"vxlan_local",
|
3176
|
+
"vxlan_local6"
|
3118
3177
|
]
|
3119
3178
|
}
|
3120
3179
|
}
|
File without changes
|
File without changes
|
File without changes
|