]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/addons/vlan.py
Merge 'vlan filtering bridge + vxlan + mlag + vrr' support from internal
[mirror_ifupdown2.git] / ifupdown2 / addons / vlan.py
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
98 return [self._get_vlan_raw_device(ifaceobj)]
99
100 def _bridge_vid_add_del(self, ifaceobj, bridgename, vlanid,
101 add=True):
102 """ If the lower device is a vlan aware bridge, add/del the vlanid
103 to the bridge """
104 if self.ipcmd.bridge_is_vlan_aware(bridgename):
105 if add:
106 rtnetlink_api.rtnl_api.bridge_vlan(add=True, dev=bridgename,
107 vid=vlanid, master=False)
108 else:
109 rtnetlink_api.rtnl_api.bridge_vlan(add=False, dev=bridgename,
110 vid=vlanid, master=False)
111
112 def _bridge_vid_check(self, ifaceobj, ifaceobjcurr, bridgename, vlanid):
113 """ If the lower device is a vlan aware bridge, check if the vlanid
114 is configured on the bridge """
115 if not self.ipcmd.bridge_is_vlan_aware(bridgename):
116 return
117 vids = self._bridge_vids_query_cache.get(bridgename)
118 if vids == None:
119 vids = self.ipcmd.bridge_port_vids_get(bridgename)
120 self._bridge_vids_query_cache[bridgename] = vids
121 if not vids or vlanid not in vids:
122 ifaceobjcurr.status = ifaceStatus.ERROR
123 ifaceobjcurr.status_str = 'bridge vid error'
124
125 def _up(self, ifaceobj):
126 vlanid = self._get_vlan_id(ifaceobj)
127 if vlanid == -1:
128 raise Exception('could not determine vlanid')
129 if self._handle_reserved_vlan(vlanid, ifaceobj.name):
130 return
131 vlanrawdevice = self._get_vlan_raw_device(ifaceobj)
132 if not vlanrawdevice:
133 raise Exception('could not determine vlan raw device')
134 if not self.PERFMODE:
135 if not self.ipcmd.link_exists(vlanrawdevice):
136 raise Exception('rawdevice %s not present' %vlanrawdevice)
137 if self.ipcmd.link_exists(ifaceobj.name):
138 self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid)
139 return
140 rtnetlink_api.rtnl_api.create_vlan(vlanrawdevice,
141 ifaceobj.name, vlanid)
142 self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid)
143 if ifaceobj.addr_method == 'manual':
144 rtnetlink_api.rtnl_api.link_set(ifaceobj.name, "up")
145
146 def _down(self, ifaceobj):
147 vlanid = self._get_vlan_id(ifaceobj)
148 if vlanid == -1:
149 raise Exception('could not determine vlanid')
150 vlanrawdevice = self._get_vlan_raw_device(ifaceobj)
151 if not vlanrawdevice:
152 raise Exception('could not determine vlan raw device')
153 if not self.PERFMODE and not self.ipcmd.link_exists(ifaceobj.name):
154 return
155 try:
156 self.ipcmd.link_delete(ifaceobj.name)
157 self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid, add=False)
158 except Exception, e:
159 self.log_warn(str(e))
160
161 def _query_check(self, ifaceobj, ifaceobjcurr):
162 if not self.ipcmd.link_exists(ifaceobj.name):
163 return
164 if not '.' in ifaceobj.name:
165 # if vlan name is not in the dot format, check its running state
166 (vlanrawdev, vlanid) = self.ipcmd.get_vlandev_attrs(ifaceobj.name)
167 if vlanrawdev != ifaceobj.get_attr_value_first('vlan-raw-device'):
168 ifaceobjcurr.update_config_with_status('vlan-raw-device',
169 vlanrawdev, 1)
170 else:
171 ifaceobjcurr.update_config_with_status('vlan-raw-device',
172 vlanrawdev, 0)
173 if vlanid != ifaceobj.get_attr_value_first('vlan-id'):
174 ifaceobjcurr.update_config_with_status('vlan-id', vlanid, 1)
175 else:
176 ifaceobjcurr.update_config_with_status('vlan-id',
177 vlanid, 0)
178 self._bridge_vid_check(ifaceobj, ifaceobjcurr, vlanrawdev, vlanid)
179
180 def _query_running(self, ifaceobjrunning):
181 if not self.ipcmd.link_exists(ifaceobjrunning.name):
182 return
183 if not self.ipcmd.get_vlandev_attrs(ifaceobjrunning.name):
184 return
185 # If vlan name is not in the dot format, get the
186 # vlan dev and vlan id
187 if not '.' in ifaceobjrunning.name:
188 (vlanrawdev, vlanid) = self.ipcmd.get_vlandev_attrs(ifaceobjrunning.name)
189 ifaceobjrunning.update_config_dict({(k, v) for k, v in
190 {'vlan-raw-device' : vlanrawdev,
191 'vlan-id' : vlanid}.items()
192 if v})
193
194 _run_ops = {'pre-up' : _up,
195 'post-down' : _down,
196 'query-checkcurr' : _query_check,
197 'query-running' : _query_running}
198
199 def get_ops(self):
200 """ returns list of ops supported by this module """
201 return self._run_ops.keys()
202
203 def _init_command_handlers(self):
204 if not self.ipcmd:
205 self.ipcmd = iproute2(**self.get_flags())
206
207
208 def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
209 """ run vlan configuration on the interface object passed as argument
210
211 Args:
212 **ifaceobj** (object): iface object
213
214 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
215 'query-running'
216 Kwargs:
217 **query_ifaceobj** (object): query check ifaceobject. This is only
218 valid when op is 'query-checkcurr'. It is an object same as
219 ifaceobj, but contains running attribute values and its config
220 status. The modules can use it to return queried running state
221 of interfaces. status is success if the running state is same
222 as user required state in ifaceobj. error otherwise.
223 """
224 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
225 return
226 op_handler = self._run_ops.get(operation)
227 if not op_handler:
228 return
229 if (operation != 'query-running' and
230 not self._is_vlan_device(ifaceobj)):
231 return
232 self._init_command_handlers()
233 if operation == 'query-checkcurr':
234 op_handler(self, ifaceobj, query_ifaceobj)
235 else:
236 op_handler(self, ifaceobj)