]>
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 JF |
19 | |
20 | from ifupdown2.ifupdown.iface import * | |
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 JF |
27 | from ifupdown2.ifupdownaddons.modulebase import moduleBase |
28 | except ImportError: | |
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 JF |
34 | |
35 | from ifupdown.iface import * | |
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() | |
3b01ed76 JF |
118 | self.ip_rule_cache = [b' '.join(r.split()) for r in ip_rules] |
119 | except Exception as e: | |
8465de90 | 120 | self.ip_rule_cache = [] |
a193d8d1 | 121 | self.logger.warn('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() | |
3b01ed76 JF |
126 | self.ip6_rule_cache = [b' '.join(r.split()) for r in ip_rules] |
127 | except Exception as e: | |
8465de90 | 128 | self.ip6_rule_cache = [] |
a193d8d1 | 129 | self.logger.warn('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 ' + | |
266 | 'map (%s)\n' %errstr) | |
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: | |
271 | self.logger.warn('unable to open iproute2 vrf to table ' + | |
272 | 'map (%s)\n' %errstr) | |
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 | |
d486dd0d JF |
351 | for t in range(table_id_start, self.vrf_table_id_end): |
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 RP |
419 | if not vrf_master: |
420 | self.logger.warn('%s: vrf master not found' %ifacename) | |
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) |
df53966d RP |
436 | self.logger.info('%s: table id auto: selected table id %s\n' |
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) | |
122ef35b RP |
468 | except: |
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 JF |
516 | if master_exists: |
517 | if not ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN: | |
223ba5af | 518 | self.netlink.link_up(ifacename) |
79cf5764 | 519 | else: |
d2b35716 | 520 | self.log_error('vrf %s not around, skipping vrf config' |
bf3eda91 | 521 | %(vrfname), ifaceobj) |
3b01ed76 | 522 | except Exception as e: |
bf3eda91 | 523 | self.log_error('%s: %s' %(ifacename, str(e)), ifaceobj) |
8465de90 RP |
524 | |
525 | def _del_vrf_rules(self, vrf_dev_name, vrf_table): | |
526 | pref = 200 | |
527 | ip_rule_out_format = '%s: from all %s %s lookup %s' | |
d486dd0d | 528 | ip_rule_cmd = '%s %s rule del pref %s %s %s table %s' |
8465de90 | 529 | |
4ce47ce4 | 530 | rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name) |
8465de90 | 531 | if rule in self.ip_rule_cache: |
d486dd0d JF |
532 | rule_cmd = ip_rule_cmd %(utils.ip_cmd, |
533 | '', pref, 'oif', vrf_dev_name, | |
25e2386e | 534 | vrf_dev_name) |
a193d8d1 | 535 | utils.exec_command(rule_cmd) |
8465de90 | 536 | |
4ce47ce4 | 537 | rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name) |
8465de90 | 538 | if rule in self.ip_rule_cache: |
d486dd0d JF |
539 | rule_cmd = ip_rule_cmd %(utils.ip_cmd, |
540 | '', pref, 'iif', vrf_dev_name, | |
25e2386e | 541 | vrf_dev_name) |
a193d8d1 | 542 | utils.exec_command(rule_cmd) |
8465de90 | 543 | |
4ce47ce4 | 544 | rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name) |
e6072769 | 545 | if rule in self.ip6_rule_cache: |
d486dd0d JF |
546 | rule_cmd = ip_rule_cmd %(utils.ip_cmd, |
547 | '-6', pref, 'oif', vrf_dev_name, | |
25e2386e | 548 | vrf_dev_name) |
a193d8d1 | 549 | utils.exec_command(rule_cmd) |
8465de90 | 550 | |
4ce47ce4 | 551 | rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name) |
e6072769 | 552 | if rule in self.ip6_rule_cache: |
d486dd0d JF |
553 | rule_cmd = ip_rule_cmd %(utils.ip_cmd, |
554 | '-6', pref, 'iif', vrf_dev_name, | |
25e2386e | 555 | vrf_dev_name) |
a193d8d1 | 556 | utils.exec_command(rule_cmd) |
8465de90 | 557 | |
0aa91758 N |
558 | def _l3mdev_rule(self, ip_rules): |
559 | for rule in ip_rules: | |
560 | if not re.search(r"\d.*from\s+all\s+lookup\s+\W?l3mdev-table\W?", | |
3b01ed76 | 561 | rule.decode("ascii")): |
0aa91758 N |
562 | continue |
563 | return True | |
564 | return False | |
565 | ||
566 | def _rule_cache_fill(self): | |
d486dd0d JF |
567 | ip_rules = utils.exec_command('%s rule show' |
568 | %utils.ip_cmd).splitlines() | |
0aa91758 N |
569 | self.ip_rule_cache = [' '.join(r.split()) for r in ip_rules] |
570 | self.l3mdev4_rule = self._l3mdev_rule(self.ip_rule_cache) | |
d486dd0d JF |
571 | ip_rules = utils.exec_command('%s -6 rule show' |
572 | %utils.ip_cmd).splitlines() | |
0aa91758 N |
573 | self.ip6_rule_cache = [' '.join(r.split()) for r in ip_rules] |
574 | self.l3mdev6_rule = self._l3mdev_rule(self.ip6_rule_cache) | |
575 | ||
8465de90 RP |
576 | def _add_vrf_rules(self, vrf_dev_name, vrf_table): |
577 | pref = 200 | |
578 | ip_rule_out_format = '%s: from all %s %s lookup %s' | |
d486dd0d | 579 | ip_rule_cmd = '%s %s rule add pref %s %s %s table %s' |
659097a0 N |
580 | if self.vrf_fix_local_table: |
581 | self.vrf_fix_local_table = False | |
582 | rule = '0: from all lookup local' | |
583 | if rule in self.ip_rule_cache: | |
584 | try: | |
d486dd0d JF |
585 | utils.exec_command('%s rule del pref 0' |
586 | %utils.ip_cmd) | |
587 | utils.exec_command('%s rule add pref 32765 table local' | |
588 | %utils.ip_cmd) | |
3b01ed76 | 589 | except Exception as e: |
a193d8d1 | 590 | self.logger.info('%s: %s' % (vrf_dev_name, str(e))) |
659097a0 | 591 | pass |
c61672da N |
592 | if rule in self.ip6_rule_cache: |
593 | try: | |
d486dd0d JF |
594 | utils.exec_command('%s -6 rule del pref 0' |
595 | %utils.ip_cmd) | |
596 | utils.exec_command('%s -6 rule add pref 32765 table local' | |
597 | %utils.ip_cmd) | |
3b01ed76 | 598 | except Exception as e: |
a193d8d1 | 599 | self.logger.info('%s: %s' % (vrf_dev_name, str(e))) |
c61672da | 600 | pass |
8465de90 | 601 | |
0aa91758 N |
602 | if not self.l3mdev_checked: |
603 | self._rule_cache_fill() | |
604 | self.l3mdev_checked = True | |
3fcb15fe N |
605 | #Example ip rule |
606 | #200: from all oif blue lookup blue | |
607 | #200: from all iif blue lookup blue | |
608 | ||
609 | rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name) | |
0aa91758 | 610 | if not self.l3mdev4_rule and rule not in self.ip_rule_cache: |
d486dd0d JF |
611 | rule_cmd = ip_rule_cmd %(utils.ip_cmd, |
612 | '', pref, 'oif', vrf_dev_name, | |
25e2386e | 613 | vrf_dev_name) |
a193d8d1 | 614 | utils.exec_command(rule_cmd) |
8465de90 | 615 | |
3fcb15fe | 616 | rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name) |
0aa91758 | 617 | if not self.l3mdev4_rule and rule not in self.ip_rule_cache: |
d486dd0d JF |
618 | rule_cmd = ip_rule_cmd %(utils.ip_cmd, |
619 | '', pref, 'iif', vrf_dev_name, | |
25e2386e | 620 | vrf_dev_name) |
a193d8d1 | 621 | utils.exec_command(rule_cmd) |
8465de90 | 622 | |
3fcb15fe | 623 | rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name) |
0aa91758 | 624 | if not self.l3mdev6_rule and rule not in self.ip6_rule_cache: |
d486dd0d JF |
625 | rule_cmd = ip_rule_cmd %(utils.ip_cmd, |
626 | '-6', pref, 'oif', vrf_dev_name, | |
25e2386e | 627 | vrf_dev_name) |
a193d8d1 | 628 | utils.exec_command(rule_cmd) |
8465de90 | 629 | |
3fcb15fe | 630 | rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name) |
0aa91758 | 631 | if not self.l3mdev6_rule and rule not in self.ip6_rule_cache: |
d486dd0d JF |
632 | rule_cmd = ip_rule_cmd %(utils.ip_cmd, |
633 | '-6', pref, 'iif', vrf_dev_name, | |
25e2386e | 634 | vrf_dev_name) |
a193d8d1 | 635 | utils.exec_command(rule_cmd) |
8465de90 | 636 | |
1b284018 RP |
637 | def _is_address_virtual_slaves(self, vrfobj, config_vrfslaves, |
638 | vrfslave): | |
639 | # Address virtual lines on a vrf slave will create | |
640 | # macvlan devices on the vrf slave and enslave them | |
641 | # to the vrf master. This function checks if the | |
642 | # vrf slave is such a macvlan interface. | |
643 | # XXX: additional possible checks that can be done here | |
644 | # are: | |
645 | # - check if it is also a macvlan device of the | |
d486dd0d | 646 | # format <vrf_slave>-v<int> created by the |
1b284018 | 647 | # address virtual module |
223ba5af | 648 | vrfslave_lowers = self.sysfs.link_get_lowers(vrfslave) |
1b284018 RP |
649 | if vrfslave_lowers: |
650 | if vrfslave_lowers[0] in config_vrfslaves: | |
651 | return True | |
652 | return False | |
653 | ||
4d2c9798 | 654 | def _add_vrf_slaves(self, ifaceobj, ifaceobj_getfunc=None): |
223ba5af | 655 | running_slaves = self.sysfs.link_get_lowers(ifaceobj.name) |
768b4ec5 | 656 | config_slaves = ifaceobj.lowerifaces |
b94e4d24 | 657 | if not config_slaves and not running_slaves: |
867e11a2 | 658 | return |
4d2c9798 RP |
659 | |
660 | if not config_slaves: config_slaves = [] | |
661 | if not running_slaves: running_slaves = [] | |
768b4ec5 RP |
662 | add_slaves = set(config_slaves).difference(set(running_slaves)) |
663 | del_slaves = set(running_slaves).difference(set(config_slaves)) | |
664 | if add_slaves: | |
665 | for s in add_slaves: | |
666 | try: | |
223ba5af | 667 | if not self.cache.link_exists(s): |
d2b35716 | 668 | continue |
09753350 RP |
669 | sobj = None |
670 | if ifaceobj_getfunc: | |
671 | sobj = ifaceobj_getfunc(s) | |
672 | self._up_vrf_slave(s, ifaceobj.name, | |
673 | sobj[0] if sobj else None, | |
674 | ifaceobj_getfunc, True) | |
3b01ed76 | 675 | except Exception as e: |
768b4ec5 RP |
676 | self.logger.info('%s: %s' %(ifaceobj.name, str(e))) |
677 | ||
678 | if del_slaves: | |
679 | for s in del_slaves: | |
680 | try: | |
1b284018 RP |
681 | if self._is_address_virtual_slaves(ifaceobj, |
682 | config_slaves, s): | |
683 | continue | |
aa36221f | 684 | sobj = None |
4d2c9798 RP |
685 | if ifaceobj_getfunc: |
686 | sobj = ifaceobj_getfunc(s) | |
0ba04b38 RP |
687 | self._down_vrf_slave(s, sobj[0] if sobj else None, |
688 | ifaceobj.name) | |
3b01ed76 | 689 | except Exception as e: |
768b4ec5 RP |
690 | self.logger.info('%s: %s' %(ifaceobj.name, str(e))) |
691 | ||
692 | if ifaceobj.link_type == ifaceLinkType.LINK_MASTER: | |
693 | for s in config_slaves: | |
694 | try: | |
7cdb931e | 695 | for slave_ifaceobj in ifaceobj_getfunc(s) or []: |
e7206159 JF |
696 | if slave_ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN: |
697 | raise Exception("link-down yes: keeping VRF slave down") | |
223ba5af | 698 | self.netlink.link_up(s) |
3b01ed76 | 699 | except Exception as e: |
e7206159 | 700 | self.logger.debug("%s: %s" % (s, str(e))) |
768b4ec5 | 701 | pass |
8465de90 | 702 | |
8ad5c767 RP |
703 | def _set_vrf_dev_processed_flag(self, ifaceobj): |
704 | ifaceobj.module_flags[self.name] = \ | |
705 | ifaceobj.module_flags.setdefault(self.name, 0) | \ | |
706 | vrfPrivFlags.PROCESSED | |
707 | ||
708 | def _check_vrf_dev_processed_flag(self, ifaceobj): | |
25e2386e | 709 | if (ifaceobj.module_flags.get(self.name, 0) & vrfPrivFlags.PROCESSED): |
8ad5c767 RP |
710 | return True |
711 | return False | |
8465de90 | 712 | |
8ad5c767 | 713 | def _create_vrf_dev(self, ifaceobj, vrf_table): |
223ba5af | 714 | if not self.cache.link_exists(ifaceobj.name): |
d486dd0d JF |
715 | self._check_vrf_system_reserved_names(ifaceobj) |
716 | ||
c8a3b44e | 717 | if self.vrf_count == self.vrf_max_count: |
9b35cb3e JF |
718 | self.log_error('max vrf count %d hit...not ' |
719 | 'creating vrf' % self.vrf_count, ifaceobj) | |
6f2890fc RP |
720 | if vrf_table == 'auto': |
721 | vrf_table = self._get_avail_vrf_table_id() | |
722 | if not vrf_table: | |
9b35cb3e | 723 | self.log_error('unable to get an auto table id', ifaceobj) |
6f2890fc RP |
724 | self.logger.info('%s: table id auto: selected table id %s\n' |
725 | %(ifaceobj.name, vrf_table)) | |
25e2386e | 726 | else: |
bf3eda91 | 727 | self._iproute2_is_vrf_tableid_inuse(ifaceobj, vrf_table) |
3b01ed76 | 728 | if ifaceobj.name in list(self.system_reserved_rt_tables.keys()): |
f6466fcb | 729 | self.log_error('cannot use system reserved %s table ids' |
3b01ed76 | 730 | %(str(list(self.system_reserved_rt_tables.keys()))), |
bf3eda91 | 731 | ifaceobj) |
d54baa22 RP |
732 | |
733 | if not vrf_table.isdigit(): | |
9b35cb3e | 734 | self.log_error('vrf-table must be an integer or \'auto\'', ifaceobj) |
d54baa22 | 735 | |
6f2890fc RP |
736 | # XXX: If we decide to not allow vrf id usages out of |
737 | # the reserved ifupdown range, then uncomment this code. | |
fd8c6caf RP |
738 | else: |
739 | if (int(vrf_table) < self.vrf_table_id_start or | |
740 | int(vrf_table) > self.vrf_table_id_end): | |
9b35cb3e JF |
741 | self.log_error('vrf table id %s out of reserved range [%d,%d]' |
742 | %(vrf_table, | |
fd8c6caf | 743 | self.vrf_table_id_start, |
bf3eda91 | 744 | self.vrf_table_id_end), ifaceobj) |
2df6a60f | 745 | try: |
223ba5af | 746 | self.netlink.link_add_vrf(ifaceobj.name, vrf_table) |
3b01ed76 | 747 | except Exception as e: |
9b35cb3e | 748 | self.log_error('create failed (%s)\n' % str(e), ifaceobj) |
25e2386e | 749 | if vrf_table != 'auto': |
bf3eda91 | 750 | self._iproute2_vrf_table_entry_add(ifaceobj, vrf_table) |
2df6a60f | 751 | else: |
4d2c9798 RP |
752 | if vrf_table == 'auto': |
753 | vrf_table = self._get_iproute2_vrf_table(ifaceobj.name) | |
d486dd0d | 754 | if not vrf_table and not ifupdownflags.flags.DRYRUN: |
9b35cb3e | 755 | self.log_error('unable to get vrf table id', ifaceobj) |
6f2890fc | 756 | |
2df6a60f | 757 | # if the device exists, check if table id is same |
223ba5af JF |
758 | running_table = self.cache.get_link_info_data_attribute(ifaceobj.name, Link.IFLA_VRF_TABLE) |
759 | ||
760 | if running_table is not None and vrf_table != str(running_table): | |
761 | self.log_error('cannot change vrf table id,running table id' | |
762 | ' %s is different from config id %s' | |
763 | % (running_table, vrf_table), ifaceobj) | |
4d2c9798 RP |
764 | return vrf_table |
765 | ||
25e2386e | 766 | def _up_vrf_helper(self, ifaceobj, vrf_table): |
c4e05f9f RP |
767 | mode = "" |
768 | if ifupdownflags.flags.PERFMODE: | |
769 | mode = "boot" | |
25e2386e | 770 | if self.vrf_helper: |
a193d8d1 JF |
771 | utils.exec_command('%s create %s %s %s' % |
772 | (self.vrf_helper, | |
773 | ifaceobj.name, | |
774 | vrf_table, | |
775 | mode)) | |
25e2386e | 776 | |
4d2c9798 RP |
777 | def _up_vrf_dev(self, ifaceobj, vrf_table, add_slaves=True, |
778 | ifaceobj_getfunc=None): | |
8ad5c767 RP |
779 | |
780 | # if vrf dev is already processed return. This can happen | |
0ba04b38 RP |
781 | # if we the slave was configured before. |
782 | # see self._up_vrf_slave_without_master | |
8ad5c767 RP |
783 | if self._check_vrf_dev_processed_flag(ifaceobj): |
784 | return True | |
785 | ||
25e2386e RP |
786 | try: |
787 | vrf_table = self._create_vrf_dev(ifaceobj, vrf_table) | |
3b01ed76 | 788 | except Exception as e: |
bf3eda91 | 789 | self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj) |
d486dd0d | 790 | |
8ad5c767 | 791 | try: |
8ad5c767 | 792 | self._add_vrf_rules(ifaceobj.name, vrf_table) |
6369e774 | 793 | self._up_vrf_helper(ifaceobj, vrf_table) |
8ad5c767 | 794 | if add_slaves: |
4d2c9798 | 795 | self._add_vrf_slaves(ifaceobj, ifaceobj_getfunc) |
8ad5c767 | 796 | self._set_vrf_dev_processed_flag(ifaceobj) |
7cdb931e JF |
797 | |
798 | if not ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN: | |
223ba5af | 799 | self.netlink.link_up(ifaceobj.name) |
3b01ed76 | 800 | except Exception as e: |
bf3eda91 | 801 | self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj) |
8ad5c767 | 802 | |
223ba5af | 803 | def _kill_ssh_connections(self, ifacename, ifaceobj): |
5c5a7b93 | 804 | try: |
223ba5af JF |
805 | running_addrs_list = [str(ip) for ip in self.cache.get_ifupdown2_addresses_list( |
806 | ifaceobj_list=[ifaceobj], | |
807 | ifname=ifacename, | |
808 | )] | |
809 | ||
810 | if not running_addrs_list: | |
5c5a7b93 | 811 | return |
223ba5af JF |
812 | |
813 | iplist = [i.split('/', 1)[0] for i in running_addrs_list] | |
814 | ||
0ba04b38 RP |
815 | if not iplist: |
816 | return | |
817 | proc=[] | |
5c5a7b93 | 818 | #Example output: |
d486dd0d | 819 | #ESTAB 0 0 10.0.1.84:ssh 10.0.1.228:45186 |
5c5a7b93 | 820 | #users:(("sshd",pid=2528,fd=3)) |
d486dd0d | 821 | cmdl = [utils.ss_cmd, '-t', '-p'] |
a193d8d1 | 822 | for line in utils.exec_commandl(cmdl).splitlines(): |
5c5a7b93 N |
823 | citems = line.split() |
824 | addr = None | |
825 | if '%' in citems[3]: | |
826 | addr = citems[3].split('%')[0] | |
827 | elif ':ssh' in citems[3]: | |
828 | addr = citems[3].split(':')[0] | |
829 | if not addr: | |
830 | continue | |
0ba04b38 | 831 | if addr in iplist: |
5c5a7b93 N |
832 | if len(citems) == 6: |
833 | proc.append(citems[5].split(',')[1].split('=')[1]) | |
834 | ||
835 | if not proc: | |
836 | return | |
be276272 N |
837 | pid = None |
838 | # outpt of '/usr/bin/pstree -Aps <pid>': | |
839 | # 'systemd(1)---sshd(990)---sshd(16112)---sshd(16126)---bash(16127)---sudo(16756)---ifreload(16761)---pstree(16842)\n' | |
840 | # get the above output to following format | |
841 | # ['systemd(1)', 'sshd(990)', 'sshd(16112)', 'sshd(16126)', 'bash(16127)', 'sudo(16756)', 'ifreload(16761)', 'pstree(16850)'] | |
d486dd0d JF |
842 | pstree = list(reversed(utils.exec_command('%s -Aps %s' % |
843 | (utils.pstree_cmd, os.getpid())).strip().split('---'))) | |
be276272 N |
844 | for index, process in enumerate(pstree): |
845 | # check the parent of SSH process to make sure | |
846 | # we don't kill SSH server or systemd process | |
847 | if 'sshd' in process and 'sshd' in pstree[index + 1]: | |
3b01ed76 | 848 | pid = [x for x in process if x.isdigit()] |
be276272 | 849 | break |
aa36221f RP |
850 | self.logger.info("%s: killing active ssh sessions: %s" |
851 | %(ifacename, str(proc))) | |
cbdde74e RP |
852 | |
853 | if ifupdownflags.flags.DRYRUN: | |
854 | return | |
5c5a7b93 N |
855 | for id in proc: |
856 | if id != pid: | |
857 | try: | |
aa36221f | 858 | os.kill(int(id), signal.SIGINT) |
5c5a7b93 N |
859 | except OSError as e: |
860 | continue | |
0ba04b38 RP |
861 | |
862 | # Kill current SSH client | |
5c5a7b93 | 863 | if pid in proc: |
32f6e6ca | 864 | try: |
0ba04b38 | 865 | forkret = os.fork() |
3b01ed76 | 866 | except OSError as e: |
0ba04b38 RP |
867 | self.logger.info("fork error : %s [%d]" % (e.strerror, e.errno)) |
868 | if (forkret == 0): # The first child. | |
869 | try: | |
870 | os.setsid() | |
871 | self.logger.info("%s: ifreload continuing in the background" %ifacename) | |
3b01ed76 JF |
872 | except OSError as xxx_todo_changeme: |
873 | (err_no, err_message) = xxx_todo_changeme.args | |
0ba04b38 RP |
874 | self.logger.info("os.setsid failed: errno=%d: %s" % (err_no, err_message)) |
875 | self.logger.info("pid=%d pgid=%d" % (os.getpid(), os.getpgid(0))) | |
5c5a7b93 | 876 | try: |
aa36221f RP |
877 | self.logger.info("%s: killing our session: %s" |
878 | %(ifacename, str(proc))) | |
879 | os.kill(int(pid), signal.SIGINT) | |
5c5a7b93 N |
880 | return |
881 | except OSError as e: | |
882 | return | |
3b01ed76 | 883 | except Exception as e: |
5c5a7b93 N |
884 | self.logger.info('%s: %s' %(ifacename, str(e))) |
885 | ||
8ad5c767 | 886 | def _up(self, ifaceobj, ifaceobj_getfunc=None): |
8465de90 RP |
887 | try: |
888 | vrf_table = ifaceobj.get_attr_value_first('vrf-table') | |
889 | if vrf_table: | |
05ac52f0 | 890 | self._iproute2_vrf_map_initialize() |
09753350 | 891 | # This is a vrf device |
4d2c9798 | 892 | self._up_vrf_dev(ifaceobj, vrf_table, True, ifaceobj_getfunc) |
8465de90 RP |
893 | else: |
894 | vrf = ifaceobj.get_attr_value_first('vrf') | |
895 | if vrf: | |
223ba5af JF |
896 | if not self.cache.link_exists(ifaceobj.name): |
897 | self.logger.warning("%s: device not found - please check your configuration" % ifaceobj.name) | |
898 | return | |
899 | ||
05ac52f0 | 900 | self._iproute2_vrf_map_initialize() |
09753350 | 901 | # This is a vrf slave |
8ad5c767 RP |
902 | self._up_vrf_slave(ifaceobj.name, vrf, ifaceobj, |
903 | ifaceobj_getfunc) | |
d486dd0d | 904 | elif not ifupdownflags.flags.PERFMODE: |
aa36221f | 905 | # check if we were a slave before |
223ba5af | 906 | master = self.cache.get_master(ifaceobj.name) |
aa36221f | 907 | if master: |
717cee31 | 908 | self._iproute2_vrf_map_initialize() |
aa36221f | 909 | if self._is_vrf_dev(master): |
0ba04b38 RP |
910 | self._down_vrf_slave(ifaceobj.name, ifaceobj, |
911 | master) | |
3b01ed76 | 912 | except Exception as e: |
bf3eda91 | 913 | self.log_error(str(e), ifaceobj) |
8465de90 | 914 | |
25e2386e | 915 | def _down_vrf_helper(self, ifaceobj, vrf_table): |
c4e05f9f RP |
916 | mode = "" |
917 | if ifupdownflags.flags.PERFMODE: | |
918 | mode = "boot" | |
25e2386e | 919 | if self.vrf_helper: |
a193d8d1 JF |
920 | utils.exec_command('%s delete %s %s %s' % |
921 | (self.vrf_helper, | |
922 | ifaceobj.name, | |
923 | vrf_table, | |
924 | mode)) | |
25e2386e | 925 | |
223ba5af | 926 | def _close_sockets(self, ifacename): |
6fbd7444 RP |
927 | if not self.vrf_close_socks_on_down: |
928 | return | |
929 | ||
223ba5af JF |
930 | try: |
931 | ifindex = self.cache.get_ifindex(ifacename) | |
932 | except Exception as e: | |
933 | self.logger.debug("%s: vrf: close sockets error: %s" % str(e)) | |
934 | ifindex = 0 | |
935 | ||
936 | if not ifindex: | |
937 | return | |
938 | ||
6fbd7444 | 939 | try: |
d486dd0d JF |
940 | utils.exec_command('%s -aK \"dev == %s\"' |
941 | %(utils.ss_cmd, ifindex)) | |
3b01ed76 | 942 | except Exception as e: |
6fbd7444 | 943 | self.logger.info('%s: closing socks using ss' |
223ba5af | 944 | ' failed (%s)\n' %(ifacename, str(e))) |
6fbd7444 RP |
945 | pass |
946 | ||
c4be5481 | 947 | def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None): |
5c5a7b93 | 948 | |
223ba5af | 949 | if not self.cache.link_exists(ifaceobj.name): |
6fbd7444 RP |
950 | return |
951 | ||
8465de90 RP |
952 | if vrf_table == 'auto': |
953 | vrf_table = self._get_iproute2_vrf_table(ifaceobj.name) | |
aa36221f | 954 | |
223ba5af | 955 | running_slaves = self.sysfs.link_get_lowers(ifaceobj.name) |
2365b3c9 RP |
956 | if running_slaves: |
957 | for s in running_slaves: | |
958 | if ifaceobj_getfunc: | |
959 | sobj = ifaceobj_getfunc(s) | |
960 | try: | |
961 | self._handle_existing_connections(sobj[0] | |
962 | if sobj else None, | |
0ba04b38 | 963 | ifaceobj.name) |
3b01ed76 | 964 | except Exception as e: |
2365b3c9 RP |
965 | self.logger.info('%s: %s' %(ifaceobj.name, str(e))) |
966 | pass | |
967 | try: | |
223ba5af JF |
968 | self.netlink.addr_flush(s) |
969 | self.netlink.link_down(s) | |
3b01ed76 | 970 | except Exception as e: |
223ba5af | 971 | self.logger.info('%s: %s' %(s, str(e))) |
2365b3c9 | 972 | pass |
768b4ec5 | 973 | |
40804f1a RP |
974 | try: |
975 | self._down_vrf_helper(ifaceobj, vrf_table) | |
3b01ed76 | 976 | except Exception as e: |
40804f1a RP |
977 | self.logger.warn('%s: %s' %(ifaceobj.name, str(e))) |
978 | pass | |
25e2386e | 979 | |
768b4ec5 | 980 | try: |
f1c92482 | 981 | self._del_vrf_rules(ifaceobj.name, vrf_table) |
3b01ed76 | 982 | except Exception as e: |
768b4ec5 RP |
983 | self.logger.info('%s: %s' %(ifaceobj.name, str(e))) |
984 | pass | |
985 | ||
223ba5af | 986 | self._close_sockets(ifaceobj.name) |
6fbd7444 | 987 | |
223ba5af JF |
988 | try: |
989 | self.netlink.link_del(ifaceobj.name) | |
3b01ed76 | 990 | except Exception as e: |
223ba5af JF |
991 | self.logger.info('%s: %s' %(ifaceobj.name, str(e))) |
992 | pass | |
6fbd7444 | 993 | |
f1c92482 RP |
994 | try: |
995 | self._iproute2_vrf_table_entry_del(vrf_table) | |
3b01ed76 | 996 | except Exception as e: |
768b4ec5 RP |
997 | self.logger.info('%s: %s' %(ifaceobj.name, str(e))) |
998 | pass | |
8465de90 | 999 | |
f1c92482 | 1000 | |
0ba04b38 | 1001 | def _down_vrf_slave(self, ifacename, ifaceobj=None, vrfname=None): |
8465de90 | 1002 | try: |
0ba04b38 | 1003 | self._handle_existing_connections(ifaceobj, vrfname) |
223ba5af | 1004 | self.netlink.link_set_nomaster(ifacename) |
dc3f4c45 RP |
1005 | # Down this slave only if it is a slave ifupdown2 manages. |
1006 | # we dont want to down slaves that maybe up'ed by | |
1007 | # somebody else. One such example is a macvlan device | |
1008 | # which ifupdown2 addressvirtual addon module auto creates | |
1009 | if ifaceobj: | |
223ba5af | 1010 | self.netlink.link_down(ifacename) |
3b01ed76 | 1011 | except Exception as e: |
768b4ec5 | 1012 | self.logger.warn('%s: %s' %(ifacename, str(e))) |
8465de90 | 1013 | |
8ad5c767 | 1014 | def _down(self, ifaceobj, ifaceobj_getfunc=None): |
8465de90 RP |
1015 | try: |
1016 | vrf_table = ifaceobj.get_attr_value_first('vrf-table') | |
1017 | if vrf_table: | |
05ac52f0 | 1018 | self._iproute2_vrf_map_initialize() |
c4be5481 | 1019 | self._down_vrf_dev(ifaceobj, vrf_table, ifaceobj_getfunc) |
8465de90 RP |
1020 | else: |
1021 | vrf = ifaceobj.get_attr_value_first('vrf') | |
1022 | if vrf: | |
05ac52f0 | 1023 | self._iproute2_vrf_map_initialize() |
0ba04b38 | 1024 | self._down_vrf_slave(ifaceobj.name, ifaceobj, None) |
3b01ed76 | 1025 | except Exception as e: |
8465de90 RP |
1026 | self.log_warn(str(e)) |
1027 | ||
1028 | def _query_check_vrf_slave(self, ifaceobj, ifaceobjcurr, vrf): | |
1029 | try: | |
223ba5af | 1030 | master = self.cache.get_master(ifaceobj.name) |
8465de90 | 1031 | if not master or master != vrf: |
586535e8 | 1032 | ifaceobjcurr.update_config_with_status('vrf', str(master), 1) |
8465de90 RP |
1033 | else: |
1034 | ifaceobjcurr.update_config_with_status('vrf', master, 0) | |
3b01ed76 | 1035 | except Exception as e: |
bf3eda91 | 1036 | self.log_error(str(e), ifaceobjcurr) |
8465de90 | 1037 | |
6e16e5ae | 1038 | def _query_check_vrf_dev(self, ifaceobj, ifaceobjcurr, vrf_table): |
8465de90 | 1039 | try: |
223ba5af | 1040 | if not self.cache.link_exists(ifaceobj.name): |
8465de90 RP |
1041 | self.logger.info('%s: vrf: does not exist' %(ifaceobj.name)) |
1042 | return | |
1043 | if vrf_table == 'auto': | |
223ba5af | 1044 | config_table = str(self._get_iproute2_vrf_table(ifaceobj.name) or 0) |
8465de90 RP |
1045 | else: |
1046 | config_table = vrf_table | |
223ba5af JF |
1047 | |
1048 | running_vrf_table = str(self.cache.get_link_info_data_attribute(ifaceobj.name, Link.IFLA_VRF_TABLE)) | |
1049 | ||
1050 | ifaceobjcurr.update_config_with_status('vrf-table', running_vrf_table, config_table != running_vrf_table) | |
1051 | ||
6e16e5ae | 1052 | if not ifupdownflags.flags.WITHDEFAULTS: |
669b422a RP |
1053 | return |
1054 | if self.vrf_helper: | |
6e16e5ae | 1055 | try: |
a193d8d1 | 1056 | utils.exec_command('%s verify %s %s' |
6e16e5ae N |
1057 | %(self.vrf_helper, |
1058 | ifaceobj.name, config_table)) | |
1059 | ifaceobjcurr.update_config_with_status('vrf-helper', | |
1060 | '%s create %s %s' | |
1061 | %(self.vrf_helper, | |
1062 | ifaceobj.name, | |
1063 | config_table), 0) | |
3b01ed76 | 1064 | except Exception as e: |
6e16e5ae N |
1065 | ifaceobjcurr.update_config_with_status('vrf-helper', |
1066 | '%s create %s %s' | |
1067 | %(self.vrf_helper, | |
1068 | ifaceobj.name, | |
1069 | config_table), 1) | |
1070 | pass | |
3b01ed76 | 1071 | except Exception as e: |
8465de90 RP |
1072 | self.log_warn(str(e)) |
1073 | ||
6e16e5ae | 1074 | def _query_check(self, ifaceobj, ifaceobjcurr): |
8465de90 RP |
1075 | try: |
1076 | vrf_table = ifaceobj.get_attr_value_first('vrf-table') | |
1077 | if vrf_table: | |
eb3ce8c8 | 1078 | self._iproute2_vrf_map_initialize(writetodisk=False) |
6e16e5ae | 1079 | self._query_check_vrf_dev(ifaceobj, ifaceobjcurr, vrf_table) |
8465de90 RP |
1080 | else: |
1081 | vrf = ifaceobj.get_attr_value_first('vrf') | |
1082 | if vrf: | |
eb3ce8c8 | 1083 | self._iproute2_vrf_map_initialize(writetodisk=False) |
8465de90 | 1084 | self._query_check_vrf_slave(ifaceobj, ifaceobjcurr, vrf) |
3b01ed76 | 1085 | except Exception as e: |
8465de90 RP |
1086 | self.log_warn(str(e)) |
1087 | ||
8ad5c767 | 1088 | def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None): |
8465de90 | 1089 | try: |
223ba5af | 1090 | kind = self.cache.get_link_kind(ifaceobjrunning.name) |
8465de90 | 1091 | if kind == 'vrf': |
223ba5af JF |
1092 | running_table = self.cache.get_link_info_data_attribute(ifaceobjrunning.name, Link.IFLA_VRF_TABLE) |
1093 | ||
1094 | if running_table is not None: | |
1095 | ifaceobjrunning.update_config('vrf-table', str(running_table)) | |
1096 | return | |
1097 | ||
1098 | slave_kind = self.cache.get_link_slave_kind(ifaceobjrunning.name) | |
5b563460 | 1099 | if slave_kind == 'vrf_slave': |
223ba5af | 1100 | vrf = self.cache.get_master(ifaceobjrunning.name) |
8465de90 RP |
1101 | if vrf: |
1102 | ifaceobjrunning.update_config('vrf', vrf) | |
3b01ed76 | 1103 | except Exception as e: |
8465de90 RP |
1104 | self.log_warn(str(e)) |
1105 | ||
baa909c6 N |
1106 | def _query(self, ifaceobj, **kwargs): |
1107 | if not self.vrf_helper: | |
1108 | return | |
1109 | if (ifaceobj.link_kind & ifaceLinkKind.VRF): | |
1110 | ifaceobj.update_config('vrf-helper', '%s %s' %(self.vrf_helper, | |
1111 | ifaceobj.name)) | |
1112 | ||
223ba5af JF |
1113 | _run_ops = { |
1114 | "pre-up": _up, | |
1115 | "post-down": _down, | |
1116 | "query-running": _query_running, | |
1117 | "query-checkcurr": _query_check, | |
1118 | "query": _query | |
1119 | } | |
8465de90 RP |
1120 | |
1121 | def get_ops(self): | |
1122 | """ returns list of ops supported by this module """ | |
3b01ed76 | 1123 | return list(self._run_ops.keys()) |
8465de90 RP |
1124 | |
1125 | def _init_command_handlers(self): | |
122ef35b | 1126 | if not self.dhclientcmd: |
fc5e1735 | 1127 | self.dhclientcmd = dhclient() |
8465de90 | 1128 | |
8ad5c767 RP |
1129 | def run(self, ifaceobj, operation, query_ifaceobj=None, |
1130 | ifaceobj_getfunc=None, **extra_args): | |
8465de90 RP |
1131 | """ run bond configuration on the interface object passed as argument |
1132 | ||
1133 | Args: | |
1134 | **ifaceobj** (object): iface object | |
1135 | ||
1136 | **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr', | |
1137 | 'query-running' | |
1138 | ||
1139 | Kwargs: | |
1140 | **query_ifaceobj** (object): query check ifaceobject. This is only | |
1141 | valid when op is 'query-checkcurr'. It is an object same as | |
1142 | ifaceobj, but contains running attribute values and its config | |
1143 | status. The modules can use it to return queried running state | |
1144 | of interfaces. status is success if the running state is same | |
1145 | as user required state in ifaceobj. error otherwise. | |
1146 | """ | |
1147 | op_handler = self._run_ops.get(operation) | |
1148 | if not op_handler: | |
1149 | return | |
1150 | self._init_command_handlers() | |
1151 | if operation == 'query-checkcurr': | |
6e16e5ae | 1152 | op_handler(self, ifaceobj, query_ifaceobj) |
8465de90 | 1153 | else: |
8ad5c767 | 1154 | op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc) |
223ba5af JF |
1155 | if self.at_exit: |
1156 | self._iproute2_vrf_map_sync_to_disk() | |
1157 | self.at_exit = False |