]>
Commit | Line | Data |
---|---|---|
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 |
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 | ||
9e0be374 | 24 | |
15ef32ea RP |
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' : | |
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) |