ifstate 1.12.0__tar.gz → 1.13.0__tar.gz

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.
Files changed (43) hide show
  1. {ifstate-1.12.0 → ifstate-1.13.0}/PKG-INFO +1 -1
  2. {ifstate-1.12.0 → ifstate-1.13.0}/ifstate/ifstate.py +39 -27
  3. ifstate-1.13.0/ifstate/vrrp.py +166 -0
  4. {ifstate-1.12.0 → ifstate-1.13.0}/ifstate.egg-info/PKG-INFO +1 -1
  5. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/__init__.py +2 -10
  6. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/parser/base.py +7 -0
  7. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/parser/yaml.py +3 -0
  8. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/routing/__init__.py +2 -2
  9. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/util.py +19 -1
  10. {ifstate-1.12.0 → ifstate-1.13.0}/schema/ifstate.conf.schema.json +27 -1
  11. ifstate-1.12.0/ifstate/vrrp.py +0 -113
  12. {ifstate-1.12.0 → ifstate-1.13.0}/LICENSE +0 -0
  13. {ifstate-1.12.0 → ifstate-1.13.0}/README.md +0 -0
  14. {ifstate-1.12.0 → ifstate-1.13.0}/ifstate/__init__.py +0 -0
  15. {ifstate-1.12.0 → ifstate-1.13.0}/ifstate/shell.py +0 -0
  16. {ifstate-1.12.0 → ifstate-1.13.0}/ifstate.egg-info/SOURCES.txt +0 -0
  17. {ifstate-1.12.0 → ifstate-1.13.0}/ifstate.egg-info/dependency_links.txt +0 -0
  18. {ifstate-1.12.0 → ifstate-1.13.0}/ifstate.egg-info/entry_points.txt +0 -0
  19. {ifstate-1.12.0 → ifstate-1.13.0}/ifstate.egg-info/requires.txt +0 -0
  20. {ifstate-1.12.0 → ifstate-1.13.0}/ifstate.egg-info/top_level.txt +0 -0
  21. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/address/__init__.py +0 -0
  22. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/bpf/__init__.py +0 -0
  23. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/bpf/ctypes.py +0 -0
  24. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/bpf/map.py +0 -0
  25. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/brport/__init__.py +0 -0
  26. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/exception.py +0 -0
  27. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/fdb/__init__.py +0 -0
  28. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/link/__init__.py +0 -0
  29. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/link/base.py +0 -0
  30. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/link/dsa.py +0 -0
  31. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/link/physical.py +0 -0
  32. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/link/tun.py +0 -0
  33. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/link/veth.py +0 -0
  34. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/log.py +0 -0
  35. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/neighbour/__init__.py +0 -0
  36. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/netns/__init__.py +0 -0
  37. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/parser/__init__.py +0 -0
  38. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/sysctl/__init__.py +0 -0
  39. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/tc/__init__.py +0 -0
  40. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/wireguard/__init__.py +0 -0
  41. {ifstate-1.12.0 → ifstate-1.13.0}/libifstate/xdp/__init__.py +0 -0
  42. {ifstate-1.12.0 → ifstate-1.13.0}/setup.cfg +0 -0
  43. {ifstate-1.12.0 → ifstate-1.13.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ifstate
3
- Version: 1.12.0
3
+ Version: 1.13.0
4
4
  Summary: Manage host interface settings in a declarative manner
5
5
  Home-page: https://ifstate.net/
6
6
  Author: Thomas Liske
@@ -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" : "dry run update the network config",
31
- "APPLY" : "update the network config",
32
- "SHOW" : "show running network config",
33
- "SHOWALL" : "show running network config (more settings)",
34
- "VRRP" : "run as keepalived notify script",
35
- "VRRP_FIFO": "run as keepalived notify_fifo_script",
36
- "SHELL" : "launch interactive python shell (pyroute2)",
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(ifstates, self.soft_schema)
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, lvl)
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)
@@ -0,0 +1,166 @@
1
+ from libifstate.util import logger, IfStateLogging
2
+
3
+ import atexit
4
+ from copy import deepcopy
5
+ import logging
6
+ import multiprocessing as mp
7
+ import threading
8
+ import os
9
+ import re
10
+ from setproctitle import setproctitle
11
+ import signal
12
+ import subprocess
13
+ import sys
14
+
15
+ class VrrpFifoProcess():
16
+ '''
17
+ Process for vrrp group/instance configuration.
18
+ '''
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()
27
+
28
+ def vrrp_update(self, vrrp_state):
29
+ self.state_queue.put(vrrp_state)
30
+
31
+ def dequeue(self):
32
+ while True:
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()
39
+ return
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
+
55
+
56
+ class VrrpStates():
57
+ '''
58
+ Tracks processes and states for vrrp groups/instances.
59
+ '''
60
+ def __init__(self, ifs_config):
61
+ self.ifs_config = ifs_config
62
+ self.processes = {}
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')
72
+
73
+ def update(self, vrrp_type, vrrp_name, vrrp_state):
74
+ '''
75
+ Updates the state for a group/instance. A new VrrpFifoProcess is spawned on-demand
76
+ if a group/instance is called the first time.
77
+ '''
78
+ key = (vrrp_type, vrrp_name)
79
+ if not key in self.processes:
80
+ worker_args = self.worker_args + key
81
+ self.processes[key] = VrrpFifoProcess(worker_args)
82
+ self.processes[key].start()
83
+
84
+ self.states[key] = vrrp_state
85
+ self.processes[key].vrrp_update(vrrp_state)
86
+
87
+ def reconfigure(self, *argv):
88
+ '''
89
+ Reconfigure all known groups/instances using their last known state by spawning
90
+ new worker Processes.
91
+ '''
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
+
101
+ for key, state in self.states.items():
102
+ worker_args = self.worker_args + key
103
+ self.processes[key].vrrp_update(None)
104
+ self.processes[key] = VrrpFifoProcess(worker_args)
105
+ self.processes[key].start()
106
+ self.processes[key].vrrp_update(self.states[key])
107
+
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)
120
+
121
+ signal.signal(signal.SIGHUP, vrrp_states.reconfigure)
122
+ signal.signal(signal.SIGPIPE, signal.SIG_IGN)
123
+ signal.signal(signal.SIGTERM, vrrp_states.cleanup_run)
124
+
125
+ try:
126
+ os.makedirs("/run/libifstate/vrrp", exist_ok=True)
127
+
128
+ with open(vrrp_states.pid_file, "w", encoding="utf-8") as fh:
129
+ fh.write(args_fifo)
130
+ atexit.register(vrrp_states.cleanup_run)
131
+
132
+ ifs_config.dump_config(vrrp_states.cfg_file)
133
+ except IOError as err:
134
+ logger.exception(f'failed to write pid file {vrrp_states.pid_file}: {err}')
135
+
136
+ try:
137
+ status_pattern = re.compile(
138
+ r'(group|instance) "([^"]+)" (unknown|fault|backup|master|stop)( \d+)?$', re.IGNORECASE)
139
+
140
+ with open(args_fifo) as fifo:
141
+ logger.debug("entering fifo loop...")
142
+ for line in fifo:
143
+ m = status_pattern.match(line.strip())
144
+ if m:
145
+ vrrp_type = m.group(1)
146
+ vrrp_name = m.group(2)
147
+ vrrp_state = m.group(3)
148
+
149
+ vrrp_states.update(vrrp_type, vrrp_name, vrrp_state)
150
+ else:
151
+ logger.warning(f'failed to parse fifo input: {line.strip()}')
152
+ mp.active_children()
153
+ finally:
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ifstate
3
- Version: 1.12.0
3
+ Version: 1.13.0
4
4
  Summary: Manage host interface settings in a declarative manner
5
5
  Home-page: https://ifstate.net/
6
6
  Author: Thomas Liske
@@ -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, NetNSNotRoot
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.12.0"
51
+ __version__ = "1.13.0"
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():
@@ -150,3 +150,10 @@ class Parser(ABC):
150
150
  del(cfg["ignore"][k])
151
151
 
152
152
  return cfg
153
+
154
+ @abstractmethod
155
+ def dump(self, stream):
156
+ """
157
+ Dump the parsed configuration into a io stream.
158
+ """
159
+ pass
@@ -42,3 +42,6 @@ class YamlParser(Parser):
42
42
  raise ParserOpenError(ex)
43
43
  except yaml.parser.ParserError as ex:
44
44
  raise ParserParseError(ex)
45
+
46
+ def dump(self, stream):
47
+ yaml.dump(self.config(), stream)
@@ -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:
@@ -307,7 +307,7 @@ class Tables(collections.abc.Mapping):
307
307
 
308
308
  kroutes = self.kernel_routes(table)
309
309
 
310
- for route in croutes:
310
+ for route in sorted(croutes, key=lambda x: [x.get('via', ''), x['dst']]):
311
311
  if 'oif' in route and type(route['oif']) == str:
312
312
  oif = next(
313
313
  iter(self.netns.ipr.link_lookup(ifname=route['oif'])), None)
@@ -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
@@ -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.11.9 Configuration Schema",
5
+ "description": "IfState 1.13 Configuration Schema",
6
6
  "type": "object",
7
7
  "required": [
8
8
  "interfaces"
@@ -3015,6 +3015,32 @@
3015
3015
  "description": "specifies the maximum number of FDB entries (0: none)",
3016
3016
  "default": 0
3017
3017
  },
3018
+ "vxlan_port": {
3019
+ "description": "specifies the UDP destination port to communicate to the remote VXLAN tunnel endpoint",
3020
+ "type": "integer",
3021
+ "minimum": 0,
3022
+ "maximum": 65535
3023
+ },
3024
+ "vxlan_port_range": {
3025
+ "description": "specifies the range of port numbers to use as UDP source ports to communicate to the remote VXLAN tunnel endpoint",
3026
+ "required": [
3027
+ "low",
3028
+ "high"
3029
+ ],
3030
+ "additionalProperties": false,
3031
+ "properties": {
3032
+ "low": {
3033
+ "type": "integer",
3034
+ "minimum": 0,
3035
+ "maximum": 65535
3036
+ },
3037
+ "high": {
3038
+ "type": "integer",
3039
+ "minimum": 0,
3040
+ "maximum": 65535
3041
+ }
3042
+ }
3043
+ },
3018
3044
  "vxlan_proxy": {
3019
3045
  "type": "integer",
3020
3046
  "minimum": 0,
@@ -1,113 +0,0 @@
1
- from libifstate.util import logger, IfStateLogging
2
-
3
- from copy import deepcopy
4
- import logging
5
- import multiprocessing as mp
6
- import os
7
- import re
8
- from setproctitle import setproctitle
9
- import signal
10
-
11
- class VrrpFifoProcess(mp.Process):
12
- '''
13
- Process for vrrp group/instance configuration.
14
- '''
15
- def __init__(self, vrrp_type, vrrp_name, ifstate, log_level):
16
- self.vrrp_type = vrrp_type
17
- self.vrrp_name = vrrp_name
18
- self.ifstate = ifstate
19
- self.log_level = log_level
20
- self.queue = mp.Queue()
21
- super().__init__(target=self.vrrp_worker, name=f'ifstate-vrrp-fifo|{vrrp_type}.{vrrp_name}', daemon=True)
22
-
23
- def vrrp_update(self, vrrp_state):
24
- self.queue.put(vrrp_state)
25
-
26
- def vrrp_worker(self):
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
- while True:
32
- vrrp_state = self.queue.get()
33
- if vrrp_state is None:
34
- logger.info('terminating')
35
- return
36
- else:
37
- ifstate = deepcopy(self.ifstate)
38
- ifstate.apply(self.vrrp_type, self.vrrp_name, vrrp_state)
39
-
40
- class VrrpStates():
41
- '''
42
- Tracks processes and states for vrrp groups/instances.
43
- '''
44
- def __init__(self, ifstate, log_level):
45
- self.ifstate = ifstate
46
- self.log_level = log_level
47
- self.processes = {}
48
- self.states = {}
49
-
50
- def update(self, vrrp_type, vrrp_name, vrrp_state):
51
- '''
52
- Updates the state for a group/instance. A new VrrpFifoProcess is spawned on-demand
53
- if a group/instance is called the first time.
54
- '''
55
- key = (vrrp_type, vrrp_name)
56
- if not key in self.processes:
57
- self.processes[key] = VrrpFifoProcess(vrrp_type, vrrp_name, self.ifstate, self.log_level)
58
- self.processes[key].start()
59
-
60
- self.states[key] = vrrp_state
61
- self.processes[key].vrrp_update(vrrp_state)
62
-
63
- def reconfigure(self, ifstate):
64
- '''
65
- Reconfigure all known groups/instances using their last known state by spawning
66
- new worker Processes.
67
- '''
68
- self.ifstate = ifstate
69
- for key, state in self.states.items():
70
- self.processes[key].vrrp_update(None)
71
- self.processes[key] = VrrpFifoProcess(*key, ifstate, self.log_level)
72
- self.processes[key].start()
73
- self.processes[key].vrrp_update(self.states[key])
74
-
75
- def vrrp_fifo(args_fifo, ifs_config, log_level):
76
- vrrp_states = VrrpStates(ifs_config.ifs, log_level)
77
- ifs_config.set_callback(vrrp_states.reconfigure)
78
-
79
- signal.signal(signal.SIGHUP, ifs_config.sighup_handler)
80
- signal.signal(signal.SIGPIPE, signal.SIG_IGN)
81
- signal.signal(signal.SIGTERM, signal.SIG_IGN)
82
-
83
- pid_file = f"/run/libifstate/vrrp/{os.getpid()}.pid"
84
- try:
85
- os.makedirs("/run/libifstate/vrrp", exist_ok=True)
86
-
87
- with open(pid_file, "w", encoding="utf-8") as fh:
88
- fh.write(args_fifo)
89
- except IOError as err:
90
- logger.exception(f'failed to write pid file {pid_file}: {err}')
91
-
92
- try:
93
- status_pattern = re.compile(
94
- r'(group|instance) "([^"]+)" (unknown|fault|backup|master)( \d+)?$', re.IGNORECASE)
95
-
96
- with open(args_fifo) as fifo:
97
- logger.debug("entering fifo loop...")
98
- for line in fifo:
99
- m = status_pattern.match(line.strip())
100
- if m:
101
- vrrp_type = m.group(1)
102
- vrrp_name = m.group(2)
103
- vrrp_state = m.group(3)
104
-
105
- vrrp_states.update(vrrp_type, vrrp_name, vrrp_state)
106
- else:
107
- logger.warning(f'failed to parse fifo input: {line.strip()}')
108
- mp.active_children()
109
- finally:
110
- try:
111
- os.remove(pid_file)
112
- except IOError:
113
- pass
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes