]>
Commit | Line | Data |
---|---|---|
35681c06 | 1 | #!/usr/bin/env python3 |
8465de90 | 2 | # |
d486dd0d | 3 | # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved. |
8465de90 RP |
4 | # Author: Roopa Prabhu, roopa@cumulusnetworks.com |
5 | # | |
6 | ||
d486dd0d | 7 | import re |
8465de90 | 8 | import os |
a193d8d1 | 9 | import fcntl |
8465de90 | 10 | import atexit |
d486dd0d JF |
11 | import signal |
12 | ||
d486dd0d | 13 | try: |
223ba5af JF |
14 | from ifupdown2.lib.addon import Addon |
15 | ||
d486dd0d JF |
16 | import ifupdown2.ifupdown.policymanager as policymanager |
17 | import ifupdown2.ifupdown.ifupdownflags as ifupdownflags | |
d92f630c | 18 | from ifupdown2.ifupdown.statemanager import statemanager_api as statemanager |
d486dd0d | 19 | |
421e9573 | 20 | from ifupdown2.ifupdown.iface import ifaceRole, ifaceLinkKind, ifaceLinkPrivFlags, ifaceLinkType |
d486dd0d | 21 | from ifupdown2.ifupdown.utils import utils |
223ba5af JF |
22 | |
23 | from ifupdown2.nlmanager.nlmanager import Link | |
d486dd0d JF |
24 | |
25 | from ifupdown2.ifupdownaddons.dhclient import dhclient | |
26 | from ifupdown2.ifupdownaddons.utilsbase import * | |
d486dd0d | 27 | from ifupdown2.ifupdownaddons.modulebase import moduleBase |
bd441a51 | 28 | except (ImportError, ModuleNotFoundError): |
223ba5af JF |
29 | from lib.addon import Addon |
30 | ||
d486dd0d JF |
31 | import ifupdown.policymanager as policymanager |
32 | import ifupdown.ifupdownflags as ifupdownflags | |
d92f630c | 33 | from ifupdown.statemanager import statemanager_api as statemanager |
d486dd0d | 34 | |
421e9573 | 35 | from ifupdown.iface import ifaceRole, ifaceLinkKind, ifaceLinkPrivFlags, ifaceLinkType |
d486dd0d | 36 | from ifupdown.utils import utils |
223ba5af JF |
37 | |
38 | from nlmanager.nlmanager import Link | |
d486dd0d JF |
39 | |
40 | from ifupdownaddons.dhclient import dhclient | |
41 | from ifupdownaddons.utilsbase import * | |
d486dd0d JF |
42 | from ifupdownaddons.modulebase import moduleBase |
43 | ||
8465de90 | 44 | |
8ad5c767 RP |
45 | class vrfPrivFlags: |
46 | PROCESSED = 0x1 | |
47 | ||
223ba5af JF |
48 | |
49 | class vrf(Addon, moduleBase): | |
8465de90 | 50 | """ ifupdown2 addon module to configure vrfs """ |
223ba5af JF |
51 | |
52 | _modinfo = { | |
53 | "mhelp": "vrf configuration module", | |
54 | "attrs": { | |
55 | "vrf-table": { | |
56 | "help": "vrf device routing table id. key to creating a vrf device. " | |
57 | "Table id is either 'auto' or 'valid routing table id'", | |
58 | "validvals": ["auto", "<number>"], | |
59 | "example": ["vrf-table auto", "vrf-table 1001"] | |
60 | }, | |
61 | "vrf": { | |
62 | "help": "vrf the interface is part of", | |
63 | "validvals": ["<text>"], | |
64 | "example": ["vrf blue"] | |
65 | } | |
66 | } | |
67 | } | |
68 | ||
69 | iproute2_vrf_filename = "/etc/iproute2/rt_tables.d/ifupdown2_vrf_map.conf" | |
70 | iproute2_vrf_filehdr = "# This file is autogenerated by ifupdown2.\n" \ | |
71 | "# It contains the vrf name to table mapping.\n" \ | |
72 | "# Reserved table range %s %s\n" | |
6f2890fc RP |
73 | VRF_TABLE_START = 1001 |
74 | VRF_TABLE_END = 5000 | |
8465de90 | 75 | |
223ba5af JF |
76 | system_reserved_rt_tables = { |
77 | "255": "local", | |
78 | "254": "main", | |
79 | "253": "default", | |
80 | "0": "unspec" | |
81 | } | |
f6466fcb | 82 | |
8465de90 | 83 | def __init__(self, *args, **kargs): |
223ba5af | 84 | Addon.__init__(self) |
d486dd0d | 85 | moduleBase.__init__(self, *args, **kargs) |
122ef35b | 86 | self.dhclientcmd = None |
8ad5c767 | 87 | self.name = self.__class__.__name__ |
223ba5af JF |
88 | self.vrf_mgmt_devname = policymanager.policymanager_api.get_module_globals( |
89 | module_name=self.__class__.__name__, | |
90 | attr="vrf-mgmt-devname" | |
91 | ) | |
92 | ||
93 | self.at_exit = False | |
d9e75535 | 94 | |
d486dd0d JF |
95 | self.user_reserved_vrf_table = [] |
96 | ||
d9e75535 RP |
97 | if (ifupdownflags.flags.PERFMODE and |
98 | not (self.vrf_mgmt_devname and os.path.exists('/sys/class/net/%s' | |
99 | %self.vrf_mgmt_devname))): | |
100 | # if perf mode is set (PERFMODE is set at boot), and this is the first | |
101 | # time we are calling ifup at boot (check for mgmt vrf existance at | |
102 | # boot, make sure this is really the first invocation at boot. | |
103 | # ifup is called with PERFMODE at boot multiple times (once for mgmt vrf | |
104 | # and the second time with all auto interfaces). We want to delete | |
105 | # the map file only the first time. This is to avoid accidently | |
106 | # deleting map file with a valid mgmt vrf entry | |
122ef35b RP |
107 | if os.path.exists(self.iproute2_vrf_filename): |
108 | try: | |
109 | self.logger.info('vrf: removing file %s' | |
110 | %self.iproute2_vrf_filename) | |
111 | os.remove(self.iproute2_vrf_filename) | |
3b01ed76 | 112 | except Exception as e: |
122ef35b RP |
113 | self.logger.debug('vrf: removing file failed (%s)' |
114 | %str(e)) | |
8465de90 | 115 | try: |
d486dd0d JF |
116 | ip_rules = utils.exec_command('%s rule show' |
117 | %utils.ip_cmd).splitlines() | |
e36ad206 | 118 | self.ip_rule_cache = [' '.join(r.split()) for r in ip_rules] |
3b01ed76 | 119 | except Exception as e: |
8465de90 | 120 | self.ip_rule_cache = [] |
c46af1c9 | 121 | self.logger.warning('vrf: cache v4: %s' % str(e)) |
8465de90 RP |
122 | |
123 | try: | |
d486dd0d JF |
124 | ip_rules = utils.exec_command('%s -6 rule show' |
125 | %utils.ip_cmd).splitlines() | |
e36ad206 | 126 | self.ip6_rule_cache = [' '.join(r.split()) for r in ip_rules] |
3b01ed76 | 127 | except Exception as e: |
8465de90 | 128 | self.ip6_rule_cache = [] |
c46af1c9 | 129 | self.logger.warning('vrf: cache v6: %s' % str(e)) |
8465de90 RP |
130 | |
131 | #self.logger.debug("vrf: ip rule cache") | |
132 | #self.logger.info(self.ip_rule_cache) | |
133 | ||
134 | #self.logger.info("vrf: ip -6 rule cache") | |
135 | #self.logger.info(self.ip6_rule_cache) | |
136 | ||
0aa91758 N |
137 | self.l3mdev_checked = False |
138 | self.l3mdev4_rule = False | |
139 | if self._l3mdev_rule(self.ip_rule_cache): | |
140 | self.l3mdev4_rule = True | |
141 | self.l3mdev_checked = True | |
142 | self.l3mdev6_rule = False | |
143 | if self._l3mdev_rule(self.ip6_rule_cache): | |
144 | self.l3mdev6_rule = True | |
145 | self.l3mdev_checked = True | |
05ac52f0 RP |
146 | self._iproute2_vrf_map_initialized = False |
147 | self.iproute2_vrf_map = {} | |
05ac52f0 RP |
148 | self.iproute2_vrf_map_sync_to_disk = False |
149 | ||
150 | self.vrf_table_id_start = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-table-id-start') | |
151 | if not self.vrf_table_id_start: | |
152 | self.vrf_table_id_start = self.VRF_TABLE_START | |
153 | self.vrf_table_id_end = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-table-id-end') | |
154 | if not self.vrf_table_id_end: | |
155 | self.vrf_table_id_end = self.VRF_TABLE_END | |
d486dd0d JF |
156 | |
157 | self._modinfo['attrs']['vrf-table']['validrange'] = [ | |
158 | str(self.vrf_table_id_start), | |
159 | str(self.vrf_table_id_end) | |
160 | ] | |
161 | ||
05ac52f0 RP |
162 | self.vrf_max_count = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-max-count') |
163 | ||
164 | self.vrf_fix_local_table = True | |
165 | self.vrf_count = 0 | |
25e2386e | 166 | self.vrf_helper = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-helper') |
6fbd7444 | 167 | self.vrf_close_socks_on_down = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-close-socks-on-down') |
6f5b74f7 RP |
168 | self.warn_on_vrf_map_write_err = True |
169 | ||
d486dd0d JF |
170 | def _check_vrf_table_id(self, ifaceobj): |
171 | vrf_table = ifaceobj.get_attr_value_first('vrf-table') | |
172 | if not vrf_table: | |
173 | return False | |
174 | if (vrf_table != 'auto' and | |
175 | (int(vrf_table) < self.vrf_table_id_start or | |
176 | int(vrf_table) > self.vrf_table_id_end)): | |
177 | self.logger.error('%s: vrf table id %s out of reserved range [%d,%d]' | |
178 | %(ifaceobj.name, | |
179 | vrf_table, | |
180 | self.vrf_table_id_start, | |
181 | self.vrf_table_id_end)) | |
182 | return False | |
183 | return True | |
184 | ||
185 | def syntax_check(self, ifaceobj, ifaceobj_getfunc): | |
186 | if ifaceobj.link_kind & ifaceLinkKind.VRF: | |
187 | try: | |
188 | check_vrf_table_id = self._check_vrf_table_id(ifaceobj) | |
189 | check_vrf_sys_names = self._check_vrf_system_reserved_names(ifaceobj) | |
190 | return check_vrf_table_id and check_vrf_sys_names | |
191 | except Exception as e: | |
192 | self.logger.error('%s: %s' % (ifaceobj.name, str(e))) | |
193 | return False | |
194 | return True | |
195 | ||
196 | def _check_vrf_system_reserved_names(self, ifaceobj): | |
3b01ed76 | 197 | system_reserved_names = list(self.system_reserved_rt_tables.values()) |
d486dd0d JF |
198 | if ifaceobj.name in system_reserved_names: |
199 | self.log_error('cannot use system reserved %s vrf names' | |
200 | % (str(system_reserved_names)), ifaceobj) | |
201 | return False | |
202 | return True | |
203 | ||
eb3ce8c8 | 204 | def _iproute2_vrf_map_initialize(self, writetodisk=True): |
05ac52f0 RP |
205 | if self._iproute2_vrf_map_initialized: |
206 | return | |
207 | ||
8465de90 RP |
208 | # XXX: check for vrf reserved overlap in /etc/iproute2/rt_tables |
209 | self.iproute2_vrf_map = {} | |
05ac52f0 | 210 | iproute2_vrf_map_force_rewrite = False |
8465de90 RP |
211 | # read or create /etc/iproute2/rt_tables.d/ifupdown2.vrf_map |
212 | if os.path.exists(self.iproute2_vrf_filename): | |
223ba5af | 213 | with open(self.iproute2_vrf_filename, "r+" if writetodisk else "r") as vrf_map_fd: |
a193d8d1 JF |
214 | lines = vrf_map_fd.readlines() |
215 | for l in lines: | |
216 | l = l.strip() | |
217 | if l[0] == '#': | |
05ac52f0 | 218 | continue |
a193d8d1 JF |
219 | try: |
220 | (table, vrf_name) = l.strip().split() | |
221 | if self.iproute2_vrf_map.get(int(table)): | |
222 | # looks like the existing file has | |
223 | # duplicate entries, force rewrite of the | |
224 | # file | |
225 | iproute2_vrf_map_force_rewrite = True | |
226 | continue | |
227 | self.iproute2_vrf_map[int(table)] = vrf_name | |
3b01ed76 | 228 | except Exception as e: |
42e85fc8 | 229 | self.logger.info('vrf: iproute2_vrf_map: unable to parse %s (%s)' %(l, str(e))) |
a193d8d1 | 230 | pass |
8465de90 | 231 | |
223ba5af | 232 | running_vrf_map = self.cache.get_vrf_table_map() |
05ac52f0 | 233 | |
4934af35 | 234 | if (not running_vrf_map or (running_vrf_map != self.iproute2_vrf_map)): |
05ac52f0 RP |
235 | self.iproute2_vrf_map = running_vrf_map |
236 | iproute2_vrf_map_force_rewrite = True | |
237 | ||
eb3ce8c8 RP |
238 | if writetodisk: |
239 | if iproute2_vrf_map_force_rewrite: | |
240 | # reopen the file and rewrite the map | |
241 | self._iproute2_vrf_map_open(True, False) | |
242 | else: | |
243 | self._iproute2_vrf_map_open(False, True) | |
8465de90 | 244 | |
05ac52f0 | 245 | self.iproute2_vrf_map_sync_to_disk = False |
223ba5af JF |
246 | self.at_exit = True |
247 | #atexit.register(self._iproute2_vrf_map_sync_to_disk) | |
05ac52f0 RP |
248 | |
249 | self.logger.info("vrf: dumping iproute2_vrf_map") | |
250 | self.logger.info(self.iproute2_vrf_map) | |
6f2890fc RP |
251 | |
252 | last_used_vrf_table = None | |
253 | for t in range(self.vrf_table_id_start, | |
254 | self.vrf_table_id_end): | |
8465de90 RP |
255 | if not self.iproute2_vrf_map.get(t): |
256 | break | |
6f2890fc | 257 | last_used_vrf_table = t |
8465de90 | 258 | self.last_used_vrf_table = last_used_vrf_table |
05ac52f0 | 259 | self._iproute2_vrf_map_initialized = True |
c8a3b44e | 260 | self.vrf_count = len(self.iproute2_vrf_map) |
09753350 | 261 | |
6f5b74f7 RP |
262 | def _iproute2_map_warn(self, errstr): |
263 | if self.warn_on_vrf_map_write_err: | |
264 | if not os.path.exists('/etc/iproute2/rt_tables.d/'): | |
265 | self.logger.info('unable to save iproute2 vrf to table ' + | |
d0e56768 | 266 | 'map (%s)' %errstr) |
6f5b74f7 RP |
267 | self.logger.info('cannot find /etc/iproute2/rt_tables.d.' + |
268 | ' pls check if your iproute2 version' + | |
269 | ' supports rt_tables.d') | |
270 | else: | |
c46af1c9 | 271 | self.logger.warning('unable to open iproute2 vrf to table ' + |
d0e56768 | 272 | 'map (%s)' %errstr) |
6f5b74f7 RP |
273 | self.warn_on_vrf_map_write_err = False |
274 | ||
05ac52f0 | 275 | def _iproute2_vrf_map_sync_to_disk(self): |
eb3ce8c8 RP |
276 | if (ifupdownflags.flags.DRYRUN or |
277 | not self.iproute2_vrf_map_sync_to_disk): | |
8465de90 | 278 | return |
05ac52f0 | 279 | self.logger.info('vrf: syncing table map to %s' |
8465de90 | 280 | %self.iproute2_vrf_filename) |
6f5b74f7 RP |
281 | try: |
282 | with open(self.iproute2_vrf_filename, 'w') as f: | |
283 | f.write(self.iproute2_vrf_filehdr %(self.vrf_table_id_start, | |
284 | self.vrf_table_id_end)) | |
3b01ed76 | 285 | for t, v in self.iproute2_vrf_map.items(): |
6f5b74f7 RP |
286 | f.write('%s %s\n' %(t, v)) |
287 | f.flush() | |
3b01ed76 | 288 | except Exception as e: |
6f5b74f7 RP |
289 | self._iproute2_map_warn(str(e)) |
290 | pass | |
05ac52f0 RP |
291 | |
292 | def _iproute2_vrf_map_open(self, sync_vrfs=False, append=False): | |
293 | self.logger.info('vrf: syncing table map to %s' | |
294 | %self.iproute2_vrf_filename) | |
eb3ce8c8 RP |
295 | if ifupdownflags.flags.DRYRUN: |
296 | return | |
05ac52f0 | 297 | fmode = 'a+' if append else 'w' |
05ac52f0 RP |
298 | if not append: |
299 | # write file header | |
223ba5af JF |
300 | with open(self.iproute2_vrf_filename, fmode) as vrf_map_fd: |
301 | vrf_map_fd.write(self.iproute2_vrf_filehdr | |
302 | %(self.vrf_table_id_start, | |
303 | self.vrf_table_id_end)) | |
3b01ed76 | 304 | for t, v in self.iproute2_vrf_map.items(): |
223ba5af JF |
305 | vrf_map_fd.write('%s %s\n' %(t, v)) |
306 | vrf_map_fd.flush() | |
8465de90 RP |
307 | |
308 | def _is_vrf(self, ifaceobj): | |
309 | if ifaceobj.get_attr_value_first('vrf-table'): | |
310 | return True | |
311 | return False | |
312 | ||
768b4ec5 | 313 | def get_upper_ifacenames(self, ifaceobj, ifacenames_all=None): |
8465de90 RP |
314 | """ Returns list of interfaces dependent on ifaceobj """ |
315 | ||
768b4ec5 RP |
316 | vrf_table = ifaceobj.get_attr_value_first('vrf-table') |
317 | if vrf_table: | |
318 | ifaceobj.link_type = ifaceLinkType.LINK_MASTER | |
319 | ifaceobj.link_kind |= ifaceLinkKind.VRF | |
f7551dcb | 320 | ifaceobj.role |= ifaceRole.MASTER |
d486dd0d JF |
321 | |
322 | if vrf_table != 'auto': | |
323 | # if the user didn't specify auto we need to store the desired | |
324 | # vrf tables ids, in case the configuration has both auto and | |
325 | # hardcoded vrf-table ids. We need to create them all without | |
326 | # collisions. | |
327 | self.user_reserved_vrf_table.append(int(vrf_table)) | |
328 | ||
8465de90 RP |
329 | vrf_iface_name = ifaceobj.get_attr_value_first('vrf') |
330 | if not vrf_iface_name: | |
331 | return None | |
768b4ec5 | 332 | ifaceobj.link_type = ifaceLinkType.LINK_SLAVE |
858a230f | 333 | ifaceobj.link_privflags |= ifaceLinkPrivFlags.VRF_SLAVE |
8ad5c767 | 334 | |
8465de90 RP |
335 | return [vrf_iface_name] |
336 | ||
768b4ec5 | 337 | def get_upper_ifacenames_running(self, ifaceobj): |
8465de90 RP |
338 | return None |
339 | ||
340 | def _get_iproute2_vrf_table(self, vrf_dev_name): | |
3b01ed76 | 341 | for t, v in self.iproute2_vrf_map.items(): |
8465de90 | 342 | if v == vrf_dev_name: |
09753350 | 343 | return str(t) |
8465de90 RP |
344 | return None |
345 | ||
346 | def _get_avail_vrf_table_id(self): | |
6f2890fc RP |
347 | if self.last_used_vrf_table == None: |
348 | table_id_start = self.vrf_table_id_start | |
349 | else: | |
350 | table_id_start = self.last_used_vrf_table + 1 | |
265bf3d3 | 351 | for t in range(table_id_start, self.vrf_table_id_end + 1): |
d486dd0d JF |
352 | if (not self.iproute2_vrf_map.get(t) |
353 | and t not in self.user_reserved_vrf_table): | |
8465de90 | 354 | self.last_used_vrf_table = t |
6f2890fc | 355 | return str(t) |
8465de90 RP |
356 | return None |
357 | ||
bf3eda91 | 358 | def _iproute2_is_vrf_tableid_inuse(self, vrfifaceobj, table_id): |
25e2386e | 359 | old_vrf_name = self.iproute2_vrf_map.get(int(table_id)) |
bf3eda91 | 360 | if old_vrf_name and old_vrf_name != vrfifaceobj.name: |
25e2386e | 361 | self.log_error('table id %s already assigned to vrf dev %s' |
bf3eda91 | 362 | %(table_id, old_vrf_name), vrfifaceobj) |
25e2386e | 363 | |
bf3eda91 | 364 | def _iproute2_vrf_table_entry_add(self, vrfifaceobj, table_id): |
05ac52f0 | 365 | old_vrf_name = self.iproute2_vrf_map.get(int(table_id)) |
25e2386e | 366 | if not old_vrf_name: |
bf3eda91 | 367 | self.iproute2_vrf_map[int(table_id)] = vrfifaceobj.name |
223ba5af JF |
368 | with open(self.iproute2_vrf_filename, "a+") as vrf_map_fd: |
369 | vrf_map_fd.write('%s %s\n' | |
370 | % (table_id, vrfifaceobj.name)) | |
371 | vrf_map_fd.flush() | |
c8a3b44e | 372 | self.vrf_count += 1 |
25e2386e | 373 | return |
bf3eda91 | 374 | if old_vrf_name != vrfifaceobj.name: |
25e2386e RP |
375 | self.log_error('table id %d already assigned to vrf dev %s' |
376 | %(table_id, old_vrf_name)) | |
8465de90 RP |
377 | |
378 | def _iproute2_vrf_table_entry_del(self, table_id): | |
379 | try: | |
05ac52f0 RP |
380 | # with any del of vrf map, we need to force sync to disk |
381 | self.iproute2_vrf_map_sync_to_disk = True | |
09753350 | 382 | del self.iproute2_vrf_map[int(table_id)] |
3b01ed76 | 383 | except Exception as e: |
04e4f352 | 384 | self.logger.info('vrf: iproute2 vrf map del failed for %s (%s)' |
8465de90 RP |
385 | %(table_id, str(e))) |
386 | pass | |
387 | ||
aa36221f RP |
388 | def _is_vrf_dev(self, ifacename): |
389 | # Look at iproute2 map for now. | |
390 | # If it was a master we knew about, | |
391 | # it is definately there | |
3b01ed76 | 392 | if ifacename in list(self.iproute2_vrf_map.values()): |
aa36221f RP |
393 | return True |
394 | return False | |
395 | ||
122ef35b RP |
396 | def _is_dhcp_slave(self, ifaceobj): |
397 | if (not ifaceobj.addr_method or | |
398 | (ifaceobj.addr_method != 'dhcp' and | |
399 | ifaceobj.addr_method != 'dhcp6')): | |
400 | return False | |
401 | return True | |
402 | ||
d486dd0d | 403 | def _up_vrf_slave_without_master(self, ifacename, vrfname, ifaceobj, vrf_master_objs, ifaceobj_getfunc=None): |
8ad5c767 RP |
404 | """ If we have a vrf slave that has dhcp configured, bring up the |
405 | vrf master now. This is needed because vrf has special handling | |
406 | in dhclient hook which requires the vrf master to be present """ | |
d486dd0d JF |
407 | vrf_master = None |
408 | if len(ifaceobj.upperifaces) > 1 and ifaceobj_getfunc: | |
409 | for upper_iface in ifaceobj.upperifaces: | |
410 | upper_ifaceobjs = ifaceobj_getfunc(upper_iface) | |
411 | ||
412 | if upper_ifaceobjs: | |
413 | for upper_obj in upper_ifaceobjs: | |
414 | if upper_obj.link_kind & ifaceLinkKind.VRF: | |
415 | vrf_master = upper_obj.name | |
416 | break | |
417 | elif ifaceobj.upperifaces: | |
418 | vrf_master = ifaceobj.upperifaces[0] | |
8ad5c767 | 419 | if not vrf_master: |
c46af1c9 | 420 | self.logger.warning('%s: vrf master not found' %ifacename) |
8ad5c767 RP |
421 | return |
422 | if os.path.exists('/sys/class/net/%s' %vrf_master): | |
423 | self.logger.info('%s: vrf master %s exists returning' | |
424 | %(ifacename, vrf_master)) | |
425 | return | |
8ad5c767 RP |
426 | self.logger.info('%s: bringing up vrf master %s' |
427 | %(ifacename, vrf_master)) | |
428 | for mobj in vrf_master_objs: | |
429 | vrf_table = mobj.get_attr_value_first('vrf-table') | |
430 | if vrf_table: | |
df53966d RP |
431 | if vrf_table == 'auto': |
432 | vrf_table = self._get_avail_vrf_table_id() | |
433 | if not vrf_table: | |
434 | self.log_error('%s: unable to get an auto table id' | |
bf3eda91 | 435 | %mobj.name, ifaceobj) |
d0e56768 | 436 | self.logger.info('%s: table id auto: selected table id %s' |
df53966d | 437 | %(mobj.name, vrf_table)) |
25e2386e RP |
438 | try: |
439 | self._up_vrf_dev(mobj, vrf_table, False) | |
440 | except Exception: | |
441 | raise | |
df53966d | 442 | break |
0ba04b38 | 443 | self._handle_existing_connections(ifaceobj, vrfname) |
97fc046b | 444 | self.enable_ipv6_if_prev_brport(ifacename) |
223ba5af | 445 | self.netlink.link_set_master(ifacename, vrfname) |
8ad5c767 RP |
446 | return |
447 | ||
97fc046b | 448 | def enable_ipv6_if_prev_brport(self, ifname): |
d92f630c | 449 | """ |
97fc046b | 450 | If the intf was previously enslaved to a bridge it is possible ipv6 is still disabled. |
d92f630c JF |
451 | """ |
452 | try: | |
223ba5af JF |
453 | for ifaceobj in statemanager.get_ifaceobjs(ifname) or []: |
454 | if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT: | |
455 | self.write_file("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname, "0") | |
456 | return | |
3b01ed76 | 457 | except Exception as e: |
d92f630c JF |
458 | self.logger.info(str(e)) |
459 | ||
717cee31 | 460 | def _down_dhcp_slave(self, ifaceobj, vrfname): |
122ef35b | 461 | try: |
717cee31 | 462 | dhclient_cmd_prefix = None |
6369e774 | 463 | if (vrfname and self.vrf_exec_cmd_prefix and |
223ba5af | 464 | self.cache.link_exists(vrfname)): |
717cee31 RP |
465 | dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix, |
466 | vrfname) | |
467 | self.dhclientcmd.release(ifaceobj.name, dhclient_cmd_prefix) | |
3218f49d | 468 | except Exception: |
122ef35b RP |
469 | # ignore any dhclient release errors |
470 | pass | |
471 | ||
0ba04b38 | 472 | def _handle_existing_connections(self, ifaceobj, vrfname): |
fc5e1735 | 473 | if not ifaceobj or ifupdownflags.flags.PERFMODE: |
0ba04b38 | 474 | return |
223ba5af JF |
475 | # XXX: re-evaluate kill_ssh_sessions |
476 | if (self.vrf_mgmt_devname and self.vrf_mgmt_devname == vrfname): | |
477 | self._kill_ssh_connections(ifaceobj.name, ifaceobj) | |
478 | self._close_sockets(ifaceobj.name) | |
0ba04b38 | 479 | if self._is_dhcp_slave(ifaceobj): |
717cee31 | 480 | self._down_dhcp_slave(ifaceobj, vrfname) |
0ba04b38 | 481 | |
8ad5c767 | 482 | def _up_vrf_slave(self, ifacename, vrfname, ifaceobj=None, |
09753350 | 483 | ifaceobj_getfunc=None, vrf_exists=False): |
8465de90 | 484 | try: |
d2b35716 | 485 | master_exists = True |
223ba5af JF |
486 | if vrf_exists or self.cache.link_exists(vrfname): |
487 | uppers = self.sysfs.link_get_uppers(ifacename) | |
42e85fc8 | 488 | if not uppers or vrfname not in uppers: |
0ba04b38 | 489 | self._handle_existing_connections(ifaceobj, vrfname) |
97fc046b | 490 | self.enable_ipv6_if_prev_brport(ifacename) |
223ba5af | 491 | self.netlink.link_set_master(ifacename, vrfname) |
79cf5764 RP |
492 | elif ifaceobj: |
493 | vrf_master_objs = ifaceobj_getfunc(vrfname) | |
494 | if not vrf_master_objs: | |
80be09ba RP |
495 | # this is the case where vrf is assigned to an interface |
496 | # but user has not provided a vrf interface. | |
497 | # people expect you to warn them but go ahead with the | |
498 | # rest of the config on that interface | |
223ba5af | 499 | self.netlink.link_up(ifacename) |
80be09ba RP |
500 | self.log_error('vrf master ifaceobj %s not found' |
501 | %vrfname) | |
79cf5764 RP |
502 | return |
503 | if (ifupdownflags.flags.ALL or | |
1ce8b5bb | 504 | ifupdownflags.flags.WITH_DEPENDS or |
79cf5764 RP |
505 | (ifupdownflags.flags.CLASS and |
506 | ifaceobj.classes and vrf_master_objs[0].classes and | |
3b01ed76 | 507 | set(ifaceobj.classes).intersection(vrf_master_objs[0].classes))): |
79cf5764 RP |
508 | self._up_vrf_slave_without_master(ifacename, vrfname, |
509 | ifaceobj, | |
d486dd0d JF |
510 | vrf_master_objs, |
511 | ifaceobj_getfunc) | |
79cf5764 RP |
512 | else: |
513 | master_exists = False | |
d2b35716 RP |
514 | else: |
515 | master_exists = False | |
f944abda | 516 | if master_exists: |
80f2b553 JF |
517 | if not ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN and not self.check_link_down_on_vlan_lower_dev( |
518 | ifaceobj, ifaceobj_getfunc | |
519 | ): | |
223ba5af | 520 | self.netlink.link_up(ifacename) |
79cf5764 | 521 | else: |
d2b35716 | 522 | self.log_error('vrf %s not around, skipping vrf config' |
bf3eda91 | 523 | %(vrfname), ifaceobj) |
3b01ed76 | 524 | except Exception as e: |
bf3eda91 | 525 | self.log_error('%s: %s' %(ifacename, str(e)), ifaceobj) |
8465de90 | 526 | |
80f2b553 JF |
527 | def check_link_down_on_vlan_lower_dev(self, ifaceobj, ifaceobj_getfunc): |
528 | if ifaceobj.link_kind & ifaceLinkKind.VLAN: | |
529 | for obj in ifaceobj_getfunc(ifaceobj.lowerifaces[0]): | |
530 | if obj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN: | |
531 | self.logger.info("%s: keeping vlan down (lower device %s has link-down flag set)" % (ifaceobj.name, obj.name)) | |
532 | return True | |
533 | return False | |
534 | ||
8465de90 RP |
535 | def _del_vrf_rules(self, vrf_dev_name, vrf_table): |
536 | pref = 200 | |
537 | ip_rule_out_format = '%s: from all %s %s lookup %s' | |
d486dd0d | 538 | ip_rule_cmd = '%s %s rule del pref %s %s %s table %s' |
8465de90 | 539 | |
4ce47ce4 | 540 | rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name) |
8465de90 | 541 | if rule in self.ip_rule_cache: |
d486dd0d JF |
542 | rule_cmd = ip_rule_cmd %(utils.ip_cmd, |
543 | '', pref, 'oif', vrf_dev_name, | |
25e2386e | 544 | vrf_dev_name) |
a193d8d1 | 545 | utils.exec_command(rule_cmd) |
8465de90 | 546 | |
4ce47ce4 | 547 | rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name) |
8465de90 | 548 | if rule in self.ip_rule_cache: |
d486dd0d JF |
549 | rule_cmd = ip_rule_cmd %(utils.ip_cmd, |
550 | '', pref, 'iif', vrf_dev_name, | |
25e2386e | 551 | vrf_dev_name) |
a193d8d1 | 552 | utils.exec_command(rule_cmd) |
8465de90 | 553 | |
4ce47ce4 | 554 | rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name) |
e6072769 | 555 | if rule in self.ip6_rule_cache: |
d486dd0d JF |
556 | rule_cmd = ip_rule_cmd %(utils.ip_cmd, |
557 | '-6', pref, 'oif', vrf_dev_name, | |
25e2386e | 558 | vrf_dev_name) |
a193d8d1 | 559 | utils.exec_command(rule_cmd) |
8465de90 | 560 | |
4ce47ce4 | 561 | rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name) |
e6072769 | 562 | if rule in self.ip6_rule_cache: |
d486dd0d JF |
563 | rule_cmd = ip_rule_cmd %(utils.ip_cmd, |
564 | '-6', pref, 'iif', vrf_dev_name, | |
25e2386e | 565 | vrf_dev_name) |
a193d8d1 | 566 | utils.exec_command(rule_cmd) |
8465de90 | 567 | |
0aa91758 N |
568 | def _l3mdev_rule(self, ip_rules): |
569 | for rule in ip_rules: | |
570 | if not re.search(r"\d.*from\s+all\s+lookup\s+\W?l3mdev-table\W?", | |
e36ad206 | 571 | rule): |
0aa91758 N |
572 | continue |
573 | return True | |
574 | return False | |
575 | ||
576 | def _rule_cache_fill(self): | |
d486dd0d JF |
577 | ip_rules = utils.exec_command('%s rule show' |
578 | %utils.ip_cmd).splitlines() | |
0aa91758 N |
579 | self.ip_rule_cache = [' '.join(r.split()) for r in ip_rules] |
580 | self.l3mdev4_rule = self._l3mdev_rule(self.ip_rule_cache) | |
d486dd0d JF |
581 | ip_rules = utils.exec_command('%s -6 rule show' |
582 | %utils.ip_cmd).splitlines() | |
0aa91758 N |
583 | self.ip6_rule_cache = [' '.join(r.split()) for r in ip_rules] |
584 | self.l3mdev6_rule = self._l3mdev_rule(self.ip6_rule_cache) | |
585 | ||
8465de90 RP |
586 | def _add_vrf_rules(self, vrf_dev_name, vrf_table): |
587 | pref = 200 | |
588 | ip_rule_out_format = '%s: from all %s %s lookup %s' | |
d486dd0d | 589 | ip_rule_cmd = '%s %s rule add pref %s %s %s table %s' |
659097a0 N |
590 | if self.vrf_fix_local_table: |
591 | self.vrf_fix_local_table = False | |
592 | rule = '0: from all lookup local' | |
593 | if rule in self.ip_rule_cache: | |
594 | try: | |
d486dd0d JF |
595 | utils.exec_command('%s rule del pref 0' |
596 | %utils.ip_cmd) | |
597 | utils.exec_command('%s rule add pref 32765 table local' | |
598 | %utils.ip_cmd) | |
3b01ed76 | 599 | except Exception as e: |
a193d8d1 | 600 | self.logger.info('%s: %s' % (vrf_dev_name, str(e))) |
659097a0 | 601 | pass |
c61672da N |
602 | if rule in self.ip6_rule_cache: |
603 | try: | |
d486dd0d JF |
604 | utils.exec_command('%s -6 rule del pref 0' |
605 | %utils.ip_cmd) | |
606 | utils.exec_command('%s -6 rule add pref 32765 table local' | |
607 | %utils.ip_cmd) | |
3b01ed76 | 608 | except Exception as e: |
a193d8d1 | 609 | self.logger.info('%s: %s' % (vrf_dev_name, str(e))) |
c61672da | 610 | pass |
8465de90 | 611 | |
0aa91758 N |
612 | if not self.l3mdev_checked: |
613 | self._rule_cache_fill() | |
614 | self.l3mdev_checked = True | |
3fcb15fe N |
615 | #Example ip rule |
616 | #200: from all oif blue lookup blue | |
617 | #200: from all iif blue lookup blue | |
618 | ||
619 | rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name) | |
0aa91758 | 620 | if not self.l3mdev4_rule and rule not in self.ip_rule_cache: |
d486dd0d JF |
621 | rule_cmd = ip_rule_cmd %(utils.ip_cmd, |
622 | '', pref, 'oif', vrf_dev_name, | |
25e2386e | 623 | vrf_dev_name) |
a193d8d1 | 624 | utils.exec_command(rule_cmd) |
8465de90 | 625 | |
3fcb15fe | 626 | rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name) |
0aa91758 | 627 | if not self.l3mdev4_rule and rule not in self.ip_rule_cache: |
d486dd0d JF |
628 | rule_cmd = ip_rule_cmd %(utils.ip_cmd, |
629 | '', pref, 'iif', vrf_dev_name, | |
25e2386e | 630 | vrf_dev_name) |
a193d8d1 | 631 | utils.exec_command(rule_cmd) |
8465de90 | 632 | |
3fcb15fe | 633 | rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name) |
0aa91758 | 634 | if not self.l3mdev6_rule and rule not in self.ip6_rule_cache: |
d486dd0d JF |
635 | rule_cmd = ip_rule_cmd %(utils.ip_cmd, |
636 | '-6', pref, 'oif', vrf_dev_name, | |
25e2386e | 637 | vrf_dev_name) |
a193d8d1 | 638 | utils.exec_command(rule_cmd) |
8465de90 | 639 | |
3fcb15fe | 640 | rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name) |
0aa91758 | 641 | if not self.l3mdev6_rule and rule not in self.ip6_rule_cache: |
d486dd0d JF |
642 | rule_cmd = ip_rule_cmd %(utils.ip_cmd, |
643 | '-6', pref, 'iif', vrf_dev_name, | |
25e2386e | 644 | vrf_dev_name) |
a193d8d1 | 645 | utils.exec_command(rule_cmd) |
8465de90 | 646 | |
1b284018 RP |
647 | def _is_address_virtual_slaves(self, vrfobj, config_vrfslaves, |
648 | vrfslave): | |
649 | # Address virtual lines on a vrf slave will create | |
650 | # macvlan devices on the vrf slave and enslave them | |
651 | # to the vrf master. This function checks if the | |
652 | # vrf slave is such a macvlan interface. | |
653 | # XXX: additional possible checks that can be done here | |
654 | # are: | |
655 | # - check if it is also a macvlan device of the | |
d486dd0d | 656 | # format <vrf_slave>-v<int> created by the |
1b284018 | 657 | # address virtual module |
223ba5af | 658 | vrfslave_lowers = self.sysfs.link_get_lowers(vrfslave) |
1b284018 RP |
659 | if vrfslave_lowers: |
660 | if vrfslave_lowers[0] in config_vrfslaves: | |
661 | return True | |
662 | return False | |
663 | ||
4d2c9798 | 664 | def _add_vrf_slaves(self, ifaceobj, ifaceobj_getfunc=None): |
223ba5af | 665 | running_slaves = self.sysfs.link_get_lowers(ifaceobj.name) |
768b4ec5 | 666 | config_slaves = ifaceobj.lowerifaces |
b94e4d24 | 667 | if not config_slaves and not running_slaves: |
867e11a2 | 668 | return |
4d2c9798 RP |
669 | |
670 | if not config_slaves: config_slaves = [] | |
671 | if not running_slaves: running_slaves = [] | |
768b4ec5 RP |
672 | add_slaves = set(config_slaves).difference(set(running_slaves)) |
673 | del_slaves = set(running_slaves).difference(set(config_slaves)) | |
674 | if add_slaves: | |
675 | for s in add_slaves: | |
676 | try: | |
223ba5af | 677 | if not self.cache.link_exists(s): |
d2b35716 | 678 | continue |
09753350 RP |
679 | sobj = None |
680 | if ifaceobj_getfunc: | |
681 | sobj = ifaceobj_getfunc(s) | |
682 | self._up_vrf_slave(s, ifaceobj.name, | |
683 | sobj[0] if sobj else None, | |
684 | ifaceobj_getfunc, True) | |
3b01ed76 | 685 | except Exception as e: |
768b4ec5 RP |
686 | self.logger.info('%s: %s' %(ifaceobj.name, str(e))) |
687 | ||
688 | if del_slaves: | |
689 | for s in del_slaves: | |
690 | try: | |
1b284018 RP |
691 | if self._is_address_virtual_slaves(ifaceobj, |
692 | config_slaves, s): | |
693 | continue | |
aa36221f | 694 | sobj = None |
4d2c9798 RP |
695 | if ifaceobj_getfunc: |
696 | sobj = ifaceobj_getfunc(s) | |
0ba04b38 RP |
697 | self._down_vrf_slave(s, sobj[0] if sobj else None, |
698 | ifaceobj.name) | |
3b01ed76 | 699 | except Exception as e: |
768b4ec5 RP |
700 | self.logger.info('%s: %s' %(ifaceobj.name, str(e))) |
701 | ||
702 | if ifaceobj.link_type == ifaceLinkType.LINK_MASTER: | |
703 | for s in config_slaves: | |
704 | try: | |
7cdb931e | 705 | for slave_ifaceobj in ifaceobj_getfunc(s) or []: |
80f2b553 JF |
706 | if slave_ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN or self.check_link_down_on_vlan_lower_dev( |
707 | slave_ifaceobj, ifaceobj_getfunc | |
708 | ): | |
e7206159 | 709 | raise Exception("link-down yes: keeping VRF slave down") |
223ba5af | 710 | self.netlink.link_up(s) |
3b01ed76 | 711 | except Exception as e: |
e7206159 | 712 | self.logger.debug("%s: %s" % (s, str(e))) |
768b4ec5 | 713 | pass |
8465de90 | 714 | |
8ad5c767 RP |
715 | def _set_vrf_dev_processed_flag(self, ifaceobj): |
716 | ifaceobj.module_flags[self.name] = \ | |
717 | ifaceobj.module_flags.setdefault(self.name, 0) | \ | |
718 | vrfPrivFlags.PROCESSED | |
719 | ||
720 | def _check_vrf_dev_processed_flag(self, ifaceobj): | |
25e2386e | 721 | if (ifaceobj.module_flags.get(self.name, 0) & vrfPrivFlags.PROCESSED): |
8ad5c767 RP |
722 | return True |
723 | return False | |
8465de90 | 724 | |
8ad5c767 | 725 | def _create_vrf_dev(self, ifaceobj, vrf_table): |
223ba5af | 726 | if not self.cache.link_exists(ifaceobj.name): |
d486dd0d JF |
727 | self._check_vrf_system_reserved_names(ifaceobj) |
728 | ||
c8a3b44e | 729 | if self.vrf_count == self.vrf_max_count: |
9b35cb3e JF |
730 | self.log_error('max vrf count %d hit...not ' |
731 | 'creating vrf' % self.vrf_count, ifaceobj) | |
6f2890fc RP |
732 | if vrf_table == 'auto': |
733 | vrf_table = self._get_avail_vrf_table_id() | |
734 | if not vrf_table: | |
9b35cb3e | 735 | self.log_error('unable to get an auto table id', ifaceobj) |
d0e56768 | 736 | self.logger.info('%s: table id auto: selected table id %s' |
6f2890fc | 737 | %(ifaceobj.name, vrf_table)) |
25e2386e | 738 | else: |
bf3eda91 | 739 | self._iproute2_is_vrf_tableid_inuse(ifaceobj, vrf_table) |
3b01ed76 | 740 | if ifaceobj.name in list(self.system_reserved_rt_tables.keys()): |
f6466fcb | 741 | self.log_error('cannot use system reserved %s table ids' |
3b01ed76 | 742 | %(str(list(self.system_reserved_rt_tables.keys()))), |
bf3eda91 | 743 | ifaceobj) |
d54baa22 RP |
744 | |
745 | if not vrf_table.isdigit(): | |
9b35cb3e | 746 | self.log_error('vrf-table must be an integer or \'auto\'', ifaceobj) |
d54baa22 | 747 | |
6f2890fc RP |
748 | # XXX: If we decide to not allow vrf id usages out of |
749 | # the reserved ifupdown range, then uncomment this code. | |
fd8c6caf RP |
750 | else: |
751 | if (int(vrf_table) < self.vrf_table_id_start or | |
752 | int(vrf_table) > self.vrf_table_id_end): | |
9b35cb3e JF |
753 | self.log_error('vrf table id %s out of reserved range [%d,%d]' |
754 | %(vrf_table, | |
fd8c6caf | 755 | self.vrf_table_id_start, |
bf3eda91 | 756 | self.vrf_table_id_end), ifaceobj) |
2df6a60f | 757 | try: |
223ba5af | 758 | self.netlink.link_add_vrf(ifaceobj.name, vrf_table) |
3b01ed76 | 759 | except Exception as e: |
d0e56768 | 760 | self.log_error('create failed (%s)' % str(e), ifaceobj) |
25e2386e | 761 | if vrf_table != 'auto': |
bf3eda91 | 762 | self._iproute2_vrf_table_entry_add(ifaceobj, vrf_table) |
2df6a60f | 763 | else: |
4d2c9798 RP |
764 | if vrf_table == 'auto': |
765 | vrf_table = self._get_iproute2_vrf_table(ifaceobj.name) | |
d486dd0d | 766 | if not vrf_table and not ifupdownflags.flags.DRYRUN: |
9b35cb3e | 767 | self.log_error('unable to get vrf table id', ifaceobj) |
6f2890fc | 768 | |
2df6a60f | 769 | # if the device exists, check if table id is same |
223ba5af JF |
770 | running_table = self.cache.get_link_info_data_attribute(ifaceobj.name, Link.IFLA_VRF_TABLE) |
771 | ||
772 | if running_table is not None and vrf_table != str(running_table): | |
773 | self.log_error('cannot change vrf table id,running table id' | |
774 | ' %s is different from config id %s' | |
775 | % (running_table, vrf_table), ifaceobj) | |
4d2c9798 RP |
776 | return vrf_table |
777 | ||
25e2386e | 778 | def _up_vrf_helper(self, ifaceobj, vrf_table): |
c4e05f9f RP |
779 | mode = "" |
780 | if ifupdownflags.flags.PERFMODE: | |
781 | mode = "boot" | |
25e2386e | 782 | if self.vrf_helper: |
a193d8d1 JF |
783 | utils.exec_command('%s create %s %s %s' % |
784 | (self.vrf_helper, | |
785 | ifaceobj.name, | |
786 | vrf_table, | |
787 | mode)) | |
25e2386e | 788 | |
4d2c9798 RP |
789 | def _up_vrf_dev(self, ifaceobj, vrf_table, add_slaves=True, |
790 | ifaceobj_getfunc=None): | |
8ad5c767 RP |
791 | |
792 | # if vrf dev is already processed return. This can happen | |
0ba04b38 RP |
793 | # if we the slave was configured before. |
794 | # see self._up_vrf_slave_without_master | |
8ad5c767 RP |
795 | if self._check_vrf_dev_processed_flag(ifaceobj): |
796 | return True | |
797 | ||
25e2386e RP |
798 | try: |
799 | vrf_table = self._create_vrf_dev(ifaceobj, vrf_table) | |
3b01ed76 | 800 | except Exception as e: |
bf3eda91 | 801 | self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj) |
d486dd0d | 802 | |
8ad5c767 | 803 | try: |
8ad5c767 | 804 | self._add_vrf_rules(ifaceobj.name, vrf_table) |
6369e774 | 805 | self._up_vrf_helper(ifaceobj, vrf_table) |
8ad5c767 | 806 | if add_slaves: |
4d2c9798 | 807 | self._add_vrf_slaves(ifaceobj, ifaceobj_getfunc) |
8ad5c767 | 808 | self._set_vrf_dev_processed_flag(ifaceobj) |
7cdb931e JF |
809 | |
810 | if not ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN: | |
223ba5af | 811 | self.netlink.link_up(ifaceobj.name) |
3b01ed76 | 812 | except Exception as e: |
bf3eda91 | 813 | self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj) |
8ad5c767 | 814 | |
223ba5af | 815 | def _kill_ssh_connections(self, ifacename, ifaceobj): |
5c5a7b93 | 816 | try: |
0e936c3f | 817 | iplist = [str(ip.ip) for ip in self.cache.get_managed_ip_addresses( |
223ba5af | 818 | ifname=ifacename, |
0e936c3f | 819 | ifaceobj_list=[ifaceobj], |
223ba5af JF |
820 | )] |
821 | ||
0ba04b38 RP |
822 | if not iplist: |
823 | return | |
824 | proc=[] | |
5c5a7b93 | 825 | #Example output: |
d486dd0d | 826 | #ESTAB 0 0 10.0.1.84:ssh 10.0.1.228:45186 |
5c5a7b93 | 827 | #users:(("sshd",pid=2528,fd=3)) |
d486dd0d | 828 | cmdl = [utils.ss_cmd, '-t', '-p'] |
a193d8d1 | 829 | for line in utils.exec_commandl(cmdl).splitlines(): |
5c5a7b93 N |
830 | citems = line.split() |
831 | addr = None | |
832 | if '%' in citems[3]: | |
833 | addr = citems[3].split('%')[0] | |
834 | elif ':ssh' in citems[3]: | |
835 | addr = citems[3].split(':')[0] | |
836 | if not addr: | |
837 | continue | |
0ba04b38 | 838 | if addr in iplist: |
5c5a7b93 N |
839 | if len(citems) == 6: |
840 | proc.append(citems[5].split(',')[1].split('=')[1]) | |
841 | ||
842 | if not proc: | |
843 | return | |
be276272 N |
844 | pid = None |
845 | # outpt of '/usr/bin/pstree -Aps <pid>': | |
846 | # 'systemd(1)---sshd(990)---sshd(16112)---sshd(16126)---bash(16127)---sudo(16756)---ifreload(16761)---pstree(16842)\n' | |
847 | # get the above output to following format | |
848 | # ['systemd(1)', 'sshd(990)', 'sshd(16112)', 'sshd(16126)', 'bash(16127)', 'sudo(16756)', 'ifreload(16761)', 'pstree(16850)'] | |
d486dd0d JF |
849 | pstree = list(reversed(utils.exec_command('%s -Aps %s' % |
850 | (utils.pstree_cmd, os.getpid())).strip().split('---'))) | |
be276272 N |
851 | for index, process in enumerate(pstree): |
852 | # check the parent of SSH process to make sure | |
853 | # we don't kill SSH server or systemd process | |
854 | if 'sshd' in process and 'sshd' in pstree[index + 1]: | |
3b01ed76 | 855 | pid = [x for x in process if x.isdigit()] |
be276272 | 856 | break |
aa36221f RP |
857 | self.logger.info("%s: killing active ssh sessions: %s" |
858 | %(ifacename, str(proc))) | |
cbdde74e RP |
859 | |
860 | if ifupdownflags.flags.DRYRUN: | |
861 | return | |
5c5a7b93 N |
862 | for id in proc: |
863 | if id != pid: | |
864 | try: | |
aa36221f | 865 | os.kill(int(id), signal.SIGINT) |
5c5a7b93 N |
866 | except OSError as e: |
867 | continue | |
0ba04b38 RP |
868 | |
869 | # Kill current SSH client | |
5c5a7b93 | 870 | if pid in proc: |
32f6e6ca | 871 | try: |
0ba04b38 | 872 | forkret = os.fork() |
3b01ed76 | 873 | except OSError as e: |
0ba04b38 RP |
874 | self.logger.info("fork error : %s [%d]" % (e.strerror, e.errno)) |
875 | if (forkret == 0): # The first child. | |
876 | try: | |
877 | os.setsid() | |
878 | self.logger.info("%s: ifreload continuing in the background" %ifacename) | |
3b01ed76 JF |
879 | except OSError as xxx_todo_changeme: |
880 | (err_no, err_message) = xxx_todo_changeme.args | |
0ba04b38 RP |
881 | self.logger.info("os.setsid failed: errno=%d: %s" % (err_no, err_message)) |
882 | self.logger.info("pid=%d pgid=%d" % (os.getpid(), os.getpgid(0))) | |
5c5a7b93 | 883 | try: |
aa36221f RP |
884 | self.logger.info("%s: killing our session: %s" |
885 | %(ifacename, str(proc))) | |
886 | os.kill(int(pid), signal.SIGINT) | |
5c5a7b93 N |
887 | return |
888 | except OSError as e: | |
889 | return | |
3b01ed76 | 890 | except Exception as e: |
5c5a7b93 N |
891 | self.logger.info('%s: %s' %(ifacename, str(e))) |
892 | ||
8ad5c767 | 893 | def _up(self, ifaceobj, ifaceobj_getfunc=None): |
8465de90 RP |
894 | try: |
895 | vrf_table = ifaceobj.get_attr_value_first('vrf-table') | |
896 | if vrf_table: | |
05ac52f0 | 897 | self._iproute2_vrf_map_initialize() |
09753350 | 898 | # This is a vrf device |
4d2c9798 | 899 | self._up_vrf_dev(ifaceobj, vrf_table, True, ifaceobj_getfunc) |
8465de90 RP |
900 | else: |
901 | vrf = ifaceobj.get_attr_value_first('vrf') | |
902 | if vrf: | |
223ba5af JF |
903 | if not self.cache.link_exists(ifaceobj.name): |
904 | self.logger.warning("%s: device not found - please check your configuration" % ifaceobj.name) | |
905 | return | |
906 | ||
05ac52f0 | 907 | self._iproute2_vrf_map_initialize() |
09753350 | 908 | # This is a vrf slave |
8ad5c767 RP |
909 | self._up_vrf_slave(ifaceobj.name, vrf, ifaceobj, |
910 | ifaceobj_getfunc) | |
d486dd0d | 911 | elif not ifupdownflags.flags.PERFMODE: |
aa36221f | 912 | # check if we were a slave before |
223ba5af | 913 | master = self.cache.get_master(ifaceobj.name) |
aa36221f | 914 | if master: |
717cee31 | 915 | self._iproute2_vrf_map_initialize() |
aa36221f | 916 | if self._is_vrf_dev(master): |
0ba04b38 RP |
917 | self._down_vrf_slave(ifaceobj.name, ifaceobj, |
918 | master) | |
3b01ed76 | 919 | except Exception as e: |
bf3eda91 | 920 | self.log_error(str(e), ifaceobj) |
8465de90 | 921 | |
25e2386e | 922 | def _down_vrf_helper(self, ifaceobj, vrf_table): |
c4e05f9f RP |
923 | mode = "" |
924 | if ifupdownflags.flags.PERFMODE: | |
925 | mode = "boot" | |
25e2386e | 926 | if self.vrf_helper: |
a193d8d1 JF |
927 | utils.exec_command('%s delete %s %s %s' % |
928 | (self.vrf_helper, | |
929 | ifaceobj.name, | |
930 | vrf_table, | |
931 | mode)) | |
25e2386e | 932 | |
223ba5af | 933 | def _close_sockets(self, ifacename): |
6fbd7444 RP |
934 | if not self.vrf_close_socks_on_down: |
935 | return | |
936 | ||
223ba5af JF |
937 | try: |
938 | ifindex = self.cache.get_ifindex(ifacename) | |
939 | except Exception as e: | |
5e3ea6fc | 940 | self.logger.debug("%s: vrf: close sockets error: %s" % (ifacename, str(e))) |
223ba5af JF |
941 | ifindex = 0 |
942 | ||
943 | if not ifindex: | |
944 | return | |
945 | ||
6fbd7444 | 946 | try: |
d486dd0d JF |
947 | utils.exec_command('%s -aK \"dev == %s\"' |
948 | %(utils.ss_cmd, ifindex)) | |
3b01ed76 | 949 | except Exception as e: |
6fbd7444 | 950 | self.logger.info('%s: closing socks using ss' |
d0e56768 | 951 | ' failed (%s)' %(ifacename, str(e))) |
6fbd7444 RP |
952 | pass |
953 | ||
c4be5481 | 954 | def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None): |
5c5a7b93 | 955 | |
223ba5af | 956 | if not self.cache.link_exists(ifaceobj.name): |
6fbd7444 RP |
957 | return |
958 | ||
8465de90 RP |
959 | if vrf_table == 'auto': |
960 | vrf_table = self._get_iproute2_vrf_table(ifaceobj.name) | |
aa36221f | 961 | |
223ba5af | 962 | running_slaves = self.sysfs.link_get_lowers(ifaceobj.name) |
2365b3c9 RP |
963 | if running_slaves: |
964 | for s in running_slaves: | |
965 | if ifaceobj_getfunc: | |
966 | sobj = ifaceobj_getfunc(s) | |
967 | try: | |
968 | self._handle_existing_connections(sobj[0] | |
969 | if sobj else None, | |
0ba04b38 | 970 | ifaceobj.name) |
3b01ed76 | 971 | except Exception as e: |
2365b3c9 RP |
972 | self.logger.info('%s: %s' %(ifaceobj.name, str(e))) |
973 | pass | |
974 | try: | |
223ba5af JF |
975 | self.netlink.addr_flush(s) |
976 | self.netlink.link_down(s) | |
3b01ed76 | 977 | except Exception as e: |
223ba5af | 978 | self.logger.info('%s: %s' %(s, str(e))) |
2365b3c9 | 979 | pass |
768b4ec5 | 980 | |
40804f1a RP |
981 | try: |
982 | self._down_vrf_helper(ifaceobj, vrf_table) | |
3b01ed76 | 983 | except Exception as e: |
c46af1c9 | 984 | self.logger.warning('%s: %s' %(ifaceobj.name, str(e))) |
40804f1a | 985 | pass |
25e2386e | 986 | |
768b4ec5 | 987 | try: |
f1c92482 | 988 | self._del_vrf_rules(ifaceobj.name, vrf_table) |
3b01ed76 | 989 | except Exception as e: |
768b4ec5 RP |
990 | self.logger.info('%s: %s' %(ifaceobj.name, str(e))) |
991 | pass | |
992 | ||
223ba5af | 993 | self._close_sockets(ifaceobj.name) |
6fbd7444 | 994 | |
223ba5af JF |
995 | try: |
996 | self.netlink.link_del(ifaceobj.name) | |
3b01ed76 | 997 | except Exception as e: |
223ba5af JF |
998 | self.logger.info('%s: %s' %(ifaceobj.name, str(e))) |
999 | pass | |
6fbd7444 | 1000 | |
f1c92482 RP |
1001 | try: |
1002 | self._iproute2_vrf_table_entry_del(vrf_table) | |
3b01ed76 | 1003 | except Exception as e: |
768b4ec5 RP |
1004 | self.logger.info('%s: %s' %(ifaceobj.name, str(e))) |
1005 | pass | |
8465de90 | 1006 | |
f1c92482 | 1007 | |
0ba04b38 | 1008 | def _down_vrf_slave(self, ifacename, ifaceobj=None, vrfname=None): |
8465de90 | 1009 | try: |
0ba04b38 | 1010 | self._handle_existing_connections(ifaceobj, vrfname) |
223ba5af | 1011 | self.netlink.link_set_nomaster(ifacename) |
3b01ed76 | 1012 | except Exception as e: |
c46af1c9 | 1013 | self.logger.warning('%s: %s' %(ifacename, str(e))) |
8465de90 | 1014 | |
8ad5c767 | 1015 | def _down(self, ifaceobj, ifaceobj_getfunc=None): |
8465de90 RP |
1016 | try: |
1017 | vrf_table = ifaceobj.get_attr_value_first('vrf-table') | |
1018 | if vrf_table: | |
05ac52f0 | 1019 | self._iproute2_vrf_map_initialize() |
c4be5481 | 1020 | self._down_vrf_dev(ifaceobj, vrf_table, ifaceobj_getfunc) |
8465de90 RP |
1021 | else: |
1022 | vrf = ifaceobj.get_attr_value_first('vrf') | |
1023 | if vrf: | |
05ac52f0 | 1024 | self._iproute2_vrf_map_initialize() |
0ba04b38 | 1025 | self._down_vrf_slave(ifaceobj.name, ifaceobj, None) |
3b01ed76 | 1026 | except Exception as e: |
8465de90 RP |
1027 | self.log_warn(str(e)) |
1028 | ||
1029 | def _query_check_vrf_slave(self, ifaceobj, ifaceobjcurr, vrf): | |
1030 | try: | |
223ba5af | 1031 | master = self.cache.get_master(ifaceobj.name) |
8465de90 | 1032 | if not master or master != vrf: |
586535e8 | 1033 | ifaceobjcurr.update_config_with_status('vrf', str(master), 1) |
8465de90 RP |
1034 | else: |
1035 | ifaceobjcurr.update_config_with_status('vrf', master, 0) | |
3b01ed76 | 1036 | except Exception as e: |
bf3eda91 | 1037 | self.log_error(str(e), ifaceobjcurr) |
8465de90 | 1038 | |
6e16e5ae | 1039 | def _query_check_vrf_dev(self, ifaceobj, ifaceobjcurr, vrf_table): |
8465de90 | 1040 | try: |
223ba5af | 1041 | if not self.cache.link_exists(ifaceobj.name): |
8465de90 RP |
1042 | self.logger.info('%s: vrf: does not exist' %(ifaceobj.name)) |
1043 | return | |
1044 | if vrf_table == 'auto': | |
223ba5af | 1045 | config_table = str(self._get_iproute2_vrf_table(ifaceobj.name) or 0) |
8465de90 RP |
1046 | else: |
1047 | config_table = vrf_table | |
223ba5af JF |
1048 | |
1049 | running_vrf_table = str(self.cache.get_link_info_data_attribute(ifaceobj.name, Link.IFLA_VRF_TABLE)) | |
1050 | ||
1051 | ifaceobjcurr.update_config_with_status('vrf-table', running_vrf_table, config_table != running_vrf_table) | |
1052 | ||
6e16e5ae | 1053 | if not ifupdownflags.flags.WITHDEFAULTS: |
669b422a RP |
1054 | return |
1055 | if self.vrf_helper: | |
6e16e5ae | 1056 | try: |
a193d8d1 | 1057 | utils.exec_command('%s verify %s %s' |
6e16e5ae N |
1058 | %(self.vrf_helper, |
1059 | ifaceobj.name, config_table)) | |
1060 | ifaceobjcurr.update_config_with_status('vrf-helper', | |
1061 | '%s create %s %s' | |
1062 | %(self.vrf_helper, | |
1063 | ifaceobj.name, | |
1064 | config_table), 0) | |
3b01ed76 | 1065 | except Exception as e: |
6e16e5ae N |
1066 | ifaceobjcurr.update_config_with_status('vrf-helper', |
1067 | '%s create %s %s' | |
1068 | %(self.vrf_helper, | |
1069 | ifaceobj.name, | |
1070 | config_table), 1) | |
1071 | pass | |
3b01ed76 | 1072 | except Exception as e: |
8465de90 RP |
1073 | self.log_warn(str(e)) |
1074 | ||
6e16e5ae | 1075 | def _query_check(self, ifaceobj, ifaceobjcurr): |
8465de90 RP |
1076 | try: |
1077 | vrf_table = ifaceobj.get_attr_value_first('vrf-table') | |
1078 | if vrf_table: | |
eb3ce8c8 | 1079 | self._iproute2_vrf_map_initialize(writetodisk=False) |
6e16e5ae | 1080 | self._query_check_vrf_dev(ifaceobj, ifaceobjcurr, vrf_table) |
8465de90 RP |
1081 | else: |
1082 | vrf = ifaceobj.get_attr_value_first('vrf') | |
1083 | if vrf: | |
eb3ce8c8 | 1084 | self._iproute2_vrf_map_initialize(writetodisk=False) |
8465de90 | 1085 | self._query_check_vrf_slave(ifaceobj, ifaceobjcurr, vrf) |
3b01ed76 | 1086 | except Exception as e: |
8465de90 RP |
1087 | self.log_warn(str(e)) |
1088 | ||
8ad5c767 | 1089 | def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None): |
8465de90 | 1090 | try: |
223ba5af | 1091 | kind = self.cache.get_link_kind(ifaceobjrunning.name) |
8465de90 | 1092 | if kind == 'vrf': |
223ba5af JF |
1093 | running_table = self.cache.get_link_info_data_attribute(ifaceobjrunning.name, Link.IFLA_VRF_TABLE) |
1094 | ||
1095 | if running_table is not None: | |
1096 | ifaceobjrunning.update_config('vrf-table', str(running_table)) | |
1097 | return | |
1098 | ||
1099 | slave_kind = self.cache.get_link_slave_kind(ifaceobjrunning.name) | |
5b563460 | 1100 | if slave_kind == 'vrf_slave': |
223ba5af | 1101 | vrf = self.cache.get_master(ifaceobjrunning.name) |
8465de90 RP |
1102 | if vrf: |
1103 | ifaceobjrunning.update_config('vrf', vrf) | |
3b01ed76 | 1104 | except Exception as e: |
8465de90 RP |
1105 | self.log_warn(str(e)) |
1106 | ||
baa909c6 N |
1107 | def _query(self, ifaceobj, **kwargs): |
1108 | if not self.vrf_helper: | |
1109 | return | |
1110 | if (ifaceobj.link_kind & ifaceLinkKind.VRF): | |
1111 | ifaceobj.update_config('vrf-helper', '%s %s' %(self.vrf_helper, | |
1112 | ifaceobj.name)) | |
1113 | ||
223ba5af JF |
1114 | _run_ops = { |
1115 | "pre-up": _up, | |
1116 | "post-down": _down, | |
1117 | "query-running": _query_running, | |
1118 | "query-checkcurr": _query_check, | |
1119 | "query": _query | |
1120 | } | |
8465de90 RP |
1121 | |
1122 | def get_ops(self): | |
1123 | """ returns list of ops supported by this module """ | |
3b01ed76 | 1124 | return list(self._run_ops.keys()) |
8465de90 RP |
1125 | |
1126 | def _init_command_handlers(self): | |
122ef35b | 1127 | if not self.dhclientcmd: |
fc5e1735 | 1128 | self.dhclientcmd = dhclient() |
8465de90 | 1129 | |
8ad5c767 RP |
1130 | def run(self, ifaceobj, operation, query_ifaceobj=None, |
1131 | ifaceobj_getfunc=None, **extra_args): | |
8465de90 RP |
1132 | """ run bond configuration on the interface object passed as argument |
1133 | ||
1134 | Args: | |
1135 | **ifaceobj** (object): iface object | |
1136 | ||
1137 | **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr', | |
1138 | 'query-running' | |
1139 | ||
1140 | Kwargs: | |
1141 | **query_ifaceobj** (object): query check ifaceobject. This is only | |
1142 | valid when op is 'query-checkcurr'. It is an object same as | |
1143 | ifaceobj, but contains running attribute values and its config | |
1144 | status. The modules can use it to return queried running state | |
1145 | of interfaces. status is success if the running state is same | |
1146 | as user required state in ifaceobj. error otherwise. | |
1147 | """ | |
1148 | op_handler = self._run_ops.get(operation) | |
1149 | if not op_handler: | |
1150 | return | |
1151 | self._init_command_handlers() | |
1152 | if operation == 'query-checkcurr': | |
6e16e5ae | 1153 | op_handler(self, ifaceobj, query_ifaceobj) |
8465de90 | 1154 | else: |
8ad5c767 | 1155 | op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc) |
223ba5af JF |
1156 | if self.at_exit: |
1157 | self._iproute2_vrf_map_sync_to_disk() | |
1158 | self.at_exit = False |