]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/addons/vlan.py
addons: vlan: error out when vlan-raw-device config change on existing vlan
[mirror_ifupdown2.git] / ifupdown2 / addons / vlan.py
1 #!/usr/bin/python
2 #
3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
5 #
6
7 try:
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
15 except 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
24
25
26 class 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' :
36 {'help' : 'vlan raw device',
37 'validvals': ['<interface>']},
38 'vlan-id' :
39 {'help' : 'vlan id',
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 }}
47
48
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
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
68 Returns eth0.100 for ifname eth0.100.200
69 Returns None if vlan raw device name cannot
70 be determined
71 """
72 vlist = ifacename.split('.', 2)
73 if len(vlist) == 2:
74 return vlist[0]
75 elif len(vlist) == 3:
76 return vlist[0] + "." + vlist[1]
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)
84
85 def get_dependent_ifacenames(self, ifaceobj, ifaceobjs_all=None):
86 if not self._is_vlan_device(ifaceobj):
87 return None
88 ifaceobj.link_kind |= ifaceLinkKind.VLAN
89 return [self._get_vlan_raw_device(ifaceobj)]
90
91 def _bridge_vid_add_del(self, ifaceobj, bridgename, vlanid,
92 add=True):
93 """ If the lower device is a vlan aware bridge, add/del the vlanid
94 to the bridge """
95 if self.ipcmd.bridge_is_vlan_aware(bridgename):
96 if add:
97 netlink.link_add_bridge_vlan(bridgename, vlanid)
98 else:
99 netlink.link_del_bridge_vlan(bridgename, vlanid)
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
106 vids = self.ipcmd.bridge_vlan_get_vids(bridgename)
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):
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')
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
131 if not ifupdownflags.flags.PERFMODE:
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
144 if not self.ipcmd.link_exists(vlanrawdevice):
145 raise Exception('rawdevice %s not present' %vlanrawdevice)
146 if vlan_exists:
147 self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid)
148 return
149
150 netlink.link_add_vlan(vlanrawdevice, ifaceobj.name, vlanid, vlan_protocol)
151 self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid)
152
153 def _down(self, ifaceobj):
154 vlanid = self._get_vlan_id(ifaceobj)
155 if vlanid == -1:
156 raise Exception('could not determine vlanid')
157 vlanrawdevice = self._get_vlan_raw_device(ifaceobj)
158 if not vlanrawdevice:
159 raise Exception('could not determine vlan raw device')
160 if (not ifupdownflags.flags.PERFMODE and
161 not self.ipcmd.link_exists(ifaceobj.name)):
162 return
163 try:
164 self.ipcmd.link_delete(ifaceobj.name)
165 self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid, add=False)
166 except Exception, e:
167 self.log_warn(str(e))
168
169 def _query_check(self, ifaceobj, ifaceobjcurr):
170 if not self.ipcmd.link_exists(ifaceobj.name):
171 return
172 if not '.' in ifaceobj.name:
173 # if vlan name is not in the dot format, check its running state
174 (vlanrawdev, vlanid, protocol) = self.ipcmd.get_vlandev_attrs(ifaceobj.name)
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)
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:
185 ifaceobjcurr.update_config_with_status('vlan-id', vlanid, 1)
186 else:
187 ifaceobjcurr.update_config_with_status('vlan-id', vlanid, 0)
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))
197
198 def _query_running(self, ifaceobjrunning):
199 if not self.ipcmd.link_exists(ifaceobjrunning.name):
200 return
201 (vlanrawdev, vlanid, protocol) = self.ipcmd.get_vlandev_attrs(ifaceobjrunning.name)
202 if not vlanid:
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:
207 ifaceobjrunning.update_config_dict({k: [v] for k, v in
208 {'vlan-raw-device' : vlanrawdev,
209 'vlan-id' : vlanid,
210 'vlan-protocol' : protocol}.items()
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:
224 self.ipcmd = LinkUtils()
225
226 def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
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 """
242 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
243 return
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':
252 op_handler(self, ifaceobj, query_ifaceobj)
253 else:
254 op_handler(self, ifaceobj)