]>
git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/addons/vxlan.py
c21af7b1434999689b3c9df761aa93857a358e37
3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
9 from ipaddr
import IPNetwork
, IPv4Address
, IPv4Network
, AddressValueError
12 import ifupdown2
.ifupdown
.policymanager
as policymanager
13 import ifupdown2
.ifupdown
.ifupdownflags
as ifupdownflags
15 from ifupdown2
.nlmanager
.nlmanager
import Link
17 from ifupdown2
.ifupdown
.iface
import *
18 from ifupdown2
.ifupdown
.utils
import utils
19 from ifupdown2
.ifupdown
.netlink
import netlink
20 from ifupdown2
.ifupdownaddons
.cache
import *
21 from ifupdown2
.ifupdownaddons
.LinkUtils
import LinkUtils
22 from ifupdown2
.ifupdownaddons
.modulebase
import moduleBase
24 import ifupdown
.policymanager
as policymanager
25 import ifupdown
.ifupdownflags
as ifupdownflags
27 from nlmanager
.nlmanager
import Link
29 from ifupdown
.iface
import *
30 from ifupdown
.utils
import utils
31 from ifupdown
.netlink
import netlink
33 from ifupdownaddons
.cache
import *
34 from ifupdownaddons
.LinkUtils
import LinkUtils
35 from ifupdownaddons
.modulebase
import moduleBase
38 class vxlan(moduleBase
):
39 _modinfo
= {'mhelp' : 'vxlan module configures vxlan interfaces.',
43 'validrange' : ['1', '16777214'],
45 'example': ['vxlan-id 100']},
46 'vxlan-local-tunnelip' :
47 {'help' : 'vxlan local tunnel ip',
48 'validvals' : ['<ipv4>'],
49 'example': ['vxlan-local-tunnelip 172.16.20.103']},
52 'validvals' : ['<ipv4>'],
53 'example': ['vxlan-svcnodeip 172.16.22.125']},
55 {'help' : 'vxlan remote ip',
56 'validvals' : ['<ipv4>'],
57 'example': ['vxlan-remoteip 172.16.22.127'],
60 {'help' : 'vxlan learning yes/no',
61 'validvals' : ['yes', 'no', 'on', 'off'],
62 'example': ['vxlan-learning no'],
65 {'help' : 'vxlan aging timer',
66 'validrange' : ['0', '4096'],
67 'example': ['vxlan-ageing 300'],
69 'vxlan-purge-remotes' :
70 {'help' : 'vxlan purge existing remote entries',
71 'validvals' : ['yes', 'no'],
72 'example': ['vxlan-purge-remotes yes'],},
74 'help': 'vxlan UDP port (transmitted to vxlan driver)',
75 'validvals': ['<number>'],
76 'example': ['vxlan-port 4789'],
77 'validrange': ['1', '65536'],
81 {'help': 'vxlan physical device',
82 'example': ['vxlan-physdev eth1']},
85 _clagd_vxlan_anycast_ip
= ""
86 _vxlan_local_tunnelip
= None
88 def __init__(self
, *args
, **kargs
):
89 moduleBase
.__init
__(self
, *args
, **kargs
)
91 purge_remotes
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vxlan-purge-remotes')
93 self
._purge
_remotes
= utils
.get_boolean_from_string(purge_remotes
)
95 self
._purge
_remotes
= False
97 def syntax_check(self
, ifaceobj
, ifaceobj_getfunc
):
98 if self
._is
_vxlan
_device
(ifaceobj
):
99 if not ifaceobj
.get_attr_value_first('vxlan-local-tunnelip') and not vxlan
._vxlan
_local
_tunnelip
:
100 self
.logger
.warning('%s: missing vxlan-local-tunnelip' % ifaceobj
.name
)
102 return self
.syntax_check_localip_anycastip_equal(
104 ifaceobj
.get_attr_value_first('vxlan-local-tunnelip') or vxlan
._vxlan
_local
_tunnelip
,
105 vxlan
._clagd
_vxlan
_anycast
_ip
109 def syntax_check_localip_anycastip_equal(self
, ifname
, local_ip
, anycast_ip
):
111 if IPNetwork(local_ip
) == IPNetwork(anycast_ip
):
112 self
.logger
.warning('%s: vxlan-local-tunnelip and clagd-vxlan-anycast-ip are identical (%s)'
113 % (ifname
, local_ip
))
119 def get_dependent_ifacenames(self
, ifaceobj
, ifaceobjs_all
=None):
120 if self
._is
_vxlan
_device
(ifaceobj
):
121 ifaceobj
.link_kind |
= ifaceLinkKind
.VXLAN
122 self
._set
_global
_local
_ip
(ifaceobj
)
123 elif ifaceobj
.name
== 'lo':
124 clagd_vxlan_list
= ifaceobj
.get_attr_value('clagd-vxlan-anycast-ip')
126 if len(clagd_vxlan_list
) != 1:
127 self
.log_warn('%s: multiple clagd-vxlan-anycast-ip lines, using first one'
129 vxlan
._clagd
_vxlan
_anycast
_ip
= clagd_vxlan_list
[0]
131 self
._set
_global
_local
_ip
(ifaceobj
)
133 # If we should use a specific underlay device for the VXLAN
134 # tunnel make sure this device is set up before the VXLAN iface.
135 physdev
= ifaceobj
.get_attr_value_first('vxlan-physdev')
142 def _set_global_local_ip(self
, ifaceobj
):
143 vxlan_local_tunnel_ip
= ifaceobj
.get_attr_value_first('vxlan-local-tunnelip')
144 if vxlan_local_tunnel_ip
and not vxlan
._vxlan
_local
_tunnelip
:
145 vxlan
._vxlan
_local
_tunnelip
= vxlan_local_tunnel_ip
147 def _is_vxlan_device(self
, ifaceobj
):
148 if ifaceobj
.get_attr_value_first('vxlan-id'):
152 def _get_purge_remotes(self
, ifaceobj
):
154 return self
._purge
_remotes
155 purge_remotes
= ifaceobj
.get_attr_value_first('vxlan-purge-remotes')
157 purge_remotes
= utils
.get_boolean_from_string(purge_remotes
)
159 purge_remotes
= self
._purge
_remotes
162 def should_create_set_vxlan(self
, link_exists
, ifname
, vxlan_id
, local
, learning
, ageing
, group
):
164 should we issue a netlink: ip link add dev %ifname type vxlan ...?
165 checking each attribute against the cache
176 for attr_list
, value
in (
177 ((ifname
, 'linkinfo', Link
.IFLA_VXLAN_ID
), vxlan_id
),
178 ((ifname
, 'linkinfo', Link
.IFLA_VXLAN_AGEING
), ageing
),
179 ((ifname
, 'linkinfo', Link
.IFLA_VXLAN_LOCAL
), local
),
180 ((ifname
, 'linkinfo', Link
.IFLA_VXLAN_LEARNING
), learning
),
181 ((ifname
, 'linkinfo', Link
.IFLA_VXLAN_GROUP
), group
),
183 if value
and not self
.ipcmd
.cache_check(attr_list
, value
):
187 def _vxlan_create(self
, ifaceobj
):
188 vxlanid
= ifaceobj
.get_attr_value_first('vxlan-id')
190 ifname
= ifaceobj
.name
191 anycastip
= self
._clagd
_vxlan
_anycast
_ip
192 group
= ifaceobj
.get_attr_value_first('vxlan-svcnodeip')
194 local
= ifaceobj
.get_attr_value_first('vxlan-local-tunnelip')
195 if not local
and vxlan
._vxlan
_local
_tunnelip
:
196 local
= vxlan
._vxlan
_local
_tunnelip
198 self
.syntax_check_localip_anycastip_equal(ifname
, local
, anycastip
)
199 # if both local-ip and anycast-ip are identical the function prints a warning
201 ageing
= ifaceobj
.get_attr_value_first('vxlan-ageing')
202 vxlan_port
= ifaceobj
.get_attr_value_first('vxlan-port')
203 physdev
= ifaceobj
.get_attr_value_first('vxlan-physdev')
204 purge_remotes
= self
._get
_purge
_remotes
(ifaceobj
)
206 link_exists
= self
.ipcmd
.link_exists(ifname
)
208 if (not link_exists
or
209 not ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.BRIDGE_PORT
):
210 vxlan_learning
= ifaceobj
.get_attr_value_first('vxlan-learning')
211 if not vxlan_learning
:
212 vxlan_learning
= self
.get_attr_default_value('vxlan-learning')
213 learning
= utils
.get_boolean_from_string(vxlan_learning
)
215 learning
= utils
.get_boolean_from_string(
216 self
.ipcmd
.get_vxlandev_learning(ifname
))
219 vxlanattrs
= self
.ipcmd
.get_vxlandev_attrs(ifname
)
220 # on ifreload do not overwrite anycast_ip to individual ip
221 # if clagd has modified
223 running_localtunnelip
= vxlanattrs
.get('local')
224 if (anycastip
and running_localtunnelip
and
225 anycastip
== running_localtunnelip
):
226 local
= running_localtunnelip
227 if vxlanattrs
.get('vxlanid') != vxlanid
:
228 self
.log_error('%s: Cannot change running vxlan id: '
229 'Operation not supported' % ifname
, ifaceobj
)
231 vxlanid
= int(vxlanid
)
233 self
.log_error('%s: invalid vxlan-id \'%s\'' % (ifname
, vxlanid
), ifaceobj
)
236 group
= policymanager
.policymanager_api
.get_attr_default(
237 module_name
=self
.__class
__.__name
__,
238 attr
='vxlan-svcnodeip'
243 group
= IPv4Address(group
)
244 except AddressValueError
:
246 group_ip
= IPv4Network(group
).ip
247 self
.logger
.warning('%s: vxlan-svcnodeip %s: netmask ignored' % (ifname
, group
))
250 raise Exception('%s: invalid vxlan-svcnodeip %s: must be in ipv4 format' % (ifname
, group
))
253 local
= policymanager
.policymanager_api
.get_attr_default(
254 module_name
=self
.__class
__.__name
__,
255 attr
='vxlan-local-tunnelip'
260 local
= IPv4Address(local
)
261 except AddressValueError
:
263 local_ip
= IPv4Network(local
).ip
264 self
.logger
.warning('%s: vxlan-local-tunnelip %s: netmask ignored' % (ifname
, local
))
267 raise Exception('%s: invalid vxlan-local-tunnelip %s: must be in ipv4 format' % (ifname
, local
))
270 ageing
= policymanager
.policymanager_api
.get_attr_default(
271 module_name
=self
.__class
__.__name
__,
275 if not ageing
and link_exists
:
276 # if link doesn't exist we let the kernel define ageing
277 ageing
= self
.get_attr_default_value('vxlan-ageing')
280 vxlan_port
= policymanager
.policymanager_api
.get_attr_default(
281 module_name
=self
.__class
__.__name
__,
286 vxlan_port
= int(vxlan_port
)
288 # TypeError means vxlan_port was None
289 # ie: not provided by the user or the policy
290 vxlan_port
= netlink
.VXLAN_UDP_PORT
291 except ValueError as e
:
292 self
.logger
.warning('%s: vxlan-port: using default %s: invalid configured value %s' % (ifname
, netlink
.VXLAN_UDP_PORT
, str(e
)))
293 vxlan_port
= netlink
.VXLAN_UDP_PORT
295 if link_exists
and not ifupdownflags
.flags
.DRYRUN
:
296 cache_port
= vxlanattrs
.get(Link
.IFLA_VXLAN_PORT
)
297 if vxlan_port
!= cache_port
:
298 self
.logger
.warning('%s: vxlan-port (%s) cannot be changed - to apply the desired change please run: ifdown %s && ifup %s'
299 % (ifname
, cache_port
, ifname
, ifname
))
300 vxlan_port
= cache_port
302 if self
.should_create_set_vxlan(link_exists
, ifname
, vxlanid
, local
, learning
, ageing
, group
):
304 netlink
.link_add_vxlan(ifname
, vxlanid
,
311 except Exception as e_netlink
:
312 self
.logger
.debug('%s: vxlan netlink: %s' % (ifname
, str(e_netlink
)))
314 self
.ipcmd
.link_create_vxlan(ifname
, vxlanid
,
317 remoteips
=ifaceobj
.get_attr_value('vxlan-remoteip'),
318 learning
='on' if learning
else 'off',
320 except Exception as e_iproute2
:
321 self
.logger
.warning('%s: vxlan add/set failed: %s' % (ifname
, str(e_iproute2
)))
325 # manually adding an entry to the caching after creating/updating the vxlan
326 if not ifname
in linkCache
.links
:
327 linkCache
.links
[ifname
] = {'linkinfo': {}}
328 linkCache
.links
[ifname
]['linkinfo'].update({
329 'learning': learning
,
330 Link
.IFLA_VXLAN_LEARNING
: learning
,
331 'vxlanid': str(vxlanid
),
332 Link
.IFLA_VXLAN_ID
: vxlanid
335 linkCache
.links
[ifname
]['linkinfo'].update({
337 Link
.IFLA_VXLAN_AGEING
: int(ageing
)
342 self
.logger
.info('%s: vxlan already exists' % ifname
)
343 # if the vxlan already exists it's already cached
345 remoteips
= ifaceobj
.get_attr_value('vxlan-remoteip')
348 for remoteip
in remoteips
:
349 IPv4Address(remoteip
)
350 except Exception as e
:
351 self
.log_error('%s: vxlan-remoteip: %s' %(ifaceobj
.name
, str(e
)))
353 if purge_remotes
or remoteips
:
354 # figure out the diff for remotes and do the bridge fdb updates
355 # only if provisioned by user and not by an vxlan external
357 peers
= self
.ipcmd
.get_vxlan_peers(ifaceobj
.name
, group
)
358 if local
and remoteips
and local
in remoteips
:
359 remoteips
.remove(local
)
360 cur_peers
= set(peers
)
362 new_peers
= set(remoteips
)
363 del_list
= cur_peers
.difference(new_peers
)
364 add_list
= new_peers
.difference(cur_peers
)
369 for addr
in del_list
:
371 self
.ipcmd
.bridge_fdb_del(ifaceobj
.name
,
377 for addr
in add_list
:
379 self
.ipcmd
.bridge_fdb_append(ifaceobj
.name
,
385 def _up(self
, ifaceobj
):
386 self
._vxlan
_create
(ifaceobj
)
388 def _down(self
, ifaceobj
):
390 self
.ipcmd
.link_delete(ifaceobj
.name
)
392 self
.log_warn(str(e
))
394 def _query_check_n_update(self
, ifaceobj
, ifaceobjcurr
, attrname
, attrval
,
396 if not ifaceobj
.get_attr_value_first(attrname
):
398 if running_attrval
and attrval
== running_attrval
:
399 ifaceobjcurr
.update_config_with_status(attrname
, attrval
, 0)
401 ifaceobjcurr
.update_config_with_status(attrname
, running_attrval
, 1)
403 def _query_check_n_update_addresses(self
, ifaceobjcurr
, attrname
,
404 addresses
, running_addresses
):
407 if a
in running_addresses
:
408 ifaceobjcurr
.update_config_with_status(attrname
, a
, 0)
410 ifaceobjcurr
.update_config_with_status(attrname
, a
, 1)
411 running_addresses
= Set(running_addresses
).difference(
413 [ifaceobjcurr
.update_config_with_status(attrname
, a
, 1)
414 for a
in running_addresses
]
416 def _query_check(self
, ifaceobj
, ifaceobjcurr
):
417 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
419 # Update vxlan object
420 vxlanattrs
= self
.ipcmd
.get_vxlandev_attrs(ifaceobj
.name
)
422 ifaceobjcurr
.check_n_update_config_with_status_many(ifaceobj
,
423 self
.get_mod_attrs(), -1)
425 self
._query
_check
_n
_update
(ifaceobj
, ifaceobjcurr
, 'vxlan-id',
426 ifaceobj
.get_attr_value_first('vxlan-id'),
427 vxlanattrs
.get('vxlanid'))
429 self
._query
_check
_n
_update
(
433 ifaceobj
.get_attr_value_first('vxlan-port'),
434 str(vxlanattrs
.get(Link
.IFLA_VXLAN_PORT
))
437 running_attrval
= vxlanattrs
.get('local')
438 attrval
= ifaceobj
.get_attr_value_first('vxlan-local-tunnelip')
440 attrval
= vxlan
._vxlan
_local
_tunnelip
441 ifaceobj
.update_config('vxlan-local-tunnelip', attrval
)
443 if running_attrval
== self
._clagd
_vxlan
_anycast
_ip
:
444 # if local ip is anycast_ip, then let query_check to go through
445 attrval
= self
._clagd
_vxlan
_anycast
_ip
446 self
._query
_check
_n
_update
(ifaceobj
, ifaceobjcurr
, 'vxlan-local-tunnelip',
447 attrval
, running_attrval
)
449 self
._query
_check
_n
_update
(ifaceobj
, ifaceobjcurr
, 'vxlan-svcnodeip',
450 ifaceobj
.get_attr_value_first('vxlan-svcnodeip'),
451 vxlanattrs
.get('svcnode'))
453 purge_remotes
= self
._get
_purge
_remotes
(ifaceobj
)
454 if purge_remotes
or ifaceobj
.get_attr_value('vxlan-remoteip'):
455 # If purge remotes or if vxlan-remoteip's are set
456 # in the config file, we are owners of the installed
457 # remote-ip's, lets check and report any remote ips we don't
459 self
._query
_check
_n
_update
_addresses
(ifaceobjcurr
, 'vxlan-remoteip',
460 ifaceobj
.get_attr_value('vxlan-remoteip'),
461 self
.ipcmd
.get_vxlan_peers(ifaceobj
.name
, vxlanattrs
.get('svcnode')))
463 learning
= ifaceobj
.get_attr_value_first('vxlan-learning')
465 running_learning
= vxlanattrs
.get('learning')
466 if learning
== 'yes' and running_learning
== 'on':
467 running_learning
= 'yes'
468 elif learning
== 'no' and running_learning
== 'off':
469 running_learning
= 'no'
470 if learning
== running_learning
:
471 ifaceobjcurr
.update_config_with_status('vxlan-learning',
474 ifaceobjcurr
.update_config_with_status('vxlan-learning',
476 ageing
= ifaceobj
.get_attr_value_first('vxlan-ageing')
478 ageing
= self
.get_mod_subattr('vxlan-ageing', 'default')
479 self
._query
_check
_n
_update
(ifaceobj
, ifaceobjcurr
, 'vxlan-ageing',
480 ageing
, vxlanattrs
.get('ageing'))
482 physdev
= ifaceobj
.get_attr_value_first('vxlan-physdev')
485 ifla_vxlan_link
= vxlanattrs
.get(Link
.IFLA_VXLAN_LINK
)
488 self
._query
_check
_n
_update
(
493 netlink
.get_iface_name(ifla_vxlan_link
)
496 ifaceobjcurr
.update_config_with_status('vxlan-physdev', physdev
, 1)
499 def _query_running(self
, ifaceobjrunning
):
500 vxlanattrs
= self
.ipcmd
.get_vxlandev_attrs(ifaceobjrunning
.name
)
503 attrval
= vxlanattrs
.get('vxlanid')
505 ifaceobjrunning
.update_config('vxlan-id', vxlanattrs
.get('vxlanid'))
507 # if there is no vxlan id, this is not a vxlan port
510 ifaceobjrunning
.update_config('vxlan-port', vxlanattrs
.get(Link
.IFLA_VXLAN_PORT
))
512 attrval
= vxlanattrs
.get('local')
514 ifaceobjrunning
.update_config('vxlan-local-tunnelip', attrval
)
515 attrval
= vxlanattrs
.get('svcnode')
517 ifaceobjrunning
.update_config('vxlan-svcnode', attrval
)
518 purge_remotes
= self
._get
_purge
_remotes
(None)
520 # if purge_remotes is on, it means we own the
521 # remote ips. Query them and add it to the running config
522 attrval
= self
.ipcmd
.get_vxlan_peers(ifaceobjrunning
.name
, vxlanattrs
.get('svcnode'))
524 [ifaceobjrunning
.update_config('vxlan-remoteip', a
)
526 attrval
= vxlanattrs
.get('learning')
527 if attrval
and attrval
== 'on':
528 ifaceobjrunning
.update_config('vxlan-learning', 'on')
529 attrval
= vxlanattrs
.get('ageing')
531 ifaceobjrunning
.update_config('vxlan-ageing', vxlanattrs
.get('ageing'))
533 ifla_vxlan_link
= vxlanattrs
.get(Link
.IFLA_VXLAN_LINK
)
535 ifaceobjrunning
.update_config(
537 netlink
.get_iface_name(ifla_vxlan_link
)
540 _run_ops
= {'pre-up' : _up
,
542 'query-checkcurr' : _query_check
,
543 'query-running' : _query_running
}
546 return self
._run
_ops
.keys()
548 def _init_command_handlers(self
):
550 self
.ipcmd
= LinkUtils()
552 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None, **extra_args
):
553 op_handler
= self
._run
_ops
.get(operation
)
556 if (operation
!= 'query-running' and
557 not self
._is
_vxlan
_device
(ifaceobj
)):
559 self
._init
_command
_handlers
()
560 if operation
== 'query-checkcurr':
561 op_handler(self
, ifaceobj
, query_ifaceobj
)
563 op_handler(self
, ifaceobj
)