3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
8 from ifupdown2
.lib
.addon
import Addon
9 from ifupdown2
.ifupdown
.iface
import *
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 *
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
27 class vlan(Addon
, moduleBase
):
28 """ ifupdown2 addon module to configure vlans """
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",
37 "help": "vlan raw device",
38 "validvals": ["<interface>"]
42 "validrange": ["0", "4096"]
45 "help": "vlan protocol",
47 "validvals": ["802.1q", "802.1ad"],
48 "example": ["vlan-protocol 802.1q"]
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.",
57 "validvals": ["on", "off"],
58 "example": ["vlan-bridge-binding on"]
63 def __init__(self
, *args
, **kargs
):
65 moduleBase
.__init
__(self
, *args
, **kargs
)
67 def _is_vlan_device(self
, ifaceobj
):
68 vlan_raw_device
= ifaceobj
.get_attr_value_first('vlan-raw-device')
71 elif '.' in ifaceobj
.name
:
73 if self
._get
_vlan
_id
(ifaceobj
) != -1:
80 def _is_vlan_by_name(ifacename
):
81 return '.' in ifacename
84 def _get_vlan_raw_device_from_ifacename(ifacename
):
85 """ Returns vlan raw device from ifname
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
92 vlist
= ifacename
.split('.', 2)
96 return vlist
[0] + "." + vlist
[1]
99 def _get_vlan_raw_device(self
, ifaceobj
):
100 vlan_raw_device
= ifaceobj
.get_attr_value_first('vlan-raw-device')
102 return vlan_raw_device
103 return self
._get
_vlan
_raw
_device
_from
_ifacename
(ifaceobj
.name
)
105 def get_dependent_ifacenames(self
, ifaceobj
, ifaceobjs_all
=None, old_ifaceobjs
=False):
106 if not self
._is
_vlan
_device
(ifaceobj
):
108 ifaceobj
.link_kind |
= ifaceLinkKind
.VLAN
109 return [self
._get
_vlan
_raw
_device
(ifaceobj
)]
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
114 if self
.cache
.bridge_is_vlan_aware(bridgename
):
116 self
.netlink
.link_add_bridge_vlan(bridgename
, vlanid
)
118 self
.netlink
.link_del_bridge_vlan(bridgename
, vlanid
)
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
):
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'
130 def _up(self
, ifaceobj
):
131 vlanid
= self
._get
_vlan
_id
(ifaceobj
)
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')
138 ifname
= ifaceobj
.name
140 if ifupdownflags
.flags
.PERFMODE
:
141 cached_vlan_ifla_info_data
= {}
143 cached_vlan_ifla_info_data
= self
.cache
.get_link_info_data(ifname
)
145 vlan_bridge_binding
= ifaceobj
.get_attr_value_first("vlan-bridge-binding")
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")
153 bool_vlan_bridge_binding
= utils
.get_boolean_from_string(vlan_bridge_binding
)
155 vlan_protocol
= ifaceobj
.get_attr_value_first('vlan-protocol')
156 cached_vlan_protocol
= cached_vlan_ifla_info_data
.get(Link
.IFLA_VLAN_PROTOCOL
)
158 if not vlan_protocol
:
159 vlan_protocol
= self
.get_attr_default_value('vlan-protocol')
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 '
165 % (ifaceobj
.name
, vlan_protocol
, ifaceobj
.name
))
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 '
172 % (ifaceobj
.name
, vlanid
, ifaceobj
.name
))
174 if not ifupdownflags
.flags
.PERFMODE
:
176 vlan_exists
= self
.cache
.link_exists(ifaceobj
.name
)
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
)
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
))
187 if not self
.cache
.link_exists(vlanrawdevice
):
188 if ifupdownflags
.flags
.DRYRUN
:
191 raise Exception('rawdevice %s not present' % vlanrawdevice
)
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
)
199 self
._bridge
_vid
_add
_del
(vlanrawdevice
, vlanid
)
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
)
209 def _down(self
, ifaceobj
):
210 vlanid
= self
._get
_vlan
_id
(ifaceobj
)
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
):
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
))
224 def _query_check(self
, ifaceobj
, ifaceobjcurr
):
225 if not self
.cache
.link_exists(ifaceobj
.name
):
228 ifname
= ifaceobj
.name
229 cached_vlan_info_data
= self
.cache
.get_link_info_data(ifname
)
231 if '.' not in ifaceobj
.name
:
232 # if vlan name is not in the dot format, check its running state
234 cached_vlan_raw_device
= self
.cache
.get_lower_device_ifname(ifname
)
239 ifaceobjcurr
.update_config_with_status(
241 cached_vlan_raw_device
,
242 cached_vlan_raw_device
!= ifaceobj
.get_attr_value_first('vlan-raw-device')
248 vlanid_config
= ifaceobj
.get_attr_value_first('vlan-id')
249 if not vlanid_config
:
250 vlanid_config
= str(self
._get
_vlan
_id
(ifaceobj
))
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
)
257 # vlan-protocol (dot or not dot format)
259 protocol_config
= ifaceobj
.get_attr_value_first('vlan-protocol')
262 cached_vlan_protocol
= cached_vlan_info_data
.get(Link
.IFLA_VLAN_PROTOCOL
)
264 if protocol_config
.upper() != cached_vlan_protocol
.upper():
265 ifaceobjcurr
.update_config_with_status(
267 cached_vlan_protocol
,
271 ifaceobjcurr
.update_config_with_status(
277 if '.' not in ifaceobj
.name
:
279 # vlan-bridge-binding
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)
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
)
292 self
._bridge
_vid
_check
(ifaceobjcurr
, cached_vlan_raw_device
, cached_vlan_id
)
294 def _query_running(self
, ifaceobjrunning
):
295 ifname
= ifaceobjrunning
.name
297 if not self
.cache
.link_exists(ifname
):
300 if not self
.cache
.get_link_kind(ifname
) == 'vlan':
303 # If vlan name is not in the dot format, get the
304 # vlan dev and vlan id
308 cached_vlan_info_data
= self
.cache
.get_link_info_data(ifname
)
310 for attr_name
, nl_attr
in (
311 ('vlan-id', Link
.IFLA_VLAN_ID
),
312 ('vlan-protocol', Link
.IFLA_VLAN_PROTOCOL
)
314 ifaceobjrunning
.update_config(attr_name
, str(cached_vlan_info_data
.get(nl_attr
)))
316 ifaceobjrunning
.update_config('vlan-raw-device', self
.cache
.get_lower_device_ifname(ifname
))
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")
324 "query-checkcurr": _query_check
,
325 "query-running": _query_running
329 """ returns list of ops supported by this module """
330 return list(self
._run
_ops
.keys())
332 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None, **extra_args
):
333 """ run vlan configuration on the interface object passed as argument
336 **ifaceobj** (object): iface object
338 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
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.
348 if ifaceobj
.type == ifaceType
.BRIDGE_VLAN
:
350 op_handler
= self
._run
_ops
.get(operation
)
353 if (operation
!= 'query-running' and
354 not self
._is
_vlan
_device
(ifaceobj
)):
356 if operation
== 'query-checkcurr':
357 op_handler(self
, ifaceobj
, query_ifaceobj
)
359 op_handler(self
, ifaceobj
)