3' href='#n93'>93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
# Copyright (c) 2021 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""VPP GENEVE Plugin utilities library."""

from ipaddress import ip_address

from resources.libraries.python.Constants import Constants
from resources.libraries.python.InterfaceUtil import InterfaceUtil
from resources.libraries.python.IPAddress import IPAddress
from resources.libraries.python.IPUtil import IPUtil
from resources.libraries.python.PapiExecutor import PapiSocketExecutor
from resources.libraries.python.topology import Topology


class GeneveUtil:
    """VPP GENEVE Plugin Keywords."""

    @staticmethod
    def add_geneve_tunnel(
            node, local_address, remote_address, vni, multicast_if=None,
            encap_vrf=0, l3_mode=False, next_index=None):
        """Add GENEVE tunnel on the specified VPP node.

        :param node: Topology node.
        :param local_address: Local IP address.
        :param remote_address: Remote IP address.
        :param vni: Virtual network ID.
        :param multicast_if: Interface key of multicast interface; used only if
            remote is multicast. (Default value = None)
        :param encap_vrf: The FIB ID for sending unicast GENEVE encap packets or
            receiving multicast packets. (Default value = 0)
        :param l3_mode: Use geneve tunnel in L3 mode (ip routing) if Tue else in
            L2 mode (L2 switching). (Default value = False)
        :param next_index: The index of the next node.
        :type node: dict
        :type local_address: str
        :type remote_address: str
        :type vni: int
        :type multicast_if: str
        :type encap_vrf: int
        :type l3_mode: bool
        :type next_index: int
        :returns: SW interface index of created geneve tunnel.
        :rtype: int
        """
        cmd = u"geneve_add_del_tunnel2"
        args = dict(
            is_add=True,
            local_address=IPAddress.create_ip_address_object(
                ip_address(local_address)
            ),
            remote_address=IPAddress.create_ip_address_object(
                ip_address(remote_address)
            ),
            mcast_sw_if_index=Topology.get_interface_sw_index(
                node, multicast_if
            ) if multicast_if else Constants.BITWISE_NON_ZERO,
            encap_vrf_id=int(encap_vrf),
            decap_next_index=next_index if l3_mode
            else Constants.BITWISE_NON_ZERO,
            vni=int(vni),
            l3_mode=l3_mode
        )
        err_msg = f"Failed to configure GENEVE tunnel on host {node[u'host']}!"
        with PapiSocketExecutor(node) as papi_exec:
            sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)

        if_key = Topology.add_new_port(node, u"geneve_tunnel")
        Topology.update_interface_sw_if_index(node, if_key, sw_if_index)

        ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
        Topology.update_interface_name(node, if_key, ifc_name)

        ifc_mac = InterfaceUtil.vpp_get_interface_mac(node, sw_if_index)
        Topology.update_interface_mac_address(node, if_key, ifc_mac)

        return sw_if_index

    @staticmethod
    def enable_interface_geneve_bypass(node, interface, is_ipv6=False):
        """Add ipv4/ipv6-geneve-bypass graph node for a given interface on
        the specified VPP node.

        :param node: Topology node.
        :param interface: Interface key from topology file of interface
            to add geneve bypass node for.
        :param is_ipv6: Enable ipv6-geneve-bypass graph node if True else enable
            ipv4-geneve-bypass graph node.
        :type node: dict
        :type interface: str
        :type is_ipv6: bool
        """
        cmd = u"sw_interface_set_geneve_bypass"
        args = dict(
            is_ipv6=is_ipv6,
            enable=True,
            sw_if_index=Topology.get_interface_sw_index(node, interface)
        )
        err_msg = (
            f"Failed to enable {u'ipv6' if is_ipv6 else u'ipv4'}-geneve-bypass "
            f"on interface {interface} on host {node[u'host']}!"
        )
        with PapiSocketExecutor(node) as papi_exec:
            papi_exec.add(cmd, **args).get_reply(err_msg)

    @staticmethod
    def show_geneve_tunnel_data(node):
        """Show the GENEVE tunnels data.

        :param node: DUT node.
        :type node: dict
        """
        cmds = [
            u"geneve_tunnel_dump",
        ]
        PapiSocketExecutor.dump_and_log(node, cmds)

    @staticmethod
    def vpp_geneve_add_multiple_tunnels(
            node, gen_tunnel, n_tunnels, dut_if1, dut_if2, tg_if1_ip4,
            tg_if2_ip4, tg_pf2_mac, next_idx):
        """Create multiple GENEVE tunnels.

        :param node: DUT node.
        :param gen_tunnel: Parameters of the GENEVE tunnel.
        :param n_tunnels: Number of tunnels.
        :param dut_if1: The first DUT interface.
        :param dut_if2: The second DUT interface.
        :param tg_if1_ip4: TG interface 1 IP address.
        :param tg_if2_ip4: TG interface 2 IP address.
        :param tg_pf2_mac: TG interface 2 MAC address.
        :param next_idx: The index of the next node.
        :type node: dict
        :type gen_tunnel: dict
        :type n_tunnels: int
        :type dut_if1: str
        :type dut_if2: str
        :type tg_if1_ip4: str
        :type tg_if2_ip4: str
        :type tg_pf2_mac: str
        :type next_idx: int
        """

        src_ip_int = IPUtil.ip_to_int(gen_tunnel[u"src_ip"])
        dst_ip_int = IPUtil.ip_to_int(gen_tunnel[u"dst_ip"])
        if_ip_int = IPUtil.ip_to_int(gen_tunnel[u"if_ip"])

        for idx in range(n_tunnels):
            src_ip = IPUtil.int_to_ip(src_ip_int + idx * 256)
            dst_ip = IPUtil.int_to_ip(dst_ip_int + idx * 256)
            if_ip = IPUtil.int_to_ip(if_ip_int + idx * 256)

            IPUtil.vpp_route_add(
                node, src_ip, gen_tunnel[u"ip_mask"],
                gateway=tg_if1_ip4, interface=dut_if1
            )
            tunnel_sw_index = GeneveUtil.add_geneve_tunnel(
                node, gen_tunnel[u"local"], gen_tunnel[u"remote"],
                gen_tunnel[u"vni"] + idx, l3_mode=True, next_index=next_idx
            )
            tunnel_if_key = Topology.get_interface_by_sw_index(
                node, tunnel_sw_index
            )
            tunnel_if_mac = Topology.get_interface_mac(
                node, tunnel_if_key
            )
            IPUtil.vpp_interface_set_ip_address(node, tunnel_if_key, if_ip, 24)
            IPUtil.vpp_add_ip_neighbor(
                node, tunnel_if_key, tg_if2_ip4, tg_pf2_mac
            )
            IPUtil.vpp_route_add(
                node, dst_ip, gen_tunnel[u"ip_mask"],
                gateway=tg_if2_ip4, interface=tunnel_if_key
            )
            IPUtil.vpp_route_add(
                node, gen_tunnel[u"remote"], 32,
                gateway=tg_if2_ip4, interface=dut_if2
            )
            IPUtil.vpp_add_ip_neighbor(
                node, tunnel_if_key, gen_tunnel[u"local"], tunnel_if_mac
            )
            IPUtil.vpp_route_add(
                node, gen_tunnel[u"local"], 32, gateway=if_ip
            )
            InterfaceUtil.set_interface_state(node, tunnel_if_key, u"up")