1 # Copyright (C) 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License as
5 # published by the Free Software Foundation; version 2.
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 # General Public License for more details.
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, write to the Free Software
14 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 # https://www.gnu.org/licenses/gpl-2.0-standalone.html
20 # Julien Fortin, julien@cumulusnetworks.com
22 # iproute2 -- contains all iproute2 related operation
33 from ifupdown2
.lib
.sysfs
import Sysfs
34 from ifupdown2
.lib
.base_objects
import Cache
, Requirements
36 import ifupdown2
.nlmanager
.ipnetwork
as ipnetwork
38 from ifupdown2
.ifupdown
.utils
import utils
39 from ifupdown2
.ifupdown
.iface
import ifaceLinkPrivFlags
40 from ifupdown2
.nlmanager
.nlpacket
import Link
41 except (ImportError, ModuleNotFoundError
):
42 from lib
.sysfs
import Sysfs
43 from lib
.base_objects
import Cache
, Requirements
45 import nlmanager
.ipnetwork
as ipnetwork
47 from ifupdown
.utils
import utils
48 from ifupdown
.iface
import ifaceLinkPrivFlags
49 from nlmanager
.nlpacket
import Link
51 # WORK AROUND - Tunnel creation should be done via netlink and not iproute2 ####
56 import ifupdown2
.nlmanager
.nlpacket
as nlpacket
#
58 import nlmanager
.nlpacket
as nlpacket
#
59 ################################################################################
62 class IPRoute2(Cache
, Requirements
):
65 VXLAN_PEER_REGEX_PATTERN
= re
.compile("\s+dst\s+(\d+.\d+.\d+.\d+)\s+")
69 Requirements
.__init
__(self
)
74 self
.__batch
_mode
= False
76 # if bridge utils is not installed overrrides specific functions to
77 # avoid constantly checking bridge_utils_is_installed
78 if not Requirements
.bridge_utils_is_installed
:
79 self
.bridge_set_stp
= lambda _
, __
: None
80 self
.bridge_del_mcqv4src
= lambda _
, __
: None
81 self
.bridge_set_mcqv4src
= lambda _
, __
, ___
: None
83 ############################################################################
85 ############################################################################
87 def __update_cache_after_link_creation(self
, ifname
, kind
):
89 WORK AROUND - when creating tunnel via iproute2 we still need to fill
90 our internal cache to keep track of this interface until we receive the
91 NEWLINK notification. This code is a copy-paste from:
92 nlcache.tx_nlpacket_get_response_with_error_and_cache_on_ack
98 packet
= nlpacket
.Link(nlpacket
.RTM_NEWLINK
, False, use_color
=False)
99 packet
.flags
= nlpacket
.NLM_F_CREATE | nlpacket
.NLM_F_REQUEST | nlpacket
.NLM_F_ACK
100 packet
.body
= struct
.pack('Bxxxiii', socket
.AF_UNSPEC
, 0, 0, 0)
101 packet
.add_attribute(nlpacket
.Link
.IFLA_IFNAME
, ifname
)
102 packet
.add_attribute(nlpacket
.Link
.IFLA_LINKINFO
, {
103 nlpacket
.Link
.IFLA_INFO_KIND
: kind
,
104 nlpacket
.Link
.IFLA_INFO_DATA
: {}
106 packet
.build_message(0, 0)
107 # When creating a new link via netlink, we don't always wait for the kernel
108 # NEWLINK notification to be cached to continue. If our request is ACKed by
109 # the OS we assume that the link was successfully created. Since we aren't
110 # waiting for the kernel notification to continue we need to manually fill
111 # our cache with the packet we just TX'ed. Once the NEWLINK notification
112 # is received it will simply override the previous entry.
113 # We need to keep track of those manually cached packets. We set a private
114 # flag on the objects via the attribute priv_flags
115 packet
.priv_flags |
= nlpacket
.NLM_F_REQUEST
117 # we need to decode the service header so all the attribute are properly
118 # filled in the packet object that we are about to store in cache.
119 # i.e.: packet.flags shouldn't contain NLM_F_* values but IFF_* (in case of Link object)
120 # otherwise call to cache.link_is_up() will probably return True
121 packet
.decode_service_header()
123 # we can ignore all errors
126 # Then we can use our normal "add_link" API call to cache the packet
127 # and fill up our additional internal data structures.
128 self
.cache
.add_link(packet
)
130 ############################################################################
132 ############################################################################
134 def __add_to_batch(self
, prefix
, cmd
):
135 if prefix
in self
.__batch
:
136 self
.__batch
[prefix
].append(cmd
)
138 self
.__batch
[prefix
] = [cmd
]
140 def __execute_or_batch(self
, prefix
, cmd
):
141 if self
.__batch
_mode
:
142 self
.__add
_to
_batch
(prefix
, cmd
)
144 utils
.exec_command("%s %s" % (prefix
, cmd
))
146 def __execute_or_batch_dry_run(self
, prefix
, cmd
):
148 The batch function has it's own dryrun handler so we only handle
149 dryrun for non-batch mode. Which will be removed once the "utils"
150 module has it's own dryrun handlers
152 if self
.__batch
_mode
:
153 self
.__add
_to
_batch
(prefix
, cmd
)
155 self
.log_info_dry_run("executing: %s %s" % (prefix
, cmd
))
157 def batch_start(self
):
158 if not self
.__batch
_mode
:
159 self
.__batch
_mode
= True
162 def batch_commit(self
):
164 if not self
.__batch
_mode
or not self
.__batch
:
166 for prefix
, commands
in self
.__batch
.items():
168 "%s -force -batch -" % prefix
,
169 stdin
="\n".join(commands
)
174 self
.__batch
_mode
= False
178 ############################################################################
180 ############################################################################
182 def link_up(self
, ifname
):
183 # TODO: if we already in a batch we shouldn't check the cache as the link might be DOWN during the batch
184 if not self
.cache
.link_is_up(ifname
):
185 self
.link_up_force(ifname
)
187 def link_down(self
, ifname
):
188 if self
.cache
.link_is_up(ifname
):
189 self
.link_down_force(ifname
)
191 def link_up_dry_run(self
, ifname
):
192 self
.link_up_force(ifname
)
194 def link_down_dry_run(self
, ifname
):
195 self
.link_down_force(ifname
)
197 def link_up_force(self
, ifname
):
198 self
.__execute
_or
_batch
(utils
.ip_cmd
, "link set dev %s up" % ifname
)
200 def link_down_force(self
, ifname
):
201 self
.__execute
_or
_batch
(utils
.ip_cmd
, "link set dev %s down" % ifname
)
205 def link_set_master(self
, ifname
, master
):
206 if master
!= self
.cache
.get_master(ifname
):
207 self
.__execute
_or
_batch
(
209 "link set dev %s master %s" % (ifname
, master
)
212 def link_set_master_dry_run(self
, ifname
, master
):
213 self
.__execute
_or
_batch
(
215 "link set dev %s master %s" % (ifname
, master
)
220 def link_set_address(self
, ifname
, address
):
221 if utils
.mac_str_to_int(address
) != self
.cache
.get_link_address_raw(ifname
):
222 self
.link_down(ifname
)
223 self
.__execute
_or
_batch
(
225 "link set dev %s address %s" % (ifname
, address
)
229 def link_set_address_dry_run(self
, ifname
, address
):
230 self
.link_down(ifname
)
231 self
.__execute
_or
_batch
(
233 "link set dev %s address %s" % (ifname
, address
)
237 def link_set_address_and_keep_down(self
, ifname
, address
, keep_down
=False):
238 if utils
.mac_str_to_int(address
) != self
.cache
.get_link_address_raw(ifname
):
240 self
.link_down(ifname
)
241 self
.__execute
_or
_batch
(
243 "link set dev %s address %s" % (ifname
, address
)
246 self
.link_up_force(ifname
)
248 def link_set_address_and_keep_down_dry_run(self
, ifname
, address
, keep_down
=False):
249 self
.link_down(ifname
)
250 self
.__execute
_or
_batch
(
252 "link set dev %s address %s" % (ifname
, address
)
259 def link_add_macvlan(self
, ifname
, macvlan_ifname
, macvlan_mode
):
261 "%s link add link %s name %s type macvlan mode %s"
262 % (utils
.ip_cmd
, ifname
, macvlan_ifname
, macvlan_mode
)
265 def link_add_macvlan_dry_run(self
, ifname
, macvlan_ifname
, macvlan_mode
):
266 # this dryrun method can be removed once dryrun handlers
267 # are added to the utils module
268 self
.log_info_ifname_dry_run(ifname
, "executing %s link add link %s name %s type macvlan mode %s"
269 % (utils
.ip_cmd
, ifname
, macvlan_ifname
, macvlan_mode
)
274 def link_add_veth(self
, ifname
, peer_name
):
276 "%s link add %s type veth peer name %s"
277 % (utils
.ip_cmd
, ifname
, peer_name
)
282 def link_add_single_vxlan(self
, link_exists
, ifname
, ip
, group
, physdev
, port
, vnifilter
="off", ttl
=None):
283 self
.logger
.info("creating single vxlan device: %s" % ifname
)
286 # When updating an SVD we need to use `ip link set` and we have to
287 # drop the external keyword:
288 # $ ip link set dev vxlan0 type vxlan external local 27.0.0.242 dev ipmr-lo
289 # Error: vxlan: cannot change COLLECT_METADATA flag.
290 cmd
= ["link set dev %s type vxlan" % ifname
]
292 cmd
= ["link add dev %s type vxlan external" % ifname
]
294 # when changing local ip, if we specify vnifilter we get:
295 # Error: vxlan: cannot change flag.
296 # So we are only setting this attribute on vxlan creation
297 if vnifilter
and utils
.get_boolean_from_string(vnifilter
):
298 cmd
.append("vnifilter")
301 cmd
.append("local %s" % ip
)
304 cmd
.append("dev %s" % physdev
)
307 cmd
.append("group %s" % group
)
310 cmd
.append("dstport %s" % port
)
313 cmd
.append("ttl %s" % ttl
)
315 self
.__execute
_or
_batch
(utils
.ip_cmd
, " ".join(cmd
))
316 self
.__update
_cache
_after
_link
_creation
(ifname
, "vxlan")
318 def link_add_l3vxi(self
, link_exists
, ifname
, ip
, group
, physdev
, port
, ttl
=None):
319 self
.logger
.info("creating l3vxi device: %s" % ifname
)
322 # When updating an SVD we need to use `ip link set` and we have to
323 # drop the external keyword:
324 # $ ip link set dev vxlan0 type vxlan external local 27.0.0.242 dev ipmr-lo
325 # Error: vxlan: cannot change COLLECT_METADATA flag.
326 cmd
= ["link set dev %s type vxlan" % ifname
]
328 cmd
= ["link add dev %s type vxlan external vnifilter" % ifname
]
329 # when changing local ip, if we specify vnifilter we get:
330 # Error: vxlan: cannot change flag.
331 # So we are only setting this attribute on vxlan creation
334 cmd
.append("local %s" % ip
)
337 cmd
.append("dev %s" % physdev
)
340 cmd
.append("group %s" % group
)
343 cmd
.append("dstport %s" % port
)
346 cmd
.append("ttl %s" % ttl
)
348 self
.__execute
_or
_batch
(utils
.ip_cmd
, " ".join(cmd
))
349 self
.__update
_cache
_after
_link
_creation
(ifname
, "vxlan")
351 def link_create_vxlan(self
, name
, vxlanid
, localtunnelip
=None, svcnodeip
=None,
352 remoteips
=None, learning
='on', ageing
=None, ttl
=None, physdev
=None, udp_csum
='on', tos
= None):
353 if svcnodeip
and remoteips
:
354 raise Exception("svcnodeip and remoteip are mutually exclusive")
356 if self
.cache
.link_exists(name
):
358 "link set dev %s type vxlan dstport %d"
359 % (name
, self
.VXLAN_UDP_PORT
)
363 "link add dev %s type vxlan id %s dstport %d"
364 % (name
, vxlanid
, self
.VXLAN_UDP_PORT
)
368 if svcnodeip
.ip
.is_multicast
:
369 cmd
.append("group %s" % svcnodeip
)
371 cmd
.append("remote %s" % svcnodeip
)
374 cmd
.append("ageing %s" % ageing
)
376 if learning
== 'off':
377 cmd
.append("nolearning")
379 if udp_csum
== 'off':
380 cmd
.append("noudpcsum")
383 cmd
.append("ttl %s" % ttl
)
386 cmd
.append("tos %s" % tos
)
389 cmd
.append("dev %s" % physdev
)
392 cmd
.append("local %s" % localtunnelip
)
394 self
.__execute
_or
_batch
(utils
.ip_cmd
, " ".join(cmd
))
396 def get_vxlan_peers(self
, dev
, svcnodeip
):
397 cmd
= "%s fdb show brport %s" % (utils
.bridge_cmd
, dev
)
400 ps
= subprocess
.Popen(shlex
.split(cmd
), stdout
=subprocess
.PIPE
, close_fds
=False)
401 utils
.enable_subprocess_signal_forwarding(ps
, signal
.SIGINT
)
402 output
= subprocess
.check_output(("grep", "00:00:00:00:00:00"), stdin
=ps
.stdout
).decode()
404 utils
.disable_subprocess_signal_forwarding(signal
.SIGINT
)
406 for l
in output
.split('\n'):
409 m
= self
.VXLAN_PEER_REGEX_PATTERN
.search(l
)
410 if m
and m
.group(1) != svcnodeip
:
411 cur_peers
.append(m
.group(1))
413 self
.logger
.warning('error parsing ip link output')
414 except subprocess
.CalledProcessError
as e
:
415 if e
.returncode
!= 1:
416 self
.logger
.error(str(e
))
418 utils
.disable_subprocess_signal_forwarding(signal
.SIGINT
)
423 def link_add_xfrm(self
, ifname
, xfrm_name
, xfrm_id
):
424 utils
.exec_commandl(['ip', 'link', 'add', xfrm_name
, 'type', 'xfrm', 'dev', ifname
, 'if_id', xfrm_id
])
425 self
.__update
_cache
_after
_link
_creation
(xfrm_name
, "xfrm")
427 def link_add_openvswitch(self
, ifname
, kind
):
428 self
.__update
_cache
_after
_link
_creation
(ifname
, kind
)
430 ############################################################################
432 ############################################################################
434 def tunnel_create(self
, tunnelname
, mode
, attrs
=None):
435 if self
.cache
.link_exists(tunnelname
):
442 if mode
in ["gretap"]:
443 cmd
.append("link add %s type %s" % (tunnelname
, mode
))
445 cmd
.append("tunnel add %s mode %s" % (tunnelname
, mode
))
448 for k
, v
in attrs
.items():
453 utils
.exec_command("%s %s" % (utils
.ip_cmd
, " ".join(cmd
)))
454 self
.__update
_cache
_after
_link
_creation
(tunnelname
, mode
)
456 def tunnel_change(self
, tunnelname
, attrs
=None):
457 """ tunnel change function """
458 if not self
.cache
.link_exists(tunnelname
):
460 cmd
= ["tunnel change %s" % tunnelname
]
462 for k
, v
in attrs
.items():
466 self
.__execute
_or
_batch
(utils
.ip_cmd
, " ".join(cmd
))
468 ############################################################################
470 ############################################################################
472 def addr_flush(self
, ifname
):
473 if self
.cache
.link_has_ip(ifname
):
474 self
.__execute
_or
_batch
(utils
.ip_cmd
, "addr flush dev %s" % ifname
)
476 def link_set_ipv6_addrgen_dry_run(self
, ifname
, addrgen
, link_created
):
477 addrgen_str
= "none" if addrgen
else "eui64"
478 self
.link_down(ifname
)
479 self
.__execute
_or
_batch
(utils
.ip_cmd
, "link set dev %s addrgenmode %s" % (ifname
, addrgen_str
))
482 def link_set_ipv6_addrgen(self
, ifname
, addrgen
, link_created
):
484 IFLA_INET6_ADDR_GEN_MODE values:
493 cached_ipv6_addr_gen_mode
= self
.cache
.get_link_ipv6_addrgen_mode(ifname
)
495 if cached_ipv6_addr_gen_mode
== addrgen
:
498 disabled_ipv6
= self
.sysfs
.get_ipv6_conf_disable_ipv6(ifname
)
501 self
.logger
.info("%s: cannot set addrgen: ipv6 is disabled on this device" % ifname
)
505 link_mtu
= self
.sysfs
.link_get_mtu(ifname
)
507 link_mtu
= self
.cache
.get_link_mtu(ifname
)
510 self
.logger
.info("%s: ipv6 addrgen is disabled on device with MTU "
511 "lower than 1280 (current mtu %s): cannot set addrgen %s"
512 % (ifname
, link_mtu
, "off" if addrgen
else "on"))
516 # When setting addrgenmode it is necessary to flap the macvlan
517 # device. After flapping the device we also need to re-add all
518 # the user configuration. The best way to add the user config
519 # is to flush our internal address cache
520 self
.cache
.address_flush_link(ifname
)
522 is_link_up
= self
.cache
.link_is_up(ifname
)
525 self
.link_down_force(ifname
)
527 self
.__execute
_or
_batch
(
529 "link set dev %s addrgenmode %s" % (ifname
, Link
.ifla_inet6_addr_gen_mode_dict
.get(addrgen
))
533 self
.link_up_force(ifname
)
538 def __compare_user_config_vs_running_state(running_addrs
, user_addrs
):
542 for ip
in user_addrs
or []:
549 for ip
in running_addrs
or []:
550 running_ipobj
.append(ip
)
552 return running_ipobj
== (ip4
+ ip6
)
554 def add_addresses(self
, ifacobj
, ifname
, address_list
, purge_existing
=False, metric
=None, with_address_virtual
=False):
556 running_address_list
= self
.cache
.get_managed_ip_addresses(
559 with_address_virtual
=with_address_virtual
562 if self
.__compare
_user
_config
_vs
_running
_state
(running_address_list
, address_list
):
565 # if primary address is not same, there is no need to keep any - reset all addresses
566 if running_address_list
and address_list
and address_list
[0] != running_address_list
[0]:
571 for addr
in running_address_list
or []:
575 self
.__execute
_or
_batch
(utils
.ip_cmd
, "addr del %s dev %s" % (addr
, ifname
))
576 except Exception as e
:
577 self
.logger
.warning("%s: removing ip address failed: %s" % (ifname
, str(e
)))
578 for addr
in address_list
:
581 self
.__execute
_or
_batch
(utils
.ip_cmd
, "addr add %s dev %s metric %s" % (addr
, ifname
, metric
))
583 self
.__execute
_or
_batch
(utils
.ip_cmd
, "addr add %s dev %s" % (addr
, ifname
))
584 except Exception as e
:
585 self
.logger
.error("%s: add_address: %s" % (ifname
, str(e
)))
587 ############################################################################
589 ############################################################################
592 def bridge_set_stp(bridge
, stp_state
):
593 utils
.exec_command("%s stp %s %s" % (utils
.brctl_cmd
, bridge
, stp_state
))
596 def bridge_fdb_show_dev(dev
):
599 output
= utils
.exec_command("%s fdb show dev %s" % (utils
.bridge_cmd
, dev
))
601 for fdb_entry
in output
.splitlines():
603 entries
= fdb_entry
.split()
604 fdbs
.setdefault(entries
[2], []).append(entries
[0])
612 def bridge_fdb_show_dev_raw_with_filters(dev
, filters
):
614 output
= utils
.exec_command("%s fdb show dev %s" % (utils
.bridge_cmd
, dev
)).splitlines()
617 filter_present
= True
620 filter_present
= False
622 filtered_output
.append(l
)
623 return filtered_output
628 def bridge_fdb_add(dev
, address
, vlan
=None, bridge
=True, remote
=None):
629 target
= "self" if bridge
else ""
630 vlan_str
= "vlan %s " % vlan
if vlan
else ""
631 dst_str
= "dst %s " % remote
if remote
else ""
634 "%s fdb replace %s dev %s %s %s %s"
646 def bridge_fdb_add_src_vni(dev
, src_vni
, dst_ip
):
648 bridge fdb add dev $dev 00:00:00:00:00:00 src_vni $src_vni dst $dst_ip static self
651 "%s fdb add dev %s 00:00:00:00:00:00 src_vni %s dst %s permanent self"
661 def bridge_fdb_append(dev
, address
, vlan
=None, bridge
=True, remote
=None, src_vni
=None):
662 cmd
= ["%s fdb append %s dev %s" % (utils
.bridge_cmd
, address
, dev
)]
668 cmd
.append("vlan %s" % vlan
)
671 cmd
.append("dst %s" % remote
)
674 cmd
.append("src_vni %s" % src_vni
)
676 utils
.exec_command(" ".join(cmd
))
679 def bridge_fdb_del_src_vni(dev
, mac
, src_vni
):
681 "%s fdb del %s dev %s src_vni %s"
691 def bridge_fdb_del(dev
, address
, vlan
=None, bridge
=True, remote
=None):
692 target
= "self" if bridge
else ""
693 vlan_str
= "vlan %s " % vlan
if vlan
else ""
694 dst_str
= "dst %s " % remote
if remote
else ""
697 "%s fdb del %s dev %s %s %s %s"
709 def bridge_fdb_del_raw(dev
, args
):
710 utils
.exec_command("%s fdb del dev %s %s" % (utils
.bridge_cmd
, dev
, args
))
713 def bridge_vlan_del_vid_list(ifname
, vids
):
718 "%s vlan del vid %s dev %s" % (utils
.bridge_cmd
, v
, ifname
)
721 def bridge_vlan_del_vid_list_self(self
, ifname
, vids
, is_bridge
=True):
722 target
= "self" if is_bridge
else ""
724 self
.__execute
_or
_batch
(
726 "vlan del vid %s dev %s %s" % (v
, ifname
, target
)
729 def bridge_vlan_del_vlan_tunnel_info(self
, ifname
, vids
, vnis
):
730 self
.__execute
_or
_batch
(
732 "vlan del dev %s vid %s tunnel_info id %s" % (
737 def bridge_vlan_add_vlan_tunnel_info(self
, ifname
, vids
, vnis
):
739 self
.__execute
_or
_batch
(
741 "vlan add dev %s vid %s tunnel_info id %s" % (
745 except Exception as e
:
746 if "exists" not in str(e
).lower():
749 def bridge_vlan_tunnel_show(self
, ifname
):
752 for entry
in utils
.exec_command("%s vlan tunnel dev %s" % (utils
.bridge_cmd
, ifname
)).splitlines()[1:]:
757 entry_list
= entry
.split()
758 length
= len(entry_list
)
761 # if len == 3, we need to remove the ifname from the list
762 # $ bridge vlan tunnel show dev vxlan42
763 # port vlan ids tunnel id
765 entry_list
= entry_list
[1:]
770 vnis
= utils
.ranges_to_ints([entry_list
[0]])
771 tunnel_ids
= utils
.ranges_to_ints([entry_list
[1]])
773 for vni
, tunnel_id
in zip(vnis
, tunnel_ids
):
774 tunnel_info
[int(vni
)] = int(tunnel_id
)
776 except Exception as e
:
777 self
.logger
.debug("iproute2: bridge vlan tunnel dev %s: %s" % (ifname
, str(e
)))
781 def bridge_vlan_add_vid_list(ifname
, vids
):
784 "%s vlan add vid %s dev %s" % (utils
.bridge_cmd
, v
, ifname
)
787 def bridge_vlan_add_vid_list_self(self
, ifname
, vids
, is_bridge
=True):
788 target
= "self" if is_bridge
else ""
790 self
.__execute
_or
_batch
(
792 "vlan add vid %s dev %s %s" % (v
, ifname
, target
)
795 def bridge_vlan_del_vid_list_self(self
, ifname
, vids
, is_bridge
=True):
796 target
= "self" if is_bridge
else ""
798 self
.__execute
_or
_batch
(
800 "vlan del vid %s dev %s %s" % (v
, ifname
, target
)
803 def bridge_vlan_del_pvid(self
, ifname
, pvid
):
804 self
.__execute
_or
_batch
(
806 "vlan del vid %s untagged pvid dev %s" % (pvid
, ifname
)
809 def bridge_vlan_add_pvid(self
, ifname
, pvid
):
810 self
.__execute
_or
_batch
(
812 "vlan add vid %s untagged pvid dev %s" % (pvid
, ifname
)
815 def bridge_del_mcqv4src(self
, bridge
, vlan
):
818 except Exception as e
:
819 self
.logger
.info("%s: del mcqv4src vlan: invalid parameter %s: %s"
820 % (bridge
, vlan
, str(e
)))
822 utils
.exec_command("%s delmcqv4src %s %d" % (utils
.brctl_cmd
, bridge
, vlan
))
824 def bridge_set_mcqv4src(self
, bridge
, vlan
, mcquerier
):
827 except Exception as e
:
828 self
.logger
.info("%s: set mcqv4src vlan: invalid parameter %s: %s" % (bridge
, vlan
, str(e
)))
830 if vlan
== 0 or vlan
> 4095:
831 self
.logger
.warning("mcqv4src vlan '%d' invalid range" % vlan
)
834 ip
= mcquerier
.split(".")
836 self
.logger
.warning("mcqv4src '%s' invalid IPv4 address" % mcquerier
)
839 if not k
.isdigit() or int(k
, 10) < 0 or int(k
, 10) > 255:
840 self
.logger
.warning("mcqv4src '%s' invalid IPv4 address" % mcquerier
)
843 utils
.exec_command("%s setmcqv4src %s %d %s" % (utils
.brctl_cmd
, bridge
, vlan
, mcquerier
))
845 ############################################################################
847 ############################################################################
850 def route_add_gateway(ifname
, gateway
, vrf
=None, metric
=None, onlink
=True):
855 cmd
= "%s route replace default via %s proto kernel" % (utils
.ip_cmd
, gateway
)
857 cmd
= "%s route replace table %s default via %s proto kernel" % (utils
.ip_cmd
, vrf
, gateway
)
860 cmd
+= " metric %s" % metric
862 cmd
+= " dev %s" % ifname
867 utils
.exec_command(cmd
)
870 def route_del_gateway(ifname
, gateway
, vrf
=None, metric
=None):
873 we don't need a DRYRUN handler here as utils.exec_command should have one
879 cmd
= "%s route del default via %s proto kernel" % (utils
.ip_cmd
, gateway
)
881 cmd
= "%s route del table %s default via %s proto kernel" % (utils
.ip_cmd
, vrf
, gateway
)
884 cmd
+= " metric %s" % metric
886 cmd
+= " dev %s" % ifname
887 utils
.exec_command(cmd
)
889 def fix_ipv6_route_metric(self
, ifaceobj
, macvlan_ifacename
, ips
):
892 if ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.VRF_SLAVE
:
894 for upper_iface
in ifaceobj
.upperifaces
:
895 vrf_table
= self
.cache
.get_vrf_table(upper_iface
)
903 ip_network_obj
= ipaddress
.ip_network(ip
)
905 if ip_network_obj
.version
== 6:
906 route_prefix
= '%s/%d' % (ip_network_obj
.network
, ip_network_obj
.prefixlen
)
909 self
.__execute
_or
_batch
(
911 "route del %s table %s dev %s" % (route_prefix
, vrf_table
, macvlan_ifacename
)
914 self
.__execute
_or
_batch
(
916 "route del %s dev %s" % (route_prefix
, macvlan_ifacename
)
919 ip_route_del
.append((route_prefix
, vrf_table
))
921 for ip
, vrf_table
in ip_route_del
:
923 self
.__execute
_or
_batch
(
925 "route add %s table %s dev %s proto kernel metric 9999" % (ip
, vrf_table
, macvlan_ifacename
)
928 self
.__execute
_or
_batch
(
930 "route add %s dev %s proto kernel metric 9999" % (ip
, macvlan_ifacename
)
933 def ip_route_get_dev(self
, prefix
, vrf_master
=None):
936 cmd
= "%s route get %s vrf %s" % (utils
.ip_cmd
, prefix
, vrf_master
)
938 cmd
= "%s route get %s" % (utils
.ip_cmd
, prefix
)
940 output
= utils
.exec_command(cmd
)
942 rline
= output
.splitlines()[0]
944 rattrs
= rline
.split()
945 return rattrs
[rattrs
.index("dev") + 1]
946 except Exception as e
:
947 self
.logger
.debug("ip_route_get_dev: failed .. %s" % str(e
))
950 def bridge_vni_update(self
, vxlandev
, vnisd
):
951 for vr
, g
in vnisd
.items():
952 cmd_args
= "vni add dev %s vni %s" % (vxlandev
, vr
)
954 cmd_args
+= ' group %s' %(g)
955 self
.__execute
_or
_batch
(utils
.bridge_cmd
, cmd_args
)
957 def bridge_vni_add(self
, vxlan_device
, vni
):
958 # bridge vni add understands ranges:
959 # bridge vni add dev vx0 vni 10,11,20-30
960 self
.__execute
_or
_batch
(
962 "vni add dev %s vni %s" % (vxlan_device
, ','.join(vni
.split()))
965 def bridge_vni_int_set_del(self
, vxlan_device
, vni
):
966 # bridge vni del understands ranges:
967 # bridge vni del dev vx0 vni 10,11,20-30
968 self
.__execute
_or
_batch
(
970 "vni del dev %s vni %s" % (vxlan_device
, ','.join([str(x
) for x
in vni
]))
973 def bridge_vni_del_list(self
, vxlandev
, vnis
):
974 cmd_args
= "vni del dev %s vni %s" % (vxlandev
, ','.join(vnis
))
975 self
.__execute
_or
_batch
(utils
.bridge_cmd
, cmd_args
)
977 def compress_vnifilter_into_ranges(self
, vnis_ints
, vnisd
):
981 for v
, g
in vnisd
.items():
982 if v
not in vnis_ints
:
989 elif ((v
- vend
) == 1 and g
== lastg
):
994 range = '%d-%d' %(vbegin
, vend
)
995 vnisd_ranges
[range] = lastg
997 vnisd_ranges
['%s' %vbegin
] = lastg
1004 range = '%d-%d' %(vbegin
, vend
)
1005 vnisd_ranges
[range] = lastg
1007 vnisd_ranges
['%s' %vbegin
] = lastg
1010 def print_data(self
, lprefix
, data
):
1011 self
.logger
.info(lprefix
)
1012 self
.logger
.info(data
)
1014 def bridge_link_update_vni_filter(self
, vxlandev
, vnisd
):
1017 cmd
= 'bridge -j -p vni show dev %s' %( vxlandev
)
1018 output
= utils
.exec_command(cmd
)
1020 vnishow
= json
.loads(output
.strip("\n"))
1021 self
.logger
.debug(vnishow
)
1023 vlist
= s
.get('vnis')
1025 vstart
= v
.get('vni')
1026 vend
= v
.get('vniEnd')
1027 group
= v
.get('group')
1029 for tv
in range(int(vstart
), int(vend
)+1):
1036 rvnisd
[int(vstart
)] = group
1038 rvnisd
[int(vstart
)] = None
1039 vnis_int
= vnisd
.keys()
1040 rvnis_int
= rvnisd
.keys()
1042 (vnis_to_del
, vnis_to_add
) = utils
.diff_ids(vnis_int
,
1046 self
.bridge_vni_del_list(vxlandev
,
1047 utils
.compress_into_ranges(vnis_to_del
))
1049 self
.bridge_vni_update(vxlandev
,
1050 self
.compress_vnifilter_into_ranges(vnis_to_add
, vnisd
))
1052 # Do any vnis need group update ?
1053 # check remaining vnis
1054 vnis_rem
= set(vnis_int
)
1056 vnis_rem
= vnis_rem
.difference(set(vnis_to_add
))
1058 vnis_rem
= vnis_rem
.difference(set(vnis_to_del
))
1059 vnis_rem
= list(vnis_rem
)
1063 # check if group is not same
1064 if vnisd
.get(v
) != rvnisd
.get(v
):
1065 vnis_to_update
.append(v
)
1067 self
.bridge_vni_update(vxlandev
,
1068 self
.compress_vnifilter_into_ranges(vnis_to_update
, vnisd
))
1070 except Exception as e
:
1071 self
.logger
.error("bridge vni show failed .. %s" % str(e
))