]>
git.proxmox.com Git - mirror_ifupdown2.git/blob - addons/addressvirtual.py
3 # Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
7 from ifupdown
.iface
import *
8 from ifupdownaddons
.modulebase
import moduleBase
9 from ifupdownaddons
.iproute2
import iproute2
10 import ifupdown
.statemanager
as statemanager
11 import ifupdown
.rtnetlink_api
as rtnetlink_api
12 from ipaddr
import IPNetwork
17 class addressvirtual(moduleBase
):
18 """ ifupdown2 addon module to configure virtual addresses """
20 _modinfo
= {'mhelp' : 'address module configures virtual addresses for ' +
21 'interfaces. It creates a macvlan interface for ' +
22 'every mac ip address-virtual line',
25 { 'help' : 'bridge router virtual mac and ip',
26 'example' : ['address-virtual 00:11:22:33:44:01 11.0.1.254/24 11.0.1.254/24']}
30 def __init__(self
, *args
, **kargs
):
31 moduleBase
.__init
__(self
, *args
, **kargs
)
33 self
._bridge
_fdb
_query
_cache
= {}
35 def _is_supported(self
, ifaceobj
):
36 if ifaceobj
.get_attr_value_first('address-virtual'):
40 def _get_macvlan_prefix(self
, ifaceobj
):
41 return '%s-v' %ifaceobj
.name
[0:13].replace('.', '-')
43 def _add_addresses_to_bridge(self
, ifaceobj
, hwaddress
):
44 # XXX: batch the addresses
45 if '.' in ifaceobj
.name
:
46 (bridgename
, vlan
) = ifaceobj
.name
.split('.')
47 if self
.ipcmd
.bridge_is_vlan_aware(bridgename
):
48 [self
.ipcmd
.bridge_fdb_add(bridgename
, addr
,
49 vlan
) for addr
in hwaddress
]
50 elif self
.ipcmd
.is_bridge(ifaceobj
.name
):
51 [self
.ipcmd
.bridge_fdb_add(ifaceobj
.name
, addr
)
52 for addr
in hwaddress
]
54 def _remove_addresses_from_bridge(self
, ifaceobj
, hwaddress
):
55 # XXX: batch the addresses
56 if '.' in ifaceobj
.name
:
57 (bridgename
, vlan
) = ifaceobj
.name
.split('.')
58 if self
.ipcmd
.bridge_is_vlan_aware(bridgename
):
59 for addr
in hwaddress
:
61 self
.ipcmd
.bridge_fdb_del(bridgename
, addr
, vlan
)
63 self
.logger
.debug("%s: %s" %(ifaceobj
.name
, str(e
)))
65 elif self
.ipcmd
.is_bridge(ifaceobj
.name
):
66 for addr
in hwaddress
:
68 self
.ipcmd
.bridge_fdb_del(ifaceobj
.name
, addr
)
70 self
.logger
.debug("%s: %s" %(ifaceobj
.name
, str(e
)))
73 def _get_bridge_fdbs(self
, bridgename
, vlan
):
74 fdbs
= self
._bridge
_fdb
_query
_cache
.get(bridgename
)
76 fdbs
= self
.ipcmd
.bridge_fdb_show_dev(bridgename
)
79 self
._bridge
_fdb
_query
_cache
[bridgename
] = fdbs
82 def _check_addresses_in_bridge(self
, ifaceobj
, hwaddress
):
83 """ If the device is a bridge, make sure the addresses
85 if '.' in ifaceobj
.name
:
86 (bridgename
, vlan
) = ifaceobj
.name
.split('.')
87 if self
.ipcmd
.bridge_is_vlan_aware(bridgename
):
88 fdb_addrs
= self
._get
_bridge
_fdbs
(bridgename
, vlan
)
89 if not fdb_addrs
or hwaddress
not in fdb_addrs
:
93 def _fix_connected_route(self
, ifaceobj
, vifacename
, addr
):
95 # XXX: Hack to make sure the primary address
96 # is the first in the routing table.
98 # We use `ip route get` on the vrr network to see which
99 # device the kernel returns. if it is the mac vlan device,
100 # flap the macvlan device to adjust the routing table entry.
102 # flapping the macvlan device makes sure the macvlan
103 # connected route goes through delete + add, hence adjusting
104 # the order in the routing table.
107 self
.logger
.info('%s: checking route entry ...' %ifaceobj
.name
)
109 route_prefix
= '%s/%d' %(ip
.network
, ip
.prefixlen
)
111 dev
= self
.ipcmd
.ip_route_get_dev(route_prefix
)
112 if dev
and dev
== vifacename
:
113 self
.logger
.info('%s: preferred routing entry ' %ifaceobj
.name
+
114 'seems to be of the macvlan dev %s'
116 ' .. flapping macvlan dev to fix entry.')
117 self
.ipcmd
.link_down(vifacename
)
118 self
.ipcmd
.link_up(vifacename
)
120 self
.logger
.debug('%s: fixing route entry failed (%s)'
124 def _get_macs_from_old_config(self
, ifaceobj
=None):
125 """ This method returns a list of the mac addresses
126 in the address-virtual attribute for the bridge. """
128 saved_ifaceobjs
= statemanager
.statemanager_api
.get_ifaceobjs(ifaceobj
.name
)
129 if not saved_ifaceobjs
:
131 # we need the old saved configs from the statemanager
132 for oldifaceobj
in saved_ifaceobjs
:
133 if not oldifaceobj
.get_attr_value('address-virtual'):
135 for av
in oldifaceobj
.get_attr_value('address-virtual'):
138 self
.logger
.debug("%s: incorrect old address-virtual attrs '%s'"
139 %(oldifaceobj
.name
, av
))
141 maclist
.append(macip
[0])
144 def _apply_address_config(self
, ifaceobj
, address_virtual_list
):
145 purge_existing
= False if self
.PERFMODE
else True
148 self
.ipcmd
.batch_start()
150 macvlan_prefix
= self
._get
_macvlan
_prefix
(ifaceobj
)
151 for av
in address_virtual_list
:
152 av_attrs
= av
.split()
153 if len(av_attrs
) < 2:
154 self
.logger
.warn("%s: incorrect address-virtual attrs '%s'"
155 %(ifaceobj
.name
, av
))
159 # Create a macvlan device on this device and set the virtual
160 # router mac and ip on it
162 macvlan_ifacename
= '%s%d' %(macvlan_prefix
, av_idx
)
163 if not self
.ipcmd
.link_exists(macvlan_ifacename
):
164 rtnetlink_api
.rtnl_api
.create_macvlan(macvlan_ifacename
,
171 # customer could have used UPPERCASE for MAC
172 self
.ipcmd
.link_set_hwaddress(macvlan_ifacename
, mac
)
173 hwaddress
.append(mac
)
174 self
.ipcmd
.addr_add_multiple(macvlan_ifacename
, ips
,
176 # If link existed before, flap the link
178 self
._fix
_connected
_route
(ifaceobj
, macvlan_ifacename
,
181 self
.ipcmd
.batch_commit()
183 # check the statemanager for old configs.
184 # We need to remove only the previously configured FDB entries
185 oldmacs
= self
._get
_macs
_from
_old
_config
(ifaceobj
)
186 # get a list of fdbs in old that are not in new config meaning they should
187 # be removed since they are gone from the config
188 removed_macs
= [mac
for mac
in oldmacs
if mac
.lower() not in hwaddress
]
189 self
._remove
_addresses
_from
_bridge
(ifaceobj
, removed_macs
)
190 # if ifaceobj is a bridge and bridge is a vlan aware bridge
191 # add the vid to the bridge
192 self
._add
_addresses
_to
_bridge
(ifaceobj
, hwaddress
)
194 def _remove_running_address_config(self
, ifaceobj
):
195 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
198 self
.ipcmd
.batch_start()
199 macvlan_prefix
= self
._get
_macvlan
_prefix
(ifaceobj
)
200 for macvlan_ifacename
in glob
.glob("/sys/class/net/%s*" %macvlan_prefix
):
201 macvlan_ifacename
= os
.path
.basename(macvlan_ifacename
)
202 if not self
.ipcmd
.link_exists(macvlan_ifacename
):
204 hwaddress
.append(self
.ipcmd
.link_get_hwaddress(macvlan_ifacename
))
205 self
.ipcmd
.link_delete(os
.path
.basename(macvlan_ifacename
))
206 # XXX: Also delete any fdb addresses. This requires, checking mac address
207 # on individual macvlan interfaces and deleting the vlan from that.
208 self
.ipcmd
.batch_commit()
210 self
._remove
_addresses
_from
_bridge
(ifaceobj
, hwaddress
)
212 def _remove_address_config(self
, ifaceobj
, address_virtual_list
=None):
213 if not address_virtual_list
:
214 self
._remove
_running
_address
_config
(ifaceobj
)
217 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
220 self
.ipcmd
.batch_start()
222 macvlan_prefix
= self
._get
_macvlan
_prefix
(ifaceobj
)
223 for av
in address_virtual_list
:
224 av_attrs
= av
.split()
225 if len(av_attrs
) < 2:
226 self
.logger
.warn("%s: incorrect address-virtual attrs '%s'"
227 %(ifaceobj
.name
, av
))
231 # Delete the macvlan device on this device
232 macvlan_ifacename
= '%s%d' %(macvlan_prefix
, av_idx
)
233 self
.ipcmd
.link_delete(os
.path
.basename(macvlan_ifacename
))
234 if av_attrs
[0] != 'None':
235 hwaddress
.append(av_attrs
[0])
237 self
.ipcmd
.batch_commit()
238 self
._remove
_addresses
_from
_bridge
(ifaceobj
, hwaddress
)
240 def _up(self
, ifaceobj
):
241 address_virtual_list
= ifaceobj
.get_attr_value('address-virtual')
242 if not address_virtual_list
:
243 # XXX: address virtual is not present. In which case,
244 # delete stale macvlan devices.
245 self
._remove
_address
_config
(ifaceobj
, address_virtual_list
)
248 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
250 self
._apply
_address
_config
(ifaceobj
, address_virtual_list
)
252 def _down(self
, ifaceobj
):
254 self
._remove
_address
_config
(ifaceobj
,
255 ifaceobj
.get_attr_value('address-virtual'))
257 self
.log_warn(str(e
))
259 def _query_check(self
, ifaceobj
, ifaceobjcurr
):
260 address_virtual_list
= ifaceobj
.get_attr_value('address-virtual')
261 if not address_virtual_list
:
263 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
266 macvlan_prefix
= self
._get
_macvlan
_prefix
(ifaceobj
)
267 for address_virtual
in address_virtual_list
:
268 av_attrs
= address_virtual
.split()
269 if len(av_attrs
) < 2:
270 self
.logger
.warn("%s: incorrect address-virtual attrs '%s'"
271 %(ifaceobj
.name
, address_virtual
))
275 # Check if the macvlan device on this interface
276 macvlan_ifacename
= '%s%d' %(macvlan_prefix
, av_idx
)
277 if not self
.ipcmd
.link_exists(macvlan_ifacename
):
278 ifaceobjcurr
.update_config_with_status('address-virtual',
282 # Check mac and ip address
283 rhwaddress
= self
.ipcmd
.link_get_hwaddress(macvlan_ifacename
)
284 raddrs
= self
.ipcmd
.addr_get(macvlan_ifacename
)
285 if not raddrs
or not rhwaddress
:
286 ifaceobjcurr
.update_config_with_status('address-virtual', '', 1)
289 raddrs
= raddrs
.keys()
290 if (rhwaddress
== av_attrs
[0] and raddrs
== av_attrs
[1:] and
291 self
._check
_addresses
_in
_bridge
(ifaceobj
, av_attrs
[0])):
292 ifaceobjcurr
.update_config_with_status('address-virtual',
295 raddress_virtual
= '%s %s' %(rhwaddress
, ' '.join(raddrs
))
296 ifaceobjcurr
.update_config_with_status('address-virtual',
301 def _query_running(self
, ifaceobjrunning
):
302 macvlan_prefix
= self
._get
_macvlan
_prefix
(ifaceobjrunning
)
303 address_virtuals
= glob
.glob("/sys/class/net/%s*" %macvlan_prefix
)
304 for av
in address_virtuals
:
305 macvlan_ifacename
= os
.path
.basename(av
)
306 rhwaddress
= self
.ipcmd
.link_get_hwaddress(macvlan_ifacename
)
307 raddress
= self
.ipcmd
.addr_get(macvlan_ifacename
)
309 self
.logger
.warn('%s: no running addresses'
310 %ifaceobjrunning
.name
)
312 ifaceobjrunning
.update_config('address-virtual',
313 '%s %s' %(rhwaddress
, ''.join(raddress
)))
316 _run_ops
= {'up' : _up
,
318 'query-checkcurr' : _query_check
,
319 'query-running' : _query_running
}
322 """ returns list of ops supported by this module """
323 return self
._run
_ops
.keys()
325 def _init_command_handlers(self
):
327 self
.ipcmd
= iproute2(**self
.get_flags())
329 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None, **extra_args
):
330 """ run vlan configuration on the interface object passed as argument
333 **ifaceobj** (object): iface object
335 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
338 **query_ifaceobj** (object): query check ifaceobject. This is only
339 valid when op is 'query-checkcurr'. It is an object same as
340 ifaceobj, but contains running attribute values and its config
341 status. The modules can use it to return queried running state
342 of interfaces. status is success if the running state is same
343 as user required state in ifaceobj. error otherwise.
345 if ifaceobj
.type == ifaceType
.BRIDGE_VLAN
:
347 op_handler
= self
._run
_ops
.get(operation
)
350 self
._init
_command
_handlers
()
351 if operation
== 'query-checkcurr':
352 op_handler(self
, ifaceobj
, query_ifaceobj
)
354 op_handler(self
, ifaceobj
)