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