]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown2/addons/vlan.py
addons: vlan: error out when vlan-raw-device config change on existing vlan
[mirror_ifupdown2.git] / ifupdown2 / addons / vlan.py
CommitLineData
15ef32ea
RP
1#!/usr/bin/python
2#
d486dd0d 3# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
15ef32ea
RP
4# Author: Roopa Prabhu, roopa@cumulusnetworks.com
5#
6
d486dd0d
JF
7try:
8 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
9
10 from ifupdown2.ifupdown.iface import *
11 from ifupdown2.ifupdown.netlink import netlink
12
13 from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
14 from ifupdown2.ifupdownaddons.modulebase import moduleBase
15except ImportError:
16 import ifupdown.ifupdownflags as ifupdownflags
17
18 from ifupdown.iface import *
19 from ifupdown.netlink import netlink
20
21 from ifupdownaddons.LinkUtils import LinkUtils
22 from ifupdownaddons.modulebase import moduleBase
23
9e0be374 24
15ef32ea
RP
25
26class vlan(moduleBase):
27 """ ifupdown2 addon module to configure vlans """
28
29 _modinfo = {'mhelp' : 'vlan module configures vlan interfaces.' +
30 'This module understands vlan interfaces with dot ' +
31 'notations. eg swp1.100. Vlan interfaces with any ' +
32 'other names need to have raw device and vlan id ' +
33 'attributes',
34 'attrs' : {
35 'vlan-raw-device' :
c6370b56 36 {'help' : 'vlan raw device',
482b2fab 37 'validvals': ['<interface>']},
15ef32ea 38 'vlan-id' :
c6370b56 39 {'help' : 'vlan id',
d486dd0d
JF
40 'validrange' : ['0', '4096']},
41 'vlan-protocol' :
42 {'help' : 'vlan protocol',
43 'default' : '802.1q',
44 'validvals': ['802.1q', '802.1ad'],
45 'example' : ['vlan-protocol 802.1q']},
46 }}
15ef32ea 47
8e113d63 48
15ef32ea
RP
49 def __init__(self, *args, **kargs):
50 moduleBase.__init__(self, *args, **kargs)
51 self.ipcmd = None
52
53 def _is_vlan_device(self, ifaceobj):
54 vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device')
55 if vlan_raw_device:
56 return True
57 elif '.' in ifaceobj.name:
58 return True
59 return False
60
15ef32ea
RP
61 def _is_vlan_by_name(self, ifacename):
62 return '.' in ifacename
63
64 def _get_vlan_raw_device_from_ifacename(self, ifacename):
65 """ Returns vlan raw device from ifname
66 Example:
67 Returns eth0 for ifname eth0.100
d486dd0d 68 Returns eth0.100 for ifname eth0.100.200
15ef32ea
RP
69 Returns None if vlan raw device name cannot
70 be determined
71 """
d486dd0d 72 vlist = ifacename.split('.', 2)
15ef32ea
RP
73 if len(vlist) == 2:
74 return vlist[0]
d486dd0d
JF
75 elif len(vlist) == 3:
76 return vlist[0] + "." + vlist[1]
15ef32ea
RP
77 return None
78
79 def _get_vlan_raw_device(self, ifaceobj):
80 vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device')
81 if vlan_raw_device:
82 return vlan_raw_device
83 return self._get_vlan_raw_device_from_ifacename(ifaceobj.name)
9e0be374 84
15ef32ea
RP
85 def get_dependent_ifacenames(self, ifaceobj, ifaceobjs_all=None):
86 if not self._is_vlan_device(ifaceobj):
87 return None
0a3bee28 88 ifaceobj.link_kind |= ifaceLinkKind.VLAN
15ef32ea
RP
89 return [self._get_vlan_raw_device(ifaceobj)]
90
e1601369 91 def _bridge_vid_add_del(self, ifaceobj, bridgename, vlanid,
8e113d63
RP
92 add=True):
93 """ If the lower device is a vlan aware bridge, add/del the vlanid
94 to the bridge """
e1601369 95 if self.ipcmd.bridge_is_vlan_aware(bridgename):
8e113d63 96 if add:
2864d6f3 97 netlink.link_add_bridge_vlan(bridgename, vlanid)
8e113d63 98 else:
2864d6f3 99 netlink.link_del_bridge_vlan(bridgename, vlanid)
8e113d63
RP
100
101 def _bridge_vid_check(self, ifaceobj, ifaceobjcurr, bridgename, vlanid):
102 """ If the lower device is a vlan aware bridge, check if the vlanid
103 is configured on the bridge """
104 if not self.ipcmd.bridge_is_vlan_aware(bridgename):
105 return
d486dd0d 106 vids = self.ipcmd.bridge_vlan_get_vids(bridgename)
8e113d63
RP
107 if not vids or vlanid not in vids:
108 ifaceobjcurr.status = ifaceStatus.ERROR
109 ifaceobjcurr.status_str = 'bridge vid error'
110
111 def _up(self, ifaceobj):
15ef32ea
RP
112 vlanid = self._get_vlan_id(ifaceobj)
113 if vlanid == -1:
114 raise Exception('could not determine vlanid')
115 vlanrawdevice = self._get_vlan_raw_device(ifaceobj)
116 if not vlanrawdevice:
117 raise Exception('could not determine vlan raw device')
d486dd0d
JF
118
119 vlan_protocol = ifaceobj.get_attr_value_first('vlan-protocol')
120 cached_vlan_protocol = self.ipcmd.get_vlan_protocol(ifaceobj.name)
121
122 if not vlan_protocol:
123 vlan_protocol = self.get_attr_default_value('vlan-protocol')
124
125 if cached_vlan_protocol and vlan_protocol.lower() != cached_vlan_protocol.lower():
126 raise Exception('%s: cannot change vlan-protocol to %s: operation not supported. '
127 'Please delete the device with \'ifdown %s\' and recreate it to '
128 'apply the change.'
129 % (ifaceobj.name, vlan_protocol, ifaceobj.name))
130
fc5e1735 131 if not ifupdownflags.flags.PERFMODE:
7114342b
JF
132
133 vlan_exists = self.ipcmd.link_exists(ifaceobj.name)
134
135 if vlan_exists:
136 user_vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device')
137 cached_vlan_raw_device = self.ipcmd.cache_get('link', [ifaceobj.name, 'link'])
138
139 if cached_vlan_raw_device != user_vlan_raw_device:
140 raise Exception('%s: cannot change vlan-raw-device from %s to %s: operation not supported. '
141 'Please delete the device with \'ifdown %s\' and recreate it to apply the change.'
142 % (ifaceobj.name, cached_vlan_raw_device, user_vlan_raw_device, ifaceobj.name))
143
84ca006f
RP
144 if not self.ipcmd.link_exists(vlanrawdevice):
145 raise Exception('rawdevice %s not present' %vlanrawdevice)
7114342b 146 if vlan_exists:
8e113d63 147 self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid)
84ca006f 148 return
d486dd0d
JF
149
150 netlink.link_add_vlan(vlanrawdevice, ifaceobj.name, vlanid, vlan_protocol)
8e113d63 151 self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid)
15ef32ea 152
8e113d63 153 def _down(self, ifaceobj):
15ef32ea
RP
154 vlanid = self._get_vlan_id(ifaceobj)
155 if vlanid == -1:
156 raise Exception('could not determine vlanid')
e1601369
RP
157 vlanrawdevice = self._get_vlan_raw_device(ifaceobj)
158 if not vlanrawdevice:
15ef32ea 159 raise Exception('could not determine vlan raw device')
fc5e1735
RP
160 if (not ifupdownflags.flags.PERFMODE and
161 not self.ipcmd.link_exists(ifaceobj.name)):
15ef32ea
RP
162 return
163 try:
164 self.ipcmd.link_delete(ifaceobj.name)
8e113d63 165 self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid, add=False)
15ef32ea
RP
166 except Exception, e:
167 self.log_warn(str(e))
168
8e113d63 169 def _query_check(self, ifaceobj, ifaceobjcurr):
15ef32ea 170 if not self.ipcmd.link_exists(ifaceobj.name):
15ef32ea
RP
171 return
172 if not '.' in ifaceobj.name:
173 # if vlan name is not in the dot format, check its running state
d486dd0d 174 (vlanrawdev, vlanid, protocol) = self.ipcmd.get_vlandev_attrs(ifaceobj.name)
15ef32ea
RP
175 if vlanrawdev != ifaceobj.get_attr_value_first('vlan-raw-device'):
176 ifaceobjcurr.update_config_with_status('vlan-raw-device',
177 vlanrawdev, 1)
178 else:
179 ifaceobjcurr.update_config_with_status('vlan-raw-device',
180 vlanrawdev, 0)
117d7654
JF
181 vlanid_config = ifaceobj.get_attr_value_first('vlan-id')
182 if not vlanid_config:
183 vlanid_config = str(self._get_vlan_id(ifaceobj))
184 if vlanid != vlanid_config:
15ef32ea
RP
185 ifaceobjcurr.update_config_with_status('vlan-id', vlanid, 1)
186 else:
117d7654 187 ifaceobjcurr.update_config_with_status('vlan-id', vlanid, 0)
d486dd0d
JF
188 protocol_config = ifaceobj.get_attr_value_first('vlan-protocol')
189 if protocol_config:
190 if protocol_config.upper() != protocol.upper():
191 ifaceobjcurr.update_config_with_status('vlan-protocol',
192 protocol, 1)
193 else:
194 ifaceobjcurr.update_config_with_status('vlan-protocol',
195 protocol, 0)
196 self._bridge_vid_check(ifaceobj, ifaceobjcurr, vlanrawdev, int(vlanid))
15ef32ea 197
8e113d63 198 def _query_running(self, ifaceobjrunning):
15ef32ea 199 if not self.ipcmd.link_exists(ifaceobjrunning.name):
15ef32ea 200 return
d486dd0d 201 (vlanrawdev, vlanid, protocol) = self.ipcmd.get_vlandev_attrs(ifaceobjrunning.name)
f78ba1de 202 if not vlanid:
15ef32ea
RP
203 return
204 # If vlan name is not in the dot format, get the
205 # vlan dev and vlan id
206 if not '.' in ifaceobjrunning.name:
d486dd0d 207 ifaceobjrunning.update_config_dict({k: [v] for k, v in
15ef32ea 208 {'vlan-raw-device' : vlanrawdev,
d486dd0d
JF
209 'vlan-id' : vlanid,
210 'vlan-protocol' : protocol}.items()
15ef32ea
RP
211 if v})
212
213 _run_ops = {'pre-up' : _up,
214 'post-down' : _down,
215 'query-checkcurr' : _query_check,
216 'query-running' : _query_running}
217
218 def get_ops(self):
219 """ returns list of ops supported by this module """
220 return self._run_ops.keys()
221
222 def _init_command_handlers(self):
223 if not self.ipcmd:
d486dd0d 224 self.ipcmd = LinkUtils()
9e0be374 225
8e113d63 226 def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
15ef32ea
RP
227 """ run vlan configuration on the interface object passed as argument
228
229 Args:
230 **ifaceobj** (object): iface object
231
232 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
233 'query-running'
234 Kwargs:
235 **query_ifaceobj** (object): query check ifaceobject. This is only
236 valid when op is 'query-checkcurr'. It is an object same as
237 ifaceobj, but contains running attribute values and its config
238 status. The modules can use it to return queried running state
239 of interfaces. status is success if the running state is same
240 as user required state in ifaceobj. error otherwise.
241 """
84ca006f
RP
242 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
243 return
15ef32ea
RP
244 op_handler = self._run_ops.get(operation)
245 if not op_handler:
246 return
247 if (operation != 'query-running' and
248 not self._is_vlan_device(ifaceobj)):
249 return
250 self._init_command_handlers()
251 if operation == 'query-checkcurr':
8e113d63 252 op_handler(self, ifaceobj, query_ifaceobj)
15ef32ea 253 else:
8e113d63 254 op_handler(self, ifaceobj)