]>
Commit | Line | Data |
---|---|---|
35681c06 | 1 | #!/usr/bin/env python3 |
904908bc | 2 | # |
d486dd0d | 3 | # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved. |
904908bc RP |
4 | # Author: Roopa Prabhu, roopa@cumulusnetworks.com |
5 | # | |
6 | # utils -- | |
7 | # helper class | |
8 | # | |
a193d8d1 | 9 | |
d40e96ee | 10 | import os |
679e6567 | 11 | import re |
a193d8d1 JF |
12 | import shlex |
13 | import fcntl | |
a4a53f4b | 14 | import signal |
a193d8d1 JF |
15 | import logging |
16 | import subprocess | |
84c47c4f | 17 | import itertools |
a4a53f4b JF |
18 | |
19 | from functools import partial | |
66eb9ce3 | 20 | from ipaddress import IPv4Address |
a4a53f4b | 21 | |
d486dd0d JF |
22 | try: |
23 | from ifupdown2.ifupdown.iface import * | |
24 | ||
70a6640c | 25 | import ifupdown2.ifupdown.policymanager as policymanager |
d486dd0d | 26 | import ifupdown2.ifupdown.ifupdownflags as ifupdownflags |
bd441a51 | 27 | except (ImportError, ModuleNotFoundError): |
d486dd0d JF |
28 | from ifupdown.iface import * |
29 | ||
70a6640c | 30 | import ifupdown.policymanager as policymanager |
d486dd0d JF |
31 | import ifupdown.ifupdownflags as ifupdownflags |
32 | ||
22b49c28 | 33 | |
a4a53f4b JF |
34 | def signal_handler_f(ps, sig, frame): |
35 | if ps: | |
36 | ps.send_signal(sig) | |
37 | if sig == signal.SIGINT: | |
38 | raise KeyboardInterrupt | |
14dc390d | 39 | |
40 | class utils(): | |
a193d8d1 JF |
41 | logger = logging.getLogger('ifupdown') |
42 | DEVNULL = open(os.devnull, 'w') | |
70a6640c | 43 | vlan_aware_bridge_address_support = None |
14dc390d | 44 | |
7f0310a7 RP |
45 | vni_max = 16777215 |
46 | ||
594fb088 JF |
47 | _string_values = { |
48 | "on": True, | |
49 | "yes": True, | |
50 | "1": True, | |
d486dd0d | 51 | "fast": True, |
594fb088 JF |
52 | "off": False, |
53 | "no": False, | |
54 | "0": False, | |
03813675 JF |
55 | "slow": False, |
56 | True: True, | |
57 | False: False | |
594fb088 JF |
58 | } |
59 | ||
60 | _binary_bool = { | |
61 | True: "1", | |
62 | False: "0", | |
63 | } | |
64 | ||
65 | _yesno_bool = { | |
66 | True: 'yes', | |
67 | False: 'no' | |
68 | } | |
69 | ||
70 | _onoff_bool = { | |
71 | 'yes': 'on', | |
72 | 'no': 'off' | |
73 | } | |
74 | ||
d486dd0d JF |
75 | _onoff_onezero = { |
76 | '1' : 'on', | |
77 | '0' : 'off' | |
78 | } | |
79 | ||
80 | _yesno_onezero = { | |
81 | '1' : 'yes', | |
82 | '0' : 'no' | |
83 | } | |
84 | ||
85 | """ | |
86 | Set debian path as default path for all the commands. | |
87 | If command not present in debian path, search for the | |
88 | commands in the other system directories. | |
89 | This search is carried out to handle different locations | |
90 | on different distros. | |
91 | If the command is not found in any of the system | |
92 | directories, command execution will fail because we have | |
93 | set default path same as debian path. | |
94 | """ | |
95 | bridge_cmd = '/sbin/bridge' | |
96 | ip_cmd = '/bin/ip' | |
97 | brctl_cmd = '/sbin/brctl' | |
98 | pidof_cmd = '/bin/pidof' | |
99 | service_cmd = '/usr/sbin/service' | |
100 | sysctl_cmd = '/sbin/sysctl' | |
101 | modprobe_cmd = '/sbin/modprobe' | |
102 | pstree_cmd = '/usr/bin/pstree' | |
103 | ss_cmd = '/bin/ss' | |
104 | vrrpd_cmd = '/usr/sbin/vrrpd' | |
105 | ifplugd_cmd = '/usr/sbin/ifplugd' | |
106 | mstpctl_cmd = '/sbin/mstpctl' | |
107 | ethtool_cmd = '/sbin/ethtool' | |
108 | systemctl_cmd = '/bin/systemctl' | |
109 | dpkg_cmd = '/usr/bin/dpkg' | |
110 | ||
223ba5af | 111 | logger.info("utils init command paths") |
d486dd0d JF |
112 | for cmd in ['bridge', |
113 | 'ip', | |
114 | 'brctl', | |
115 | 'pidof', | |
116 | 'service', | |
117 | 'sysctl', | |
118 | 'modprobe', | |
119 | 'pstree', | |
120 | 'ss', | |
121 | 'vrrpd', | |
122 | 'ifplugd', | |
123 | 'mstpctl', | |
124 | 'ethtool', | |
125 | 'systemctl', | |
126 | 'dpkg' | |
127 | ]: | |
128 | if os.path.exists(vars()[cmd + '_cmd']): | |
129 | continue | |
130 | for path in ['/bin/', | |
131 | '/sbin/', | |
132 | '/usr/bin/', | |
133 | '/usr/sbin/',]: | |
134 | if os.path.exists(path + cmd): | |
135 | vars()[cmd + '_cmd'] = path + cmd | |
136 | else: | |
137 | logger.debug('warning: path %s not found: %s won\'t be usable' % (path + cmd, cmd)) | |
138 | ||
3b01ed76 | 139 | mac_translate_tab = str.maketrans(":.-,", " ") |
223ba5af JF |
140 | |
141 | @classmethod | |
142 | def mac_str_to_int(cls, hw_address): | |
143 | mac = 0 | |
144 | if hw_address: | |
3b01ed76 | 145 | pass |
223ba5af JF |
146 | for i in hw_address.translate(cls.mac_translate_tab).split(): |
147 | mac = mac << 8 | |
148 | mac += int(i, 16) | |
149 | return mac | |
150 | ||
d486dd0d JF |
151 | @staticmethod |
152 | def get_onff_from_onezero(value): | |
153 | if value in utils._onoff_onezero: | |
154 | return utils._onoff_onezero[value] | |
155 | return value | |
156 | ||
157 | @staticmethod | |
158 | def get_yesno_from_onezero(value): | |
159 | if value in utils._yesno_onezero: | |
160 | return utils._yesno_onezero[value] | |
161 | return value | |
162 | ||
594fb088 JF |
163 | @staticmethod |
164 | def get_onoff_bool(value): | |
165 | if value in utils._onoff_bool: | |
166 | return utils._onoff_bool[value] | |
167 | return value | |
168 | ||
169 | @staticmethod | |
70a6640c JF |
170 | def get_boolean_from_string(value, default=False): |
171 | return utils._string_values.get(value, default) | |
594fb088 JF |
172 | |
173 | @staticmethod | |
174 | def get_yesno_boolean(bool): | |
175 | return utils._yesno_bool[bool] | |
176 | ||
177 | @staticmethod | |
178 | def boolean_support_binary(value): | |
179 | return utils._binary_bool[utils.get_boolean_from_string(value)] | |
180 | ||
181 | @staticmethod | |
182 | def is_binary_bool(value): | |
183 | return value == '0' or value == '1' | |
184 | ||
185 | @staticmethod | |
186 | def support_yesno_attrs(attrsdict, attrslist, ifaceobj=None): | |
187 | if ifaceobj: | |
188 | for attr in attrslist: | |
189 | value = ifaceobj.get_attr_value_first(attr) | |
190 | if value and not utils.is_binary_bool(value): | |
191 | if attr in attrsdict: | |
192 | bool = utils.get_boolean_from_string(attrsdict[attr]) | |
193 | attrsdict[attr] = utils.get_yesno_boolean(bool) | |
194 | else: | |
195 | for attr in attrslist: | |
196 | if attr in attrsdict: | |
197 | attrsdict[attr] = utils.boolean_support_binary(attrsdict[attr]) | |
198 | ||
d486dd0d JF |
199 | @staticmethod |
200 | def get_int_from_boolean_and_string(value): | |
201 | try: | |
202 | return int(value) | |
3218f49d | 203 | except Exception: |
d486dd0d JF |
204 | return int(utils.get_boolean_from_string(value)) |
205 | ||
206 | @staticmethod | |
207 | def strip_hwaddress(hwaddress): | |
208 | if hwaddress and hwaddress.startswith("ether"): | |
209 | hwaddress = hwaddress[5:].strip() | |
210 | return hwaddress.lower() if hwaddress else hwaddress | |
211 | # we need to "normalize" the user provided MAC so it can match with | |
212 | # what we have in the cache (data retrieved via a netlink dump by | |
213 | # nlmanager). nlmanager return all macs in lower-case | |
214 | ||
14dc390d | 215 | @classmethod |
216 | def importName(cls, modulename, name): | |
217 | """ Import a named object """ | |
218 | try: | |
219 | module = __import__(modulename, globals(), locals(), [name]) | |
220 | except ImportError: | |
221 | return None | |
222 | return getattr(module, name) | |
d40e96ee | 223 | |
224 | @classmethod | |
225 | def lockFile(cls, lockfile): | |
226 | try: | |
227 | fp = os.open(lockfile, os.O_CREAT | os.O_TRUNC | os.O_WRONLY) | |
228 | fcntl.flock(fp, fcntl.LOCK_EX | fcntl.LOCK_NB) | |
a193d8d1 | 229 | fcntl.fcntl(fp, fcntl.F_SETFD, fcntl.FD_CLOEXEC) |
d40e96ee | 230 | except IOError: |
231 | return False | |
232 | return True | |
233 | ||
679e6567 RP |
234 | @classmethod |
235 | def parse_iface_range(cls, name): | |
eba4da6e RP |
236 | # eg: swp1.[2-100] |
237 | # return (prefix, range-start, range-end) | |
238 | # eg return ("swp1.", 1, 20, ".100") | |
239 | range_match = re.match("^([\w]+)\[([\d]+)-([\d]+)\]([\.\w]+)", name) | |
679e6567 RP |
240 | if range_match: |
241 | range_groups = range_match.groups() | |
242 | if range_groups[1] and range_groups[2]: | |
243 | return (range_groups[0], int(range_groups[1], 10), | |
eba4da6e RP |
244 | int(range_groups[2], 10), range_groups[3]) |
245 | else: | |
246 | # eg: swp[1-20].100 | |
247 | # return (prefix, range-start, range-end, suffix) | |
248 | # eg return ("swp", 1, 20, ".100") | |
249 | range_match = re.match("^([\w\.]+)\[([\d]+)-([\d]+)\]", name) | |
250 | if range_match: | |
251 | range_groups = range_match.groups() | |
252 | if range_groups[1] and range_groups[2]: | |
253 | return (range_groups[0], int(range_groups[1], 10), | |
254 | int(range_groups[2], 10)) | |
679e6567 RP |
255 | return None |
256 | ||
257 | @classmethod | |
258 | def expand_iface_range(cls, name): | |
259 | ifacenames = [] | |
eba4da6e RP |
260 | irange = cls.parse_iface_range(name) |
261 | if irange: | |
262 | if len(irange) == 3: | |
263 | # eg swp1.[2-4], r = "swp1.", 2, 4) | |
264 | for i in range(irange[1], irange[2]): | |
265 | ifacenames.append('%s%d' %(irange[0], i)) | |
266 | elif len(irange) == 4: | |
267 | for i in range(irange[1], irange[2]): | |
268 | # eg swp[2-4].100, r = ("swp", 2, 4, ".100") | |
269 | ifacenames.append('%s%d%s' %(irange[0], i, irange[3])) | |
679e6567 RP |
270 | return ifacenames |
271 | ||
eba4da6e RP |
272 | @classmethod |
273 | def is_ifname_range(cls, name): | |
274 | if '[' in name or ']' in name: | |
275 | return True | |
276 | return False | |
277 | ||
d3ad131e ST |
278 | @classmethod |
279 | def check_ifname_size_invalid(cls, name=''): | |
280 | """ IFNAMSIZ in include/linux/if.h is 16 so we check this """ | |
281 | IFNAMSIZ = 16 | |
282 | if len(name) > IFNAMSIZ - 1: | |
283 | return True | |
284 | else: | |
285 | return False | |
d40e96ee | 286 | |
a4a53f4b JF |
287 | @classmethod |
288 | def enable_subprocess_signal_forwarding(cls, ps, sig): | |
289 | signal.signal(sig, partial(signal_handler_f, ps)) | |
290 | ||
291 | @classmethod | |
292 | def disable_subprocess_signal_forwarding(cls, sig): | |
293 | signal.signal(sig, signal.SIG_DFL) | |
294 | ||
a193d8d1 JF |
295 | @classmethod |
296 | def _log_command_exec(cls, cmd, stdin): | |
ca45cd9e | 297 | dry_run = "DRY-RUN: " if ifupdownflags.flags.DRYRUN else "" |
a193d8d1 | 298 | if stdin: |
ca45cd9e | 299 | cls.logger.info('%sexecuting %s [%s]' % (dry_run, cmd, stdin)) |
a193d8d1 | 300 | else: |
ca45cd9e | 301 | cls.logger.info('%sexecuting %s' % (dry_run, cmd)) |
a193d8d1 JF |
302 | |
303 | @classmethod | |
304 | def _format_error(cls, cmd, cmd_returncode, cmd_output, stdin): | |
305 | if type(cmd) is list: | |
306 | cmd = ' '.join(cmd) | |
307 | if stdin: | |
308 | cmd = '%s [%s]' % (cmd, stdin) | |
309 | if cmd_output: | |
310 | return 'cmd \'%s\' failed: returned %d (%s)' % \ | |
311 | (cmd, cmd_returncode, cmd_output) | |
312 | else: | |
313 | return 'cmd \'%s\' failed: returned %d' % (cmd, cmd_returncode) | |
314 | ||
22b49c28 JF |
315 | @classmethod |
316 | def is_addr_ip_allowed_on(cls, ifaceobj, syntax_check=False): | |
70a6640c JF |
317 | if cls.vlan_aware_bridge_address_support is None: |
318 | cls.vlan_aware_bridge_address_support = utils.get_boolean_from_string( | |
319 | policymanager.policymanager_api.get_module_globals( | |
320 | module_name='address', | |
321 | attr='vlan_aware_bridge_address_support' | |
322 | ), | |
323 | True | |
324 | ) | |
22b49c28 JF |
325 | msg = ('%s: ignoring ip address. Assigning an IP ' |
326 | 'address is not allowed on' % ifaceobj.name) | |
327 | if (ifaceobj.role & ifaceRole.SLAVE | |
328 | and not (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE)): | |
329 | up = None | |
330 | if ifaceobj.upperifaces: | |
331 | up = ifaceobj.upperifaces[0] | |
332 | msg = ('%s enslaved interfaces. %s' | |
333 | % (msg, ('%s is enslaved to %s' | |
334 | % (ifaceobj.name, up)) if up else '')).strip() | |
335 | if syntax_check: | |
336 | cls.logger.warning(msg) | |
337 | else: | |
338 | cls.logger.info(msg) | |
339 | return False | |
340 | elif (ifaceobj.link_kind & ifaceLinkKind.BRIDGE | |
70a6640c JF |
341 | and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE |
342 | and not cls.vlan_aware_bridge_address_support | |
343 | ): | |
344 | msg = '%s bridge vlan aware interfaces' % msg | |
22b49c28 JF |
345 | if syntax_check: |
346 | cls.logger.warning(msg) | |
347 | else: | |
348 | cls.logger.info(msg) | |
349 | return False | |
350 | return True | |
351 | ||
a193d8d1 JF |
352 | @classmethod |
353 | def _execute_subprocess(cls, cmd, | |
354 | env=None, | |
355 | shell=False, | |
356 | close_fds=False, | |
357 | stdout=True, | |
358 | stdin=None, | |
bdbe0379 | 359 | stderr=subprocess.STDOUT): |
a193d8d1 JF |
360 | """ |
361 | exec's commands using subprocess Popen | |
362 | Args: | |
363 | cmd, should be shlex.split if not shell | |
364 | returns: output | |
365 | ||
366 | Note: close_fds=True is affecting performance (2~3 times slower) | |
367 | """ | |
368 | if ifupdownflags.flags.DRYRUN: | |
369 | return '' | |
370 | ||
371 | cmd_output = None | |
372 | try: | |
373 | ch = subprocess.Popen(cmd, | |
374 | env=env, | |
375 | shell=shell, | |
376 | close_fds=close_fds, | |
377 | stdin=subprocess.PIPE if stdin else None, | |
bdbe0379 | 378 | stdout=subprocess.PIPE if stdout else cls.DEVNULL, |
a193d8d1 JF |
379 | stderr=stderr) |
380 | utils.enable_subprocess_signal_forwarding(ch, signal.SIGINT) | |
381 | if stdout or stdin: | |
6ab519dd | 382 | cmd_output = ch.communicate(input=stdin.encode() if stdin else stdin)[0] |
a193d8d1 JF |
383 | cmd_returncode = ch.wait() |
384 | except Exception as e: | |
bdbe0379 | 385 | raise Exception('cmd \'%s\' failed (%s)' % (' '.join(cmd), str(e))) |
a193d8d1 JF |
386 | finally: |
387 | utils.disable_subprocess_signal_forwarding(signal.SIGINT) | |
e36ad206 | 388 | |
eee38e73 | 389 | cmd_output_string = cmd_output.decode() if cmd_output is not None else cmd_output |
e36ad206 | 390 | |
a193d8d1 JF |
391 | if cmd_returncode != 0: |
392 | raise Exception(cls._format_error(cmd, | |
393 | cmd_returncode, | |
e36ad206 | 394 | cmd_output_string, |
a193d8d1 | 395 | stdin)) |
e36ad206 | 396 | return cmd_output_string |
a193d8d1 JF |
397 | |
398 | @classmethod | |
ac645a1a | 399 | def exec_user_command(cls, cmd, env=None, close_fds=False, stdout=True, |
a193d8d1 JF |
400 | stdin=None, stderr=subprocess.STDOUT): |
401 | cls._log_command_exec(cmd, stdin) | |
402 | return cls._execute_subprocess(cmd, | |
403 | shell=True, | |
ac645a1a | 404 | env=env, |
a193d8d1 JF |
405 | close_fds=close_fds, |
406 | stdout=stdout, | |
407 | stdin=stdin, | |
bdbe0379 | 408 | stderr=stderr) |
a193d8d1 JF |
409 | |
410 | @classmethod | |
411 | def exec_command(cls, cmd, env=None, close_fds=False, stdout=True, | |
412 | stdin=None, stderr=subprocess.STDOUT): | |
413 | cls._log_command_exec(cmd, stdin) | |
414 | return cls._execute_subprocess(shlex.split(cmd), | |
415 | env=env, | |
416 | close_fds=close_fds, | |
417 | stdout=stdout, | |
418 | stdin=stdin, | |
419 | stderr=stderr) | |
420 | ||
421 | @classmethod | |
422 | def exec_commandl(cls, cmdl, env=None, close_fds=False, stdout=True, | |
423 | stdin=None, stderr=subprocess.STDOUT): | |
424 | cls._log_command_exec(' '.join(cmdl), stdin) | |
425 | return cls._execute_subprocess(cmdl, | |
426 | env=env, | |
427 | close_fds=close_fds, | |
428 | stdout=stdout, | |
429 | stdin=stdin, | |
430 | stderr=stderr) | |
431 | ||
84c47c4f RP |
432 | @classmethod |
433 | def ints_to_ranges(cls, ints): | |
434 | for a, b in itertools.groupby(enumerate(ints), lambda x_y: x_y[1] - x_y[0]): | |
435 | b = list(b) | |
436 | yield b[0][1], b[-1][1] | |
437 | ||
438 | @classmethod | |
439 | def ranges_to_ints(cls, rangelist): | |
440 | """ returns expanded list of integers given set of string ranges | |
441 | example: ['1', '2-4', '6'] returns [1, 2, 3, 4, 6] | |
442 | """ | |
443 | result = [] | |
444 | try: | |
445 | for part in rangelist: | |
446 | if '-' in part: | |
447 | a, b = part.split('-') | |
448 | a, b = int(a), int(b) | |
449 | result.extend(list(range(a, b + 1))) | |
450 | else: | |
451 | a = int(part) | |
452 | result.append(a) | |
453 | except Exception: | |
454 | cls.logger.warning('unable to parse vids \'%s\'' %''.join(rangelist)) | |
455 | pass | |
456 | return result | |
457 | ||
458 | @classmethod | |
459 | def compress_into_ranges(cls, ids_ints): | |
460 | return ['%d' %start if start == end else '%d-%d' %(start, end) | |
461 | for start, end in cls.ints_to_ranges(ids_ints)] | |
462 | ||
66eb9ce3 JF |
463 | @classmethod |
464 | def compress_into_ip_ranges(cls, ip_list): | |
465 | return [ | |
466 | "%s" % IPv4Address(start) if start == end else "%s-%s" % (IPv4Address(start), IPv4Address(end)) for | |
467 | start, end in cls.ints_to_ranges(map(int, ip_list)) | |
468 | ] | |
469 | ||
84c47c4f RP |
470 | @classmethod |
471 | def diff_ids(cls, ids1_ints, ids2_ints): | |
472 | return set(ids2_ints).difference(ids1_ints), set(ids1_ints).difference(ids2_ints) | |
473 | ||
474 | @classmethod | |
475 | def compare_ids(cls, ids1, ids2, pvid=None, expand_range=True): | |
476 | """ Returns true if the ids are same else return false """ | |
477 | ||
478 | if expand_range: | |
479 | ids1_ints = cls.ranges_to_ints(ids1) | |
480 | ids2_ints = cls.ranges_to_ints(ids2) | |
481 | else: | |
482 | ids1_ints = cls.ranges_to_ints(ids1) | |
483 | ids2_ints = ids2 | |
484 | set_diff = set(ids1_ints).symmetric_difference(ids2_ints) | |
485 | if pvid and int(pvid) in set_diff: | |
486 | set_diff.remove(int(pvid)) | |
487 | if set_diff: | |
488 | return False | |
489 | else: | |
490 | return True | |
491 | ||
7f0310a7 RP |
492 | @classmethod |
493 | def get_vlan_vni_in_map_entry(cls, vlan_vni_map_entry): | |
494 | # a good example for map is bridge-vlan-vni-map attribute | |
495 | # format eg: <vlan>=<vni> | |
496 | # 1000-1004=5000-5004 | |
497 | # 1000-1004=auto /* here vni = vlan */ | |
498 | # 1000-1004=auto-10 /* here vni = vlan - 10 */ | |
499 | # 1000-1004=auto+10 /* here vni = vlan + 10 */ | |
500 | ||
501 | vlan = None | |
502 | vni = None | |
503 | try: | |
504 | (vlan, vni) = vlan_vni_map_entry.split('=', 1) | |
505 | if vni == 'auto': | |
506 | vni = vlan | |
507 | elif vni.startswith('auto'): | |
508 | vnistart = 0 | |
509 | vniend = 0 | |
510 | if vni.startswith('auto+'): | |
511 | vni = vni.split('+', 1)[1] | |
512 | vint = int(vni) | |
513 | if vint < 0: | |
514 | raise Exception("invalid auto vni suffix %d" % (vint)) | |
515 | if '-' in vlan: | |
516 | (vstart, vend) = vlan.split('-', 1) | |
517 | vnistart = int(vstart) + vint | |
518 | vniend = int(vend) + vint | |
519 | else: | |
520 | vnistart = int(vlan) + vint | |
521 | elif vni.startswith('auto-'): | |
522 | vni = vni.split('-', 1)[1] | |
523 | vint = int(vni) | |
524 | if vint < 0: | |
525 | raise Exception("invalid auto vni suffix %d" % (vint)) | |
526 | if '-' in vlan: | |
527 | (vstart, vend) = vlan.split('-', 1) | |
528 | vnistart = int(vstart) - vint | |
529 | vniend = int(vend) - vint | |
530 | else: | |
531 | vnistart = int(vlan) - vint | |
532 | if (vnistart <= 0 or (vniend > 0 and (vniend < vnistart)) or | |
533 | (vnistart > cls.vni_max) or (vniend > cls.vni_max)): | |
534 | raise Exception("invalid vni - unable to derive auto vni %s" % (vni)) | |
535 | if vniend > 0: | |
536 | vni = '%d-%d' % (vnistart, vniend) | |
537 | else: | |
538 | vni = '%d' % (vnistart) | |
539 | except Exception as e: | |
540 | raise Exception(str(e)) | |
541 | return | |
542 | return (vlan, vni) | |
543 | ||
544 | @classmethod | |
545 | def get_vlan_vnis_in_map(cls, vlan_vni_map): | |
546 | # a good example for map is bridge-vlan-vni-map attribute | |
547 | # format eg: <vlan>=<vni> | |
548 | # 1000-1004=5000-5004 | |
549 | # 1000-1004=auto /* here vni = vlan */ | |
550 | # 1000-1004=auto-10 /* here vni = vlan - 10 */ | |
551 | # 1000-1004=auto+10 /* here vni = vlan + 10 */ | |
552 | vnis = [] | |
553 | vlans = [] | |
554 | for ventry in vlan_vni_map.split(): | |
555 | try: | |
556 | (vlan, vni) = cls.get_vlan_vni_in_map_entry(ventry) | |
557 | except Exception as e: | |
558 | cls.logger.error("invalid vlan vni map entry - %s (%s)" % (ventry, str(e))) | |
559 | raise | |
560 | vlans.extend([vlan]) | |
561 | vnis.extend([vni]) | |
562 | return (vlans, vnis) | |
563 | ||
af8d5db2 RP |
564 | @classmethod |
565 | def get_vni_mcastgrp_in_map(cls, vni_mcastgrp_map): | |
566 | vnid = {} | |
567 | for ventry in vni_mcastgrp_map.split(): | |
568 | try: | |
569 | (vnis, mcastgrp) = ventry.split('=', 1) | |
570 | vnis_int = utils.ranges_to_ints([vnis]) | |
571 | for v in vnis_int: | |
572 | vnid[v] = mcastgrp | |
573 | except Exception as e: | |
574 | cls.logger.error("invalid vlan mcast grp map entry - %s (%s)" % (ventry, str(e))) | |
575 | raise | |
576 | return vnid | |
577 | ||
a193d8d1 | 578 | fcntl.fcntl(utils.DEVNULL, fcntl.F_SETFD, fcntl.FD_CLOEXEC) |