]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/addons/vlan.py
addons: address: fix merge-indentation issue
[mirror_ifupdown2.git] / ifupdown2 / addons / vlan.py
1 #!/usr/bin/env python3
2 #
3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
5 #
6
7 try:
8 from ifupdown2.lib.addon import Addon
9 from ifupdown2.ifupdown.iface import ifaceType, ifaceLinkKind, ifaceStatus
10 from ifupdown2.nlmanager.nlmanager import Link
11 from ifupdown2.ifupdownaddons.modulebase import moduleBase
12 from ifupdown2.ifupdown.utils import utils
13 from ifupdown2.lib.exceptions import RetryCMD
14 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
15 import ifupdown2.ifupdown.policymanager as policymanager
16 except (ImportError, ModuleNotFoundError):
17 from lib.addon import Addon
18 from ifupdown.iface import ifaceType, ifaceLinkKind, ifaceStatus
19 from nlmanager.nlmanager import Link
20 from ifupdownaddons.modulebase import moduleBase
21 from ifupdown.utils import utils
22 from lib.exceptions import RetryCMD
23 import ifupdown.ifupdownflags as ifupdownflags
24 import ifupdown.policymanager as policymanager
25
26
27 class vlan(Addon, moduleBase):
28 """ ifupdown2 addon module to configure vlans """
29
30 _modinfo = {
31 "mhelp": "vlan module configures vlan interfaces. "
32 "This module understands vlan interfaces with dot "
33 "notations. eg swp1.100. Vlan interfaces with any "
34 "other names need to have raw device and vlan id attributes",
35 "attrs": {
36 "vlan-raw-device": {
37 "help": "vlan raw device",
38 "validvals": ["<interface>"]
39 },
40 "vlan-id": {
41 "help": "vlan id",
42 "validrange": ["0", "4096"]
43 },
44 "vlan-protocol": {
45 "help": "vlan protocol",
46 "default": "802.1q",
47 "validvals": ["802.1q", "802.1ad"],
48 "example": ["vlan-protocol 802.1q"]
49 },
50 "vlan-bridge-binding": {
51 "help": "The link state of the vlan device may need to track only the state of the subset of ports "
52 "that are also members of the corresponding vlan, rather than that of all ports. Add a flag to "
53 "specify a vlan bridge binding mode, by which the link state is no longer automatically "
54 "transferred from the lower device, but is instead determined by the bridge ports that are "
55 "members of the vlan.",
56 "default": "off",
57 "validvals": ["on", "off"],
58 "example": ["vlan-bridge-binding on"]
59 }
60 }
61 }
62
63 def __init__(self, *args, **kargs):
64 Addon.__init__(self)
65 moduleBase.__init__(self, *args, **kargs)
66
67 def _is_vlan_device(self, ifaceobj):
68 vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device')
69 if vlan_raw_device:
70 return True
71 elif '.' in ifaceobj.name:
72 try:
73 if self._get_vlan_id(ifaceobj) != -1:
74 return True
75 except Exception:
76 pass
77 return False
78
79 @staticmethod
80 def _is_vlan_by_name(ifacename):
81 return '.' in ifacename
82
83 @staticmethod
84 def _get_vlan_raw_device_from_ifacename(ifacename):
85 """ Returns vlan raw device from ifname
86 Example:
87 Returns eth0 for ifname eth0.100
88 Returns eth0.100 for ifname eth0.100.200
89 Returns None if vlan raw device name cannot
90 be determined
91 """
92 vlist = ifacename.split('.', 2)
93 if len(vlist) == 2:
94 return vlist[0]
95 elif len(vlist) == 3:
96 return vlist[0] + "." + vlist[1]
97 return None
98
99 def _get_vlan_raw_device(self, ifaceobj):
100 vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device')
101 if vlan_raw_device:
102 return vlan_raw_device
103 return self._get_vlan_raw_device_from_ifacename(ifaceobj.name)
104
105 def get_dependent_ifacenames(self, ifaceobj, ifaceobjs_all=None, old_ifaceobjs=False):
106 if not self._is_vlan_device(ifaceobj):
107 return None
108 ifaceobj.link_kind |= ifaceLinkKind.VLAN
109 return [self._get_vlan_raw_device(ifaceobj)]
110
111 def _bridge_vid_add_del(self, bridgename, vlanid, add=True):
112 """ If the lower device is a vlan aware bridge, add/del the vlanid
113 to the bridge """
114 if self.cache.bridge_is_vlan_aware(bridgename):
115 if add:
116 self.netlink.link_add_bridge_vlan(bridgename, vlanid)
117 else:
118 self.netlink.link_del_bridge_vlan(bridgename, vlanid)
119
120 def _bridge_vid_check(self, ifaceobjcurr, bridgename, vlanid):
121 """ If the lower device is a vlan aware bridge, check if the vlanid
122 is configured on the bridge """
123 if not self.cache.bridge_is_vlan_aware(bridgename):
124 return
125 _, vids = self.cache.get_pvid_and_vids(bridgename)
126 if not vids or vlanid not in vids:
127 ifaceobjcurr.status = ifaceStatus.ERROR
128 ifaceobjcurr.status_str = 'bridge vid error'
129
130 def _up(self, ifaceobj):
131 vlanid = self._get_vlan_id(ifaceobj)
132 if vlanid == -1:
133 raise Exception('could not determine vlanid')
134 vlanrawdevice = self._get_vlan_raw_device(ifaceobj)
135 if not vlanrawdevice:
136 raise Exception('could not determine vlan raw device')
137
138 ifname = ifaceobj.name
139
140 if ifupdownflags.flags.PERFMODE:
141 cached_vlan_ifla_info_data = {}
142 else:
143 cached_vlan_ifla_info_data = self.cache.get_link_info_data(ifname)
144
145 vlan_bridge_binding = ifaceobj.get_attr_value_first("vlan-bridge-binding")
146
147 if not vlan_bridge_binding:
148 vlan_bridge_binding = policymanager.policymanager_api.get_attr_default(
149 self.__class__.__name__,
150 "vlan-bridge-binding"
151 ) or self.get_attr_default_value("vlan-bridge-binding")
152
153 bool_vlan_bridge_binding = utils.get_boolean_from_string(vlan_bridge_binding)
154
155 vlan_protocol = ifaceobj.get_attr_value_first('vlan-protocol')
156 cached_vlan_protocol = cached_vlan_ifla_info_data.get(Link.IFLA_VLAN_PROTOCOL)
157
158 if not vlan_protocol:
159 vlan_protocol = self.get_attr_default_value('vlan-protocol')
160
161 if cached_vlan_protocol and vlan_protocol.lower() != cached_vlan_protocol.lower():
162 raise Exception('%s: cannot change vlan-protocol to %s: operation not supported. '
163 'Please delete the device with \'ifdown %s\' and recreate it to '
164 'apply the change.'
165 % (ifaceobj.name, vlan_protocol, ifaceobj.name))
166
167 cached_vlan_id = cached_vlan_ifla_info_data.get(Link.IFLA_VLAN_ID)
168 if cached_vlan_id is not None and vlanid != cached_vlan_id:
169 raise Exception('%s: cannot change vlan-id to %s: operation not supported. '
170 'Please delete the device with \'ifdown %s\' and recreate it to '
171 'apply the change.'
172 % (ifaceobj.name, vlanid, ifaceobj.name))
173
174 if not ifupdownflags.flags.PERFMODE:
175
176 vlan_exists = self.cache.link_exists(ifaceobj.name)
177
178 if vlan_exists:
179 user_vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device')
180 cached_vlan_raw_device = self.cache.get_lower_device_ifname(ifname)
181
182 if cached_vlan_raw_device and user_vlan_raw_device and cached_vlan_raw_device != user_vlan_raw_device:
183 raise Exception('%s: cannot change vlan-raw-device from %s to %s: operation not supported. '
184 'Please delete the device with \'ifdown %s\' and recreate it to apply the change.'
185 % (ifaceobj.name, cached_vlan_raw_device, user_vlan_raw_device, ifaceobj.name))
186
187 if not self.cache.link_exists(vlanrawdevice):
188 if ifupdownflags.flags.DRYRUN:
189 return
190 else:
191 raise Exception('rawdevice %s not present' % vlanrawdevice)
192 if vlan_exists:
193
194 # vlan-bridge-binding has changed we need to update it
195 if vlan_bridge_binding is not None and bool_vlan_bridge_binding != cached_vlan_ifla_info_data.get(Link.IFLA_VLAN_FLAGS, {}).get(Link.VLAN_FLAG_BRIDGE_BINDING, False):
196 self.logger.info("%s: mismatch detected: resetting: vlan-bridge-binding %s" % (ifname, vlan_bridge_binding))
197 self.netlink.link_add_vlan(vlanrawdevice, ifaceobj.name, vlanid, vlan_protocol, bool_vlan_bridge_binding)
198
199 self._bridge_vid_add_del(vlanrawdevice, vlanid)
200 return
201
202 try:
203 self.netlink.link_add_vlan(vlanrawdevice, ifaceobj.name, vlanid, vlan_protocol, bool_vlan_bridge_binding if vlan_bridge_binding is not None else None)
204 except RetryCMD as e:
205 self.logger.info("%s: attempting to create vlan without bridge_binding (capability not detected on the system)" % ifaceobj.name)
206 utils.exec_command(e.cmd)
207 self._bridge_vid_add_del(vlanrawdevice, vlanid)
208
209 def _down(self, ifaceobj):
210 vlanid = self._get_vlan_id(ifaceobj)
211 if vlanid == -1:
212 raise Exception('could not determine vlanid')
213 vlanrawdevice = self._get_vlan_raw_device(ifaceobj)
214 if not vlanrawdevice:
215 raise Exception('could not determine vlan raw device')
216 if not ifupdownflags.flags.PERFMODE and not self.cache.link_exists(ifaceobj.name):
217 return
218 try:
219 self.netlink.link_del(ifaceobj.name)
220 self._bridge_vid_add_del(vlanrawdevice, vlanid, add=False)
221 except Exception as e:
222 self.log_warn(str(e))
223
224 def _query_check(self, ifaceobj, ifaceobjcurr):
225 if not self.cache.link_exists(ifaceobj.name):
226 return
227
228 ifname = ifaceobj.name
229 cached_vlan_info_data = self.cache.get_link_info_data(ifname)
230
231 if '.' not in ifaceobj.name:
232 # if vlan name is not in the dot format, check its running state
233
234 cached_vlan_raw_device = self.cache.get_lower_device_ifname(ifname)
235
236 #
237 # vlan-raw-device
238 #
239 ifaceobjcurr.update_config_with_status(
240 'vlan-raw-device',
241 cached_vlan_raw_device,
242 cached_vlan_raw_device != ifaceobj.get_attr_value_first('vlan-raw-device')
243 )
244
245 #
246 # vlan-id
247 #
248 vlanid_config = ifaceobj.get_attr_value_first('vlan-id')
249 if not vlanid_config:
250 vlanid_config = str(self._get_vlan_id(ifaceobj))
251
252 cached_vlan_id = cached_vlan_info_data.get(Link.IFLA_VLAN_ID)
253 cached_vlan_id_str = str(cached_vlan_id)
254 ifaceobjcurr.update_config_with_status('vlan-id', cached_vlan_id_str, vlanid_config != cached_vlan_id_str)
255
256 #
257 # vlan-protocol (dot or not dot format)
258 #
259 protocol_config = ifaceobj.get_attr_value_first('vlan-protocol')
260 if protocol_config:
261
262 cached_vlan_protocol = cached_vlan_info_data.get(Link.IFLA_VLAN_PROTOCOL)
263
264 if protocol_config.upper() != cached_vlan_protocol.upper():
265 ifaceobjcurr.update_config_with_status(
266 'vlan-protocol',
267 cached_vlan_protocol,
268 1
269 )
270 else:
271 ifaceobjcurr.update_config_with_status(
272 'vlan-protocol',
273 protocol_config,
274 0
275 )
276
277 if '.' not in ifaceobj.name:
278 #
279 # vlan-bridge-binding
280 #
281 vlan_bridge_binding = ifaceobj.get_attr_value_first("vlan-bridge-binding")
282 if vlan_bridge_binding:
283 cached_vlan_bridge_binding = cached_vlan_info_data.get(Link.IFLA_VLAN_FLAGS, {}).get(
284 Link.VLAN_FLAG_BRIDGE_BINDING, False)
285
286 ifaceobjcurr.update_config_with_status(
287 "vlan-bridge-binding",
288 "on" if cached_vlan_bridge_binding else "off",
289 cached_vlan_bridge_binding != utils.get_boolean_from_string(vlan_bridge_binding)
290 )
291
292 self._bridge_vid_check(ifaceobjcurr, cached_vlan_raw_device, cached_vlan_id)
293
294 def _query_running(self, ifaceobjrunning):
295 ifname = ifaceobjrunning.name
296
297 if not self.cache.link_exists(ifname):
298 return
299
300 if self.cache.get_link_kind(ifname) != 'vlan':
301 return
302
303 # If vlan name is not in the dot format, get the
304 # vlan dev and vlan id
305 if '.' in ifname:
306 return
307
308 cached_vlan_info_data = self.cache.get_link_info_data(ifname)
309
310 for attr_name, nl_attr in (
311 ('vlan-id', Link.IFLA_VLAN_ID),
312 ('vlan-protocol', Link.IFLA_VLAN_PROTOCOL)
313 ):
314 ifaceobjrunning.update_config(attr_name, str(cached_vlan_info_data.get(nl_attr)))
315
316 ifaceobjrunning.update_config('vlan-raw-device', self.cache.get_lower_device_ifname(ifname))
317
318 if cached_vlan_info_data.get(Link.IFLA_VLAN_FLAGS, {}).get(Link.VLAN_FLAG_BRIDGE_BINDING, False):
319 ifaceobjrunning.update_config("vlan-bridge-binding", "on")
320
321 _run_ops = {
322 "pre-up": _up,
323 "post-down": _down,
324 "query-checkcurr": _query_check,
325 "query-running": _query_running
326 }
327
328 def get_ops(self):
329 """ returns list of ops supported by this module """
330 return list(self._run_ops.keys())
331
332 def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
333 """ run vlan configuration on the interface object passed as argument
334
335 Args:
336 **ifaceobj** (object): iface object
337
338 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
339 'query-running'
340 Kwargs:
341 **query_ifaceobj** (object): query check ifaceobject. This is only
342 valid when op is 'query-checkcurr'. It is an object same as
343 ifaceobj, but contains running attribute values and its config
344 status. The modules can use it to return queried running state
345 of interfaces. status is success if the running state is same
346 as user required state in ifaceobj. error otherwise.
347 """
348 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
349 return
350 op_handler = self._run_ops.get(operation)
351 if not op_handler:
352 return
353 if (operation != 'query-running' and
354 not self._is_vlan_device(ifaceobj)):
355 return
356 if operation == 'query-checkcurr':
357 op_handler(self, ifaceobj, query_ifaceobj)
358 else:
359 op_handler(self, ifaceobj)