From bffa01fae02a929722cb2691a4629e06d7078228 Mon Sep 17 00:00:00 2001 From: Prince Sunny Date: Mon, 26 Nov 2018 11:52:08 -0800 Subject: [PATCH] VNET/VXLAN changes (#643) * Vnetvxlan changes * VS test case for Vnet/Vxlan * Addressed review comments, fixed a VS test case * Modifications based on feedback for VNET Table flow, VS test restructure to adopt to new changes * Schema updated for App DB --- doc/swss-schema.md | 29 ++ orchagent/orchdaemon.cpp | 3 + orchagent/vnetorch.cpp | 77 +++- orchagent/vnetorch.h | 55 ++- orchagent/vxlanorch.cpp | 441 ++++++++++++++++++++--- orchagent/vxlanorch.h | 135 ++++++- tests/test_vnet.py | 704 +++++++++++++++++++++++++++++++++++++ tests/test_vxlan_tunnel.py | 104 +++--- 8 files changed, 1410 insertions(+), 138 deletions(-) create mode 100644 tests/test_vnet.py diff --git a/doc/swss-schema.md b/doc/swss-schema.md index 085fd66abde0..03f0ec3c9f36 100644 --- a/doc/swss-schema.md +++ b/doc/swss-schema.md @@ -630,6 +630,35 @@ Equivalent RedisDB entry: 12) "0" 127.0.0.1:6379> +### VXLAN\_TUNNEL\_MAP + ;Stores vxlan tunnel map configuration. Defines mapping between vxlan vni and vrf + + key = VXLAN_TUNNEL_MAP:tunnel_name:tunnel_map_name + ; tunnel_name is a reference to created vxlan tunnel + ; tunnel_map_name is an arbitrary name of the map + vni = 1*8DIGIT ; vni id, defined for tunnel map + vrf = vrf_name ; name of the vrf + +### VNET\_ROUTE\_TUNNEL_TABLE + ;Defines schema for VNet Route tunnel table attributes + + key = VNET_ROUTE_TUNNEL_TABLE:vnet_name:prefix + ; Vnet route tunnel table with prefix + ; field value + endpoint = IP ; Host VM IP address + mac_address = 12HEXDIG ; Inner dest mac in encapsulated packet (Optional) + vxlanid = 1*8DIGIT ; VNI value in encapsulated packet (Optional) + + ;value annotations + vnet_name = 1*16VCHAR + +### VNET\_ROUTE_TABLE + ;Defines schema for VNet Route table attributes + key = VNET_ROUTE_TABLE:vnet_name:prefix + ; Vnet route table with prefix + ;field value + nexthop = IP ; Nexthop IP address (Optional) + ifname = ifname ; Interface name ## Configuration DB schema diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 856bb03e6689..50c51104a977 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -94,6 +94,8 @@ bool OrchDaemon::init() gDirectory.set(vxlan_tunnel_orch); VxlanTunnelMapOrch *vxlan_tunnel_map_orch = new VxlanTunnelMapOrch(m_configDb, CFG_VXLAN_TUNNEL_MAP_TABLE_NAME); gDirectory.set(vxlan_tunnel_map_orch); + VxlanVrfMapOrch *vxlan_vrf_orch = new VxlanVrfMapOrch(m_applDb, APP_VXLAN_VRF_TABLE_NAME); + gDirectory.set(vxlan_vrf_orch); vector qos_tables = { CFG_TC_TO_QUEUE_MAP_TABLE_NAME, @@ -193,6 +195,7 @@ bool OrchDaemon::init() m_orchList.push_back(vrf_orch); m_orchList.push_back(vxlan_tunnel_orch); m_orchList.push_back(vxlan_tunnel_map_orch); + m_orchList.push_back(vxlan_vrf_orch); m_select = new Select(); diff --git a/orchagent/vnetorch.cpp b/orchagent/vnetorch.cpp index 6988aeedbb35..955eb243034b 100644 --- a/orchagent/vnetorch.cpp +++ b/orchagent/vnetorch.cpp @@ -11,11 +11,14 @@ #include "portsorch.h" #include "request_parser.h" #include "vnetorch.h" +#include "vxlanorch.h" +#include "directory.h" #include "swssnet.h" extern sai_virtual_router_api_t* sai_virtual_router_api; extern sai_route_api_t* sai_route_api; extern sai_object_id_t gSwitchId; +extern Directory gDirectory; extern PortsOrch *gPortsOrch; /* @@ -23,10 +26,10 @@ extern PortsOrch *gPortsOrch; */ std::vector vr_cntxt; -VNetVrfObject::VNetVrfObject(const std::string& name, set& p_list, vector& attrs) - : VNetObject(p_list) +VNetVrfObject::VNetVrfObject(const std::string& vnet, string& tunnel, set& peer, + vector& attrs) : VNetObject(tunnel, peer) { - vnet_name_ = name; + vnet_name_ = vnet; createObj(attrs); } @@ -138,10 +141,10 @@ VNetVrfObject::~VNetVrfObject() */ template -std::unique_ptr VNetOrch::createObject(const string& vnet_name, set& plist, +std::unique_ptr VNetOrch::createObject(const string& vnet_name, string& tunnel, set& plist, vector& attrs) { - std::unique_ptr vnet_obj(new T(vnet_name, plist, attrs)); + std::unique_ptr vnet_obj(new T(vnet_name, tunnel, plist, attrs)); return vnet_obj; } @@ -168,6 +171,8 @@ bool VNetOrch::addOperation(const Request& request) vector attrs; set peer_list = {}; bool peer = false, create = false; + uint32_t vni=0; + string tunnel; for (const auto& name: request.getAttrFieldNames()) { @@ -183,6 +188,14 @@ bool VNetOrch::addOperation(const Request& request) peer_list = request.getAttrSet("peer_list"); peer = true; } + else if (name == "vni") + { + vni = static_cast(request.getAttrUint("vni")); + } + else if (name == "vxlan_tunnel") + { + tunnel = request.getAttrString("vxlan_tunnel"); + } else { SWSS_LOG_WARN("Logic error: Unknown attribute: %s", name.c_str()); @@ -199,11 +212,27 @@ bool VNetOrch::addOperation(const Request& request) auto it = vnet_table_.find(vnet_name); if (isVnetExecVrf()) { + VxlanTunnelOrch* vxlan_orch = gDirectory.get(); + + if (!vxlan_orch->isTunnelExists(tunnel)) + { + SWSS_LOG_WARN("Vxlan tunnel '%s' doesn't exist", tunnel.c_str()); + return false; + } + if (it == std::end(vnet_table_)) { - obj = createObject(vnet_name, peer_list, attrs); + obj = createObject(vnet_name, tunnel, peer_list, attrs); create = true; } + + if (!vxlan_orch->createVxlanTunnelMap(tunnel, TUNNEL_MAP_T_VIRTUAL_ROUTER, vni, + obj->getEncapMapId(), obj->getDecapMapId())) + { + SWSS_LOG_ERROR("VNET '%s', tunnel '%s', map create failed", + vnet_name.c_str(), tunnel.c_str()); + } + SWSS_LOG_INFO("VNET '%s' was added ", vnet_name.c_str()); } else @@ -291,29 +320,34 @@ VNetRouteOrch::VNetRouteOrch(DBConnector *db, vector &tableNames, VNetOr handler_map_.insert(handler_pair(APP_VNET_RT_TUNNEL_TABLE_NAME, &VNetRouteOrch::handleTunnel)); } -sai_object_id_t VNetRouteOrch::getNextHop(const string& vnet, IpAddress& ipAddr) +sai_object_id_t VNetRouteOrch::getNextHop(const string& vnet, tunnelEndpoint& endp) { auto it = nh_tunnels_.find(vnet); if (it != nh_tunnels_.end()) { - if (it->second.find(ipAddr) != it->second.end()) + if (it->second.find(endp.ip) != it->second.end()) { - return it->second.at(ipAddr); + return it->second.at(endp.ip); } } sai_object_id_t nh_id = SAI_NULL_OBJECT_ID; + auto tun_name = vnet_orch_->getTunnelName(vnet); - /* - * @FIXEME createNextHopTunnel(vnet, ipAddr, nh_id) , throw if failed - */ + VxlanTunnelOrch* vxlan_orch = gDirectory.get(); - nh_tunnels_[vnet].insert({ipAddr, nh_id}); + nh_id = vxlan_orch->createNextHopTunnel(tun_name, endp.ip, endp.mac, endp.vni); + if (nh_id == SAI_NULL_OBJECT_ID) + { + throw std::runtime_error("NH Tunnel create failed for " + vnet + " ip " + endp.ip.to_string()); + } + + nh_tunnels_[vnet].insert({endp.ip, nh_id}); return nh_id; } template<> -bool VNetRouteOrch::doRouteTask(const string& vnet, IpPrefix& ipPrefix, IpAddress& endIp) +bool VNetRouteOrch::doRouteTask(const string& vnet, IpPrefix& ipPrefix, tunnelEndpoint& endp) { SWSS_LOG_ENTER(); @@ -345,7 +379,7 @@ bool VNetRouteOrch::doRouteTask(const string& vnet, IpPrefix& ipP sai_ip_prefix_t pfx; copy(pfx, ipPrefix); - sai_object_id_t nh_id = getNextHop(vnet, endIp); + sai_object_id_t nh_id = getNextHop(vnet, endp); for (auto vr_id : vr_set) { @@ -450,6 +484,8 @@ void VNetRouteOrch::handleTunnel(const Request& request) SWSS_LOG_ENTER(); IpAddress ip; + MacAddress mac; + uint32_t vni = 0; for (const auto& name: request.getAttrFieldNames()) { @@ -457,6 +493,14 @@ void VNetRouteOrch::handleTunnel(const Request& request) { ip = request.getAttrIP(name); } + else if (name == "vni") + { + vni = static_cast(request.getAttrUint(name)); + } + else if (name == "mac_address") + { + mac = request.getAttrMacAddress(name); + } else { SWSS_LOG_WARN("Logic error: Unknown attribute: %s", name.c_str()); @@ -469,9 +513,10 @@ void VNetRouteOrch::handleTunnel(const Request& request) SWSS_LOG_INFO("VNET-RT '%s' add for endpoint %s", vnet_name.c_str(), ip_pfx.to_string().c_str()); + tunnelEndpoint endp = { ip, mac, vni }; if (vnet_orch_->isVnetExecVrf()) { - if (!doRouteTask(vnet_name, ip_pfx, ip)) + if (!doRouteTask(vnet_name, ip_pfx, endp)) { throw std::runtime_error("Route add failed"); } diff --git a/orchagent/vnetorch.h b/orchagent/vnetorch.h index f3911d0b79cf..b6939d37704a 100644 --- a/orchagent/vnetorch.h +++ b/orchagent/vnetorch.h @@ -14,10 +14,11 @@ const request_description_t vnet_request_description = { { REQ_T_STRING }, { { "src_mac", REQ_T_MAC_ADDRESS }, - { "vnet_name", REQ_T_STRING }, + { "vxlan_tunnel", REQ_T_STRING }, + { "vni", REQ_T_UINT }, { "peer_list", REQ_T_SET }, }, - { } // no mandatory attributes + { "vxlan_tunnel", "vni" } // mandatory attributes }; enum class VNET_EXEC @@ -34,7 +35,7 @@ enum class VR_TYPE VR_INVALID }; -using vrid_list_t = map; +typedef map vrid_list_t; extern std::vector vr_cntxt; class VNetRequest : public Request @@ -46,10 +47,7 @@ class VNetRequest : public Request class VNetObject { public: - VNetObject(set& p_list) - { - peer_list_ = p_list; - } + VNetObject(string& tunName, set& peer) : tunnel_(tunName), peer_list_(peer) { } virtual sai_object_id_t getEncapMapId() const = 0; @@ -69,16 +67,22 @@ class VNetObject return peer_list_; } + string getTunnelName() const + { + return tunnel_; + } + virtual ~VNetObject() {}; private: set peer_list_ = {}; + string tunnel_; }; class VNetVrfObject : public VNetObject { public: - VNetVrfObject(const std::string& name, set& p_list, vector& attrs); + VNetVrfObject(const string& vnet, string& tunnel, set& peer, vector& attrs); sai_object_id_t getVRidIngress() const; @@ -112,7 +116,7 @@ class VNetVrfObject : public VNetObject vrid_list_t vr_ids_; }; -using VNetObject_T = std::unique_ptr; +typedef std::unique_ptr VNetObject_T; typedef std::unordered_map VNetTable; class VNetOrch : public Orch2 @@ -151,6 +155,11 @@ class VNetOrch : public Orch2 return vnet_table_.at(name)->getVRid(); } + string getTunnelName(const std::string& name) const + { + return vnet_table_.at(name)->getTunnelName(); + } + bool isVnetExecVrf() const { return (vnet_exec_ == VNET_EXEC::VNET_EXEC_VRF); @@ -166,7 +175,7 @@ class VNetOrch : public Orch2 virtual bool delOperation(const Request& request); template - std::unique_ptr createObject(const string&, set&, vector&); + std::unique_ptr createObject(const string&, string&, set&, vector&); VNetTable vnet_table_; VNetRequest request_; @@ -177,8 +186,10 @@ class VNetOrch : public Orch2 const request_description_t vnet_route_description = { { REQ_T_STRING, REQ_T_IP_PREFIX }, { - { "endpoint", REQ_T_IP }, - { "ifname", REQ_T_STRING }, + { "endpoint", REQ_T_IP }, + { "ifname", REQ_T_STRING }, + { "vni", REQ_T_UINT }, + { "mac_address", REQ_T_MAC_ADDRESS }, }, { } }; @@ -189,15 +200,23 @@ class VNetRouteRequest : public Request VNetRouteRequest() : Request(vnet_route_description, ':') { } }; -using NextHopMap = map; -using NextHopTunnels = map; +typedef map NextHopMap; +typedef map NextHopTunnels; + +struct tunnelEndpoint +{ + IpAddress ip; + MacAddress mac; + uint32_t vni; +}; class VNetRouteOrch : public Orch2 { public: VNetRouteOrch(DBConnector *db, vector &tableNames, VNetOrch *); - using handler_pair = pair; - using handler_map = map; + + typedef pair handler_pair; + typedef map handler_map; private: virtual bool addOperation(const Request& request); @@ -207,12 +226,12 @@ class VNetRouteOrch : public Orch2 void handleTunnel(const Request&); template - bool doRouteTask(const string& vnet, IpPrefix& ipPrefix, IpAddress& ipAddr); + bool doRouteTask(const string& vnet, IpPrefix& ipPrefix, tunnelEndpoint& endp); template bool doRouteTask(const string& vnet, IpPrefix& ipPrefix, string& ifname); - sai_object_id_t getNextHop(const string& vnet, IpAddress& ipAddr); + sai_object_id_t getNextHop(const string& vnet, tunnelEndpoint& endp); VNetOrch *vnet_orch_; VNetRouteRequest request_; diff --git a/orchagent/vxlanorch.cpp b/orchagent/vxlanorch.cpp index 8a08645f65aa..997920db6fb7 100644 --- a/orchagent/vxlanorch.cpp +++ b/orchagent/vxlanorch.cpp @@ -13,23 +13,82 @@ #include "request_parser.h" #include "vxlanorch.h" #include "directory.h" +#include "swssnet.h" /* Global variables */ extern sai_object_id_t gSwitchId; extern sai_object_id_t gVirtualRouterId; extern sai_tunnel_api_t *sai_tunnel_api; +extern sai_next_hop_api_t *sai_next_hop_api; extern Directory gDirectory; extern PortsOrch* gPortsOrch; extern sai_object_id_t gUnderlayIfId; +const map vxlanTunnelMap = +{ + { MAP_T::VNI_TO_VLAN_ID, SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID }, + { MAP_T::VLAN_ID_TO_VNI, SAI_TUNNEL_MAP_TYPE_VLAN_ID_TO_VNI }, + { MAP_T::VRID_TO_VNI, SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI }, + { MAP_T::VNI_TO_VRID, SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID }, + { MAP_T::BRIDGE_TO_VNI, SAI_TUNNEL_MAP_TYPE_BRIDGE_IF_TO_VNI }, + { MAP_T::VNI_TO_BRIDGE, SAI_TUNNEL_MAP_TYPE_VNI_TO_BRIDGE_IF}, +}; + +const map> vxlanTunnelMapKeyVal = +{ + { MAP_T::VNI_TO_VLAN_ID, + { SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY, SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE } + }, + { MAP_T::VLAN_ID_TO_VNI, + { SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_KEY, SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_VALUE } + }, + { MAP_T::VRID_TO_VNI, + { SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_KEY, SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_VALUE } + }, + { MAP_T::VNI_TO_VRID, + { SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY, SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_VALUE } + }, + { MAP_T::BRIDGE_TO_VNI, + { SAI_TUNNEL_MAP_ENTRY_ATTR_BRIDGE_ID_KEY, SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_VALUE } + }, + { MAP_T::VNI_TO_BRIDGE, + { SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY, SAI_TUNNEL_MAP_ENTRY_ATTR_BRIDGE_ID_VALUE } + }, +}; + +/* + * Manipulators for the above Map + */ +static inline uint32_t tunnel_map_type (MAP_T map_t) +{ + return vxlanTunnelMap.at(map_t); +} + +static inline uint32_t tunnel_map_key (MAP_T map_t) +{ + return vxlanTunnelMapKeyVal.at(map_t).first; +} + +static inline uint32_t tunnel_map_val (MAP_T map_t) +{ + return vxlanTunnelMapKeyVal.at(map_t).second; +} + static sai_object_id_t -create_tunnel_map() +create_tunnel_map(MAP_T map_t) { sai_attribute_t attr; std::vector tunnel_map_attrs; + if (map_t == MAP_T::MAP_TO_INVALID) + { + SWSS_LOG_ERROR("Invalid map type %d", map_t); + return SAI_NULL_OBJECT_ID; + } + attr.id = SAI_TUNNEL_MAP_ATTR_TYPE; - attr.value.s32 = SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID; + attr.value.s32 = tunnel_map_type(map_t); + tunnel_map_attrs.push_back(attr); sai_object_id_t tunnel_map_id; @@ -47,38 +106,47 @@ create_tunnel_map() return tunnel_map_id; } -static sai_object_id_t -create_tunnel_map_entry( +static sai_object_id_t create_tunnel_map_entry( + MAP_T map_t, sai_object_id_t tunnel_map_id, sai_uint32_t vni, - sai_uint16_t vlan_id) + sai_uint16_t vlan_id, + sai_object_id_t obj_id=SAI_NULL_OBJECT_ID, + bool encap=false + ) { sai_attribute_t attr; + sai_object_id_t tunnel_map_entry_id; std::vector tunnel_map_entry_attrs; attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE; - attr.value.s32 = SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID; + attr.value.s32 = tunnel_map_type(map_t); tunnel_map_entry_attrs.push_back(attr); attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP; attr.value.oid = tunnel_map_id; tunnel_map_entry_attrs.push_back(attr); - attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY; - attr.value.u32 = vni; + attr.id = (encap)? tunnel_map_key(map_t):tunnel_map_val(map_t); + if (obj_id != SAI_NULL_OBJECT_ID) + { + attr.value.oid = obj_id; + } + else + { + attr.value.u16 = vlan_id; + } + tunnel_map_entry_attrs.push_back(attr); - attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE; - attr.value.u16 = vlan_id; + attr.id = (encap)? tunnel_map_val(map_t):tunnel_map_key(map_t); + attr.value.u32 = vni; tunnel_map_entry_attrs.push_back(attr); - sai_object_id_t tunnel_map_entry_id; - sai_status_t status = sai_tunnel_api->create_tunnel_map_entry( - &tunnel_map_entry_id, - gSwitchId, - static_cast(tunnel_map_entry_attrs.size()), - tunnel_map_entry_attrs.data() - ); + sai_status_t status = sai_tunnel_api->create_tunnel_map_entry(&tunnel_map_entry_id, gSwitchId, + static_cast (tunnel_map_entry_attrs.size()), + tunnel_map_entry_attrs.data()); + if (status != SAI_STATUS_SUCCESS) { throw std::runtime_error("Can't create a tunnel map entry object"); @@ -87,9 +155,55 @@ create_tunnel_map_entry( return tunnel_map_entry_id; } +static sai_status_t create_nexthop_tunnel( + sai_ip_address_t host_ip, + sai_uint32_t vni, // optional vni + sai_mac_t *mac, // inner destination mac + sai_object_id_t tunnel_id, + sai_object_id_t *next_hop_id) +{ + std::vector next_hop_attrs; + sai_attribute_t next_hop_attr; + + next_hop_attr.id = SAI_NEXT_HOP_ATTR_TYPE; + next_hop_attr.value.s32 = SAI_NEXT_HOP_TYPE_TUNNEL_ENCAP; + next_hop_attrs.push_back(next_hop_attr); + + next_hop_attr.id = SAI_NEXT_HOP_ATTR_IP; + next_hop_attr.value.ipaddr = host_ip; + next_hop_attrs.push_back(next_hop_attr); + + next_hop_attr.id = SAI_NEXT_HOP_ATTR_TUNNEL_ID; + next_hop_attr.value.oid = tunnel_id; + next_hop_attrs.push_back(next_hop_attr); + + if (vni != 0) + { + next_hop_attr.id = SAI_NEXT_HOP_ATTR_TUNNEL_VNI; + next_hop_attr.value.u32 = vni; + next_hop_attrs.push_back(next_hop_attr); + } + + if (mac != nullptr) + { + next_hop_attr.id = SAI_NEXT_HOP_ATTR_TUNNEL_MAC; + memcpy(next_hop_attr.value.mac, mac, sizeof(sai_mac_t)); + next_hop_attrs.push_back(next_hop_attr); + } + + sai_status_t status = sai_next_hop_api->create_next_hop(next_hop_id, gSwitchId, + static_cast(next_hop_attrs.size()), + next_hop_attrs.data()); + return status; +} + // Create Tunnel static sai_object_id_t -create_tunnel(sai_object_id_t tunnel_map_id) +create_tunnel( + sai_object_id_t tunnel_encap_id, + sai_object_id_t tunnel_decap_id, + sai_ip_address_t *src_ip, + sai_object_id_t underlay_rif) { sai_attribute_t attr; std::vector tunnel_attrs; @@ -99,15 +213,32 @@ create_tunnel(sai_object_id_t tunnel_map_id) tunnel_attrs.push_back(attr); attr.id = SAI_TUNNEL_ATTR_UNDERLAY_INTERFACE; - attr.value.oid = gUnderlayIfId; + attr.value.oid = underlay_rif; tunnel_attrs.push_back(attr); - sai_object_id_t decap_list[] = { tunnel_map_id }; + sai_object_id_t decap_list[] = { tunnel_decap_id }; attr.id = SAI_TUNNEL_ATTR_DECAP_MAPPERS; attr.value.objlist.count = 1; attr.value.objlist.list = decap_list; tunnel_attrs.push_back(attr); + if (tunnel_encap_id != SAI_NULL_OBJECT_ID) + { + sai_object_id_t encap_list[] = { tunnel_encap_id }; + attr.id = SAI_TUNNEL_ATTR_ENCAP_MAPPERS; + attr.value.objlist.count = 1; + attr.value.objlist.list = encap_list; + tunnel_attrs.push_back(attr); + } + + // source ip + if (src_ip != nullptr) + { + attr.id = SAI_TUNNEL_ATTR_ENCAP_SRC_IP; + attr.value.ipaddr = *src_ip; + tunnel_attrs.push_back(attr); + } + sai_object_id_t tunnel_id; sai_status_t status = sai_tunnel_api->create_tunnel( &tunnel_id, @@ -124,18 +255,17 @@ create_tunnel(sai_object_id_t tunnel_map_id) } // Create tunnel termination - static sai_object_id_t create_tunnel_termination( sai_object_id_t tunnel_oid, - sai_ip4_t srcip, - sai_ip4_t dstip, + sai_ip_address_t srcip, + sai_ip_address_t *dstip, sai_object_id_t default_vrid) { sai_attribute_t attr; std::vector tunnel_attrs; - if(dstip == 0x0) // It's P2MP tunnel + if(dstip == nullptr) // It's P2MP tunnel { attr.id = SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE; attr.value.s32 = SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP; @@ -148,8 +278,7 @@ create_tunnel_termination( tunnel_attrs.push_back(attr); attr.id = SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_SRC_IP; - attr.value.ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV4; - attr.value.ipaddr.addr.ip4 = dstip; + attr.value.ipaddr = *dstip; tunnel_attrs.push_back(attr); } @@ -158,8 +287,7 @@ create_tunnel_termination( tunnel_attrs.push_back(attr); attr.id = SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_DST_IP; - attr.value.ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV4; - attr.value.ipaddr.addr.ip4 = srcip; + attr.value.ipaddr = srcip; tunnel_attrs.push_back(attr); attr.id = SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TUNNEL_TYPE; @@ -185,6 +313,143 @@ create_tunnel_termination( return term_table_id; } +bool VxlanTunnel::createTunnel(MAP_T encap, MAP_T decap) +{ + try + { + sai_ip_address_t ips, ipd, *ip=nullptr; + swss::copy(ips, src_ip_); + + ids_.tunnel_decap_id = SAI_NULL_OBJECT_ID; + ids_.tunnel_encap_id = SAI_NULL_OBJECT_ID; + + if (decap != MAP_T::MAP_TO_INVALID) + { + ids_.tunnel_decap_id = create_tunnel_map(decap); + } + if (encap != MAP_T::MAP_TO_INVALID) + { + ids_.tunnel_encap_id = create_tunnel_map(encap); + ip = &ips; + } + + ids_.tunnel_id = create_tunnel(ids_.tunnel_encap_id, ids_.tunnel_decap_id, ip, gUnderlayIfId); + + ip = nullptr; + if (!dst_ip_.isZero()) + { + swss::copy(ipd, dst_ip_); + ip = &ipd; + } + + ids_.tunnel_term_id = create_tunnel_termination(ids_.tunnel_id, ips, ip, gVirtualRouterId); + active_ = true; + tunnel_map_ = { encap, decap }; + } + catch (const std::runtime_error& error) + { + SWSS_LOG_ERROR("Error creating tunnel %s: %s", tunnel_name_.c_str(), error.what()); + // FIXME: add code to remove already created objects + return false; + } + + SWSS_LOG_INFO("Vxlan tunnel '%s' was created", tunnel_name_.c_str()); + return true; +} + +sai_object_id_t VxlanTunnel::addEncapMapperEntry(sai_object_id_t obj, uint32_t vni) +{ + const auto encap_id = getEncapMapId(); + const auto map_t = tunnel_map_.first; + return create_tunnel_map_entry(map_t, encap_id, vni, 0, obj, true); +} + +sai_object_id_t VxlanTunnel::addDecapMapperEntry(sai_object_id_t obj, uint32_t vni) +{ + const auto decap_id = getDecapMapId(); + const auto map_t = tunnel_map_.second; + return create_tunnel_map_entry(map_t, decap_id, vni, 0, obj); +} + +sai_object_id_t +VxlanTunnelOrch::createNextHopTunnel(string tunnelName, IpAddress& ipAddr, MacAddress macAddress, uint32_t vni) +{ + if(!isTunnelExists(tunnelName)) + { + SWSS_LOG_ERROR("Vxlan tunnel '%s' does not exists", tunnelName.c_str()); + return SAI_NULL_OBJECT_ID; + } + + sai_ip_address_t host_ip; + swss::copy(host_ip, ipAddr); + + sai_mac_t mac, *macptr = nullptr; + if (macAddress) + { + memcpy(mac, macAddress.getMac(), ETHER_ADDR_LEN); + macptr = &mac; + } + + auto tunnel_obj = getVxlanTunnel(tunnelName); + + sai_object_id_t nh_id, tunnel_id = tunnel_obj->getTunnelId(); + + if (create_nexthop_tunnel(host_ip, vni, macptr, tunnel_id, &nh_id) != SAI_STATUS_SUCCESS) + { + string err_msg = "NH tunnel create failed for " + ipAddr.to_string() + " " + to_string(vni); + throw std::runtime_error(err_msg); + } + + SWSS_LOG_INFO("NH vxlan tunnel was created for %s, id 0x%lx", tunnelName.c_str(), nh_id); + return nh_id; +} + +bool VxlanTunnelOrch::createVxlanTunnelMap(string tunnelName, tunnel_map_type_t map, uint32_t vni, + sai_object_id_t encap, sai_object_id_t decap) +{ + SWSS_LOG_ENTER(); + + if(!isTunnelExists(tunnelName)) + { + SWSS_LOG_ERROR("Vxlan tunnel '%s' does not exists", tunnelName.c_str()); + return false; + } + + auto tunnel_obj = getVxlanTunnel(tunnelName); + + if (!tunnel_obj->isActive()) + { + if (map == TUNNEL_MAP_T_VIRTUAL_ROUTER) + { + tunnel_obj->createTunnel(MAP_T::VRID_TO_VNI, MAP_T::VNI_TO_VRID); + } + else if (map == TUNNEL_MAP_T_BRIDGE) + { + tunnel_obj->createTunnel(MAP_T::BRIDGE_TO_VNI, MAP_T::VNI_TO_BRIDGE); + } + } + + try + { + /* + * Create encap and decap mapper + */ + auto encap_id = tunnel_obj->addEncapMapperEntry(encap, vni); + auto decap_id = tunnel_obj->addDecapMapperEntry(decap, vni); + + SWSS_LOG_DEBUG("Vxlan tunnel encap entry '%lx' decap entry '0x%lx'", encap_id, decap_id); + } + catch(const std::runtime_error& error) + { + SWSS_LOG_ERROR("Error adding tunnel map entry. Tunnel: %s. Error: %s", + tunnelName.c_str(), error.what()); + return false; + } + + SWSS_LOG_NOTICE("Vxlan map for tunnel '%s' was created", tunnelName.c_str()); + return true; +} + bool VxlanTunnelOrch::addOperation(const Request& request) { SWSS_LOG_ENTER(); @@ -220,24 +485,9 @@ bool VxlanTunnelOrch::addOperation(const Request& request) return true; } - tunnel_ids_t ids; - try - { - ids.tunnel_map_id = create_tunnel_map(); - ids.tunnel_id = create_tunnel(ids.tunnel_map_id); - ids.tunnel_term_id = create_tunnel_termination(ids.tunnel_id, src_ip.getV4Addr(), dst_ip.getV4Addr(), gVirtualRouterId); - } - catch (const std::runtime_error& error) - { - SWSS_LOG_ERROR("Error creating tunnel %s: %s", tunnel_name.c_str(), error.what()); - // FIXME: add code to remove already created objects - return false; - } - - vxlan_tunnel_table_[tunnel_name] = ids; - - SWSS_LOG_NOTICE("Vxlan tunnel '%s' was created", tunnel_name.c_str()); + vxlan_tunnel_table_[tunnel_name] = std::unique_ptr(new VxlanTunnel(tunnel_name, src_ip, dst_ip)); + SWSS_LOG_INFO("Vxlan tunnel '%s' was added", tunnel_name.c_str()); return true; } @@ -258,7 +508,7 @@ bool VxlanTunnelMapOrch::addOperation(const Request& request) Port tempPort; if(!gPortsOrch->getVlanByVlanId(vlan_id, tempPort)) { - SWSS_LOG_ERROR("Vxlan tunnel map vlan id doesn't exist: %d", vlan_id); + SWSS_LOG_WARN("Vxlan tunnel map vlan id doesn't exist: %d", vlan_id); return false; } @@ -273,23 +523,31 @@ bool VxlanTunnelMapOrch::addOperation(const Request& request) VxlanTunnelOrch* tunnel_orch = gDirectory.get(); if (!tunnel_orch->isTunnelExists(tunnel_name)) { - SWSS_LOG_ERROR("Vxlan tunnel '%s' doesn't exist", tunnel_name.c_str()); + SWSS_LOG_WARN("Vxlan tunnel '%s' doesn't exist", tunnel_name.c_str()); return false; } + auto tunnel_obj = tunnel_orch->getVxlanTunnel(tunnel_name); + if (!tunnel_obj->isActive()) + { + //@Todo, currently only decap mapper is allowed + tunnel_obj->createTunnel(MAP_T::MAP_TO_INVALID, MAP_T::VNI_TO_VLAN_ID); + } + const auto full_tunnel_map_entry_name = request.getFullKey(); if (isTunnelMapExists(full_tunnel_map_entry_name)) { - SWSS_LOG_ERROR("Vxlan tunnel map '%s' is already exist", full_tunnel_map_entry_name.c_str()); + SWSS_LOG_NOTICE("Vxlan tunnel map '%s' already exist", full_tunnel_map_entry_name.c_str()); return true; } - const auto tunnel_map_id = tunnel_orch->getTunnelMapId(tunnel_name); + const auto tunnel_map_id = tunnel_obj->getDecapMapId(); const auto tunnel_map_entry_name = request.getKeyString(1); try { - auto tunnel_map_entry_id = create_tunnel_map_entry(tunnel_map_id, vni_id, vlan_id); + auto tunnel_map_entry_id = create_tunnel_map_entry(MAP_T::VNI_TO_VLAN_ID, + tunnel_map_id, vni_id, vlan_id); vxlan_tunnel_map_table_[full_tunnel_map_entry_name] = tunnel_map_entry_id; } catch(const std::runtime_error& error) @@ -299,7 +557,8 @@ bool VxlanTunnelMapOrch::addOperation(const Request& request) return false; } - SWSS_LOG_NOTICE("Vxlan tunnel map entry '%s' for tunnel '%s' was created", tunnel_map_entry_name.c_str(), tunnel_name.c_str()); + SWSS_LOG_NOTICE("Vxlan tunnel map entry '%s' for tunnel '%s' was created", + tunnel_map_entry_name.c_str(), tunnel_name.c_str()); return true; } @@ -312,3 +571,83 @@ bool VxlanTunnelMapOrch::delOperation(const Request& request) return true; } + +bool VxlanVrfMapOrch::addOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + auto tunnel_name = request.getKeyString(0); + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + if (!tunnel_orch->isTunnelExists(tunnel_name)) + { + SWSS_LOG_WARN("Vxlan tunnel '%s' doesn't exist", tunnel_name.c_str()); + return false; + } + + auto vni_id = static_cast(request.getAttrUint("vni")); + if (vni_id >= 1<<24) + { + SWSS_LOG_ERROR("Vxlan vni id is too big: %d", vni_id); + return true; + } + + const auto full_map_entry_name = request.getFullKey(); + if (isVrfMapExists(full_map_entry_name)) + { + SWSS_LOG_ERROR("Vxlan map '%s' is already exist", full_map_entry_name.c_str()); + return true; + } + + auto tunnel_obj = tunnel_orch->getVxlanTunnel(tunnel_name); + sai_object_id_t vrf_id; + + string vrf_name = request.getAttrString("vrf"); + VRFOrch* vrf_orch = gDirectory.get(); + + if (vrf_orch->isVRFexists(vrf_name)) + { + tunnel_obj->createTunnel(MAP_T::VRID_TO_VNI, MAP_T::VNI_TO_VRID); + vrf_id = vrf_orch->getVRFid(vrf_name); + } + else + { + SWSS_LOG_WARN("Vrf '%s' hasn't been created yet", vrf_name.c_str()); + return false; + } + + const auto tunnel_map_entry_name = request.getKeyString(1); + vrf_map_entry_t entry; + try + { + /* + * Create encap and decap mapper + */ + entry.encap_id = tunnel_obj->addEncapMapperEntry(vrf_id, vni_id); + entry.decap_id = tunnel_obj->addDecapMapperEntry(vrf_id, vni_id); + + SWSS_LOG_DEBUG("Vxlan tunnel encap entry '%lx' decap entry '0x%lx'", + entry.encap_id, entry.decap_id); + + vxlan_vrf_table_[full_map_entry_name] = entry; + vxlan_vrf_tunnel_[vrf_name] = tunnel_obj->getTunnelId(); + } + catch(const std::runtime_error& error) + { + SWSS_LOG_ERROR("Error adding tunnel map entry. Tunnel: %s. Entry: %s. Error: %s", + tunnel_name.c_str(), tunnel_map_entry_name.c_str(), error.what()); + return false; + } + + SWSS_LOG_NOTICE("Vxlan vrf map entry '%s' for tunnel '%s' was created", + tunnel_map_entry_name.c_str(), tunnel_name.c_str()); + return true; +} + +bool VxlanVrfMapOrch::delOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_ERROR("DEL operation is not implemented"); + + return true; +} diff --git a/orchagent/vxlanorch.h b/orchagent/vxlanorch.h index 6eaac403f523..f5e3774e7dac 100644 --- a/orchagent/vxlanorch.h +++ b/orchagent/vxlanorch.h @@ -1,8 +1,71 @@ #pragma once #include +#include +#include #include "request_parser.h" #include "portsorch.h" +#include "vrforch.h" + +enum class MAP_T +{ + MAP_TO_INVALID, + VNI_TO_VLAN_ID, + VLAN_ID_TO_VNI, + VRID_TO_VNI, + VNI_TO_VRID, + BRIDGE_TO_VNI, + VNI_TO_BRIDGE +}; + +struct tunnel_ids_t +{ + sai_object_id_t tunnel_encap_id; + sai_object_id_t tunnel_decap_id; + sai_object_id_t tunnel_id; + sai_object_id_t tunnel_term_id; +}; + +class VxlanTunnel +{ +public: + VxlanTunnel(string name, IpAddress srcIp, IpAddress dstIp) + :tunnel_name_(name), src_ip_(srcIp), dst_ip_(dstIp) { } + + bool isActive() const + { + return active_; + } + + bool createTunnel(MAP_T encap, MAP_T decap); + sai_object_id_t addEncapMapperEntry(sai_object_id_t obj, uint32_t vni); + sai_object_id_t addDecapMapperEntry(sai_object_id_t obj, uint32_t vni); + + sai_object_id_t getTunnelId() const + { + return ids_.tunnel_id; + } + + sai_object_id_t getDecapMapId() const + { + return ids_.tunnel_decap_id; + } + + sai_object_id_t getEncapMapId() const + { + return ids_.tunnel_encap_id; + } + +private: + string tunnel_name_; + bool active_ = false; + + tunnel_ids_t ids_; + std::pair tunnel_map_ = { MAP_T::MAP_TO_INVALID, MAP_T::MAP_TO_INVALID }; + + IpAddress src_ip_; + IpAddress dst_ip_ = 0x0; +}; const request_description_t vxlan_tunnel_request_description = { { REQ_T_STRING }, @@ -19,29 +82,37 @@ class VxlanTunnelRequest : public Request VxlanTunnelRequest() : Request(vxlan_tunnel_request_description, '|') { } }; -struct tunnel_ids_t +typedef std::unique_ptr VxlanTunnel_T; +typedef std::map VxlanTunnelTable; + +typedef enum { - sai_object_id_t tunnel_map_id; - sai_object_id_t tunnel_id; - sai_object_id_t tunnel_term_id; -}; -typedef std::map VxlanTunnelTable; + TUNNEL_MAP_T_VLAN, + TUNNEL_MAP_T_BRIDGE, + TUNNEL_MAP_T_VIRTUAL_ROUTER, +} tunnel_map_type_t; class VxlanTunnelOrch : public Orch2 { public: VxlanTunnelOrch(DBConnector *db, const std::string& tableName) : Orch2(db, tableName, request_) { } - bool isTunnelExists(const std::string& tunnel_name) const + bool isTunnelExists(const std::string& tunnelName) const { - return vxlan_tunnel_table_.find(tunnel_name) != std::end(vxlan_tunnel_table_); + return vxlan_tunnel_table_.find(tunnelName) != std::end(vxlan_tunnel_table_); } - sai_object_id_t getTunnelMapId(const std::string& tunnel_name) const + VxlanTunnel* getVxlanTunnel(const std::string& tunnelName) { - return vxlan_tunnel_table_.at(tunnel_name).tunnel_map_id; + return vxlan_tunnel_table_.at(tunnelName).get(); } + bool createVxlanTunnelMap(string tunnelName, tunnel_map_type_t mapType, uint32_t vni, + sai_object_id_t encap, sai_object_id_t decap); + + sai_object_id_t + createNextHopTunnel(string tunnelName, IpAddress& ipAddr, MacAddress macAddress, uint32_t vni=0); + private: virtual bool addOperation(const Request& request); virtual bool delOperation(const Request& request); @@ -83,3 +154,47 @@ class VxlanTunnelMapOrch : public Orch2 VxlanTunnelMapTable vxlan_tunnel_map_table_; VxlanTunnelMapRequest request_; }; + +const request_description_t vxlan_vrf_request_description = { + { REQ_T_STRING, REQ_T_STRING }, + { + { "vni", REQ_T_UINT }, + { "vrf", REQ_T_STRING }, + }, + { "vni", "vrf" } +}; + +class VxlanVrfRequest : public Request +{ +public: + VxlanVrfRequest() : Request(vxlan_vrf_request_description, ':') { } +}; + +struct vrf_map_entry_t { + sai_object_id_t encap_id; + sai_object_id_t decap_id; +}; + +typedef std::map VxlanVrfTable; +typedef std::map VxlanVrfTunnel; + +class VxlanVrfMapOrch : public Orch2 +{ +public: + VxlanVrfMapOrch(DBConnector *db, const std::string& tableName) : Orch2(db, tableName, request_) { } + + typedef std::pair handler_pair; + + bool isVrfMapExists(const std::string& name) const + { + return vxlan_vrf_table_.find(name) != std::end(vxlan_vrf_table_); + } + +private: + virtual bool addOperation(const Request& request); + virtual bool delOperation(const Request& request); + + VxlanVrfTable vxlan_vrf_table_; + VxlanVrfTunnel vxlan_vrf_tunnel_; + VxlanVrfRequest request_; +}; diff --git a/tests/test_vnet.py b/tests/test_vnet.py new file mode 100644 index 000000000000..1a37a3700d2c --- /dev/null +++ b/tests/test_vnet.py @@ -0,0 +1,704 @@ +from swsscommon import swsscommon +import time +import json +import random +import time +from pprint import pprint + + +def create_entry(tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + time.sleep(1) + + +def create_entry_tbl(db, table, separator, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + + +def create_entry_pst(db, table, separator, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + create_entry(tbl, key, pairs) + + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + + +def entries(db, table): + tbl = swsscommon.Table(db, table) + return set(tbl.getKeys()) + + +def get_exist_entries(dvs, table): + db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + tbl = swsscommon.Table(db, table) + return set(tbl.getKeys()) + + +def get_created_entry(db, table, existed_entries): + tbl = swsscommon.Table(db, table) + entries = set(tbl.getKeys()) + new_entries = list(entries - existed_entries) + assert len(new_entries) == 1, "Wrong number of created entries." + return new_entries[0] + + +def get_created_entries(db, table, existed_entries, count): + tbl = swsscommon.Table(db, table) + entries = set(tbl.getKeys()) + new_entries = list(entries - existed_entries) + assert len(new_entries) == count, "Wrong number of created entries." + new_entries.sort() + return new_entries + + +def get_default_vr_id(dvs): + db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + table = 'ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER' + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + assert len(keys) == 1, "Wrong number of virtual routers found" + + return keys[0] + + +def check_object(db, table, key, expected_attributes): + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + assert key in keys, "The desired key is not presented" + + status, fvs = tbl.get(key) + assert status, "Got an error when get a key" + + assert len(fvs) >= len(expected_attributes), "Incorrect attributes" + + attr_keys = {entry[0] for entry in fvs} + + for name, value in fvs: + if name in expected_attributes: + assert expected_attributes[name] == value, "Wrong value %s for the attribute %s = %s" % \ + (value, name, expected_attributes[name]) + + +def create_vnet_local_routes(dvs, prefix, vnet_name, ifname): + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + create_entry_pst( + app_db, + "VNET_ROUTE_TABLE", ':', "%s:%s" % (vnet_name, prefix), + [ + ("ifname", ifname), + ] + ) + + time.sleep(2) + + +def create_vnet_routes(dvs, prefix, vnet_name, endpoint, mac="", vni=0): + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + attrs = [ + ("endpoint", endpoint), + ] + + if vni: + attrs.append(('vni', vni)) + + if mac: + attrs.append(('mac_address', mac)) + + create_entry_pst( + app_db, + "VNET_ROUTE_TUNNEL_TABLE", ':', "%s:%s" % (vnet_name, prefix), + attrs, + ) + + time.sleep(2) + + +def create_vlan(dvs, vlan_name, vlan_ids): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + vlan_id = vlan_name[4:] + + # create vlan + create_entry_tbl( + conf_db, + "VLAN", '|', vlan_name, + [ + ("vlanid", vlan_id), + ], + ) + + time.sleep(1) + + vlan_oid = get_created_entry(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN", vlan_ids) + + check_object(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN", vlan_oid, + { + "SAI_VLAN_ATTR_VLAN_ID": vlan_id, + } + ) + + return vlan_oid + + +def create_vlan_interface(dvs, vlan_name, ifname, vnet_name, ipaddr): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + vlan_ids = get_exist_entries(dvs, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + + vlan_oid = create_vlan (dvs, vlan_name, vlan_ids) + + # create a vlan member in config db + create_entry_tbl( + conf_db, + "VLAN_MEMBER", '|', "%s|%s" % (vlan_name, ifname), + [ + ("tagging_mode", "untagged"), + ], + ) + + time.sleep(1) + + # create vlan interface in config db + create_entry_tbl( + conf_db, + "VLAN_INTERFACE", '|', vlan_name, + [ + ("vnet_name", vnet_name), + ], + ) + + #FIXME - This is created by IntfMgr + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + create_entry_pst( + app_db, + "INTF_TABLE", ':', vlan_name, + [ + ("vnet_name", vnet_name), + ], + ) + time.sleep(2) + + create_entry_tbl( + conf_db, + "VLAN_INTERFACE", '|', "%s|%s" % (vlan_name, ipaddr), + [ + ("family", "IPv4"), + ], + ) + + time.sleep(2) + + return vlan_oid + + +def create_phy_interface(dvs, ifname, vnet_name, ipaddr): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + exist_rifs = get_exist_entries(dvs, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + + # create vlan interface in config db + create_entry_tbl( + conf_db, + "INTERFACE", '|', ifname, + [ + ("vnet_name", vnet_name), + ], + ) + + #FIXME - This is created by IntfMgr + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + create_entry_pst( + app_db, + "INTF_TABLE", ':', ifname, + [ + ("vnet_name", vnet_name), + ], + ) + time.sleep(2) + + create_entry_tbl( + conf_db, + "INTERFACE", '|', "%s|%s" % (ifname, ipaddr), + [ + ("family", "IPv4"), + ], + ) + + +def create_vnet_entry(dvs, name, tunnel, vni, peer_list): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + attrs = [ + ("vxlan_tunnel", tunnel), + ("vni", vni), + ("peer_list", peer_list), + ] + + # create the VXLAN tunnel Term entry in Config DB + create_entry_tbl( + conf_db, + "VNET", '|', name, + attrs, + ) + + time.sleep(2) + + +def create_vxlan_tunnel(dvs, name, src_ip): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attrs = [ + ("src_ip", src_ip), + ] + + # create the VXLAN tunnel Term entry in Config DB + create_entry_tbl( + conf_db, + "VXLAN_TUNNEL", '|', name, + attrs, + ) + + +def get_lo(dvs): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + vr_id = get_default_vr_id(dvs) + + tbl = swsscommon.Table(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE') + + entries = tbl.getKeys() + lo_id = None + for entry in entries: + status, fvs = tbl.get(entry) + assert status, "Got an error when get a key" + for key, value in fvs: + if key == 'SAI_ROUTER_INTERFACE_ATTR_TYPE' and value == 'SAI_ROUTER_INTERFACE_TYPE_LOOPBACK': + lo_id = entry + break + else: + assert False, 'Don\'t found loopback id' + + return lo_id + + +def get_switch_mac(dvs): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_SWITCH') + + entries = tbl.getKeys() + mac = None + for entry in entries: + status, fvs = tbl.get(entry) + assert status, "Got an error when get a key" + for key, value in fvs: + if key == 'SAI_SWITCH_ATTR_SRC_MAC_ADDRESS': + mac = value + break + else: + assert False, 'Don\'t found switch mac' + + return mac + + +loopback_id = 0 +def_vr_id = 0 +switch_mac = None + + +class VnetVxlanVrfTunnel(object): + + ASIC_TUNNEL_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL" + ASIC_TUNNEL_MAP = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP" + ASIC_TUNNEL_MAP_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY" + ASIC_TUNNEL_TERM_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY" + ASIC_RIF_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE" + ASIC_VRF_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER" + ASIC_ROUTE_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY" + ASIC_NEXT_HOP = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP" + + tunnel_map_ids = set() + tunnel_map_entry_ids = set() + tunnel_ids = set() + tunnel_term_ids = set() + tunnel_map_map = {} + tunnel = {} + vnet_vr_ids = set() + vr_map = {} + nh_ids = {} + + def fetch_exist_entries(self, dvs): + self.vnet_vr_ids = get_exist_entries(dvs, self.ASIC_VRF_TABLE) + self.tunnel_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_TABLE) + self.tunnel_map_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_MAP) + self.tunnel_map_entry_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_MAP_ENTRY) + self.tunnel_term_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_TERM_ENTRY) + self.rifs = get_exist_entries(dvs, self.ASIC_RIF_TABLE) + self.routes = get_exist_entries(dvs, self.ASIC_ROUTE_ENTRY) + self.nhops = get_exist_entries(dvs, self.ASIC_NEXT_HOP) + + global loopback_id, def_vr_id, switch_mac + if not loopback_id: + loopback_id = get_lo(dvs) + + if not def_vr_id: + def_vr_id = get_default_vr_id(dvs) + + if switch_mac is None: + switch_mac = get_switch_mac(dvs) + + def check_vxlan_tunnel(self, dvs, tunnel_name, src_ip): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + global loopback_id, def_vr_id + + tunnel_map_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP, self.tunnel_map_ids, 2) + tunnel_id = get_created_entry(asic_db, self.ASIC_TUNNEL_TABLE, self.tunnel_ids) + tunnel_term_id = get_created_entry(asic_db, self.ASIC_TUNNEL_TERM_ENTRY, self.tunnel_term_ids) + + # check that the vxlan tunnel termination are there + assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP) == (len(self.tunnel_map_ids) + 2), "The TUNNEL_MAP wasn't created" + assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) == len(self.tunnel_map_entry_ids), "The TUNNEL_MAP_ENTRY is created" + assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_TABLE) == (len(self.tunnel_ids) + 1), "The TUNNEL wasn't created" + assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_TERM_ENTRY) == (len(self.tunnel_term_ids) + 1), "The TUNNEL_TERM_TABLE_ENTRY wasm't created" + + check_object(asic_db, self.ASIC_TUNNEL_MAP, tunnel_map_id[0], + { + 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID', + } + ) + + check_object(asic_db, self.ASIC_TUNNEL_MAP, tunnel_map_id[1], + { + 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI', + } + ) + + check_object(asic_db, self.ASIC_TUNNEL_TABLE, tunnel_id, + { + 'SAI_TUNNEL_ATTR_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', + 'SAI_TUNNEL_ATTR_UNDERLAY_INTERFACE': loopback_id, + 'SAI_TUNNEL_ATTR_DECAP_MAPPERS': '1:%s' % tunnel_map_id[0], + 'SAI_TUNNEL_ATTR_ENCAP_MAPPERS': '1:%s' % tunnel_map_id[1], + 'SAI_TUNNEL_ATTR_ENCAP_SRC_IP': src_ip, + } + ) + + expected_attributes = { + 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE': 'SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP', + 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_VR_ID': def_vr_id, + 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_DST_IP': src_ip, + 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TUNNEL_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', + 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_ACTION_TUNNEL_ID': tunnel_id, + } + + check_object(asic_db, self.ASIC_TUNNEL_TERM_ENTRY, tunnel_term_id, expected_attributes) + + self.tunnel_map_ids.update(tunnel_map_id) + self.tunnel_ids.add(tunnel_id) + self.tunnel_term_ids.add(tunnel_term_id) + self.tunnel_map_map[tunnel_name] = tunnel_map_id + self.tunnel[tunnel_name] = tunnel_id + + def check_vxlan_tunnel_entry(self, dvs, tunnel_name, vnet_name, vni_id): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + time.sleep(2) + + if (self.tunnel_map_map.get(tunnel_name) is None): + tunnel_map_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP, self.tunnel_map_ids, 2) + else: + tunnel_map_id = self.tunnel_map_map[tunnel_name] + + tunnel_map_entry_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 2) + + # check that the vxlan tunnel termination are there + assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) == (len(self.tunnel_map_entry_ids) + 2), "The TUNNEL_MAP_ENTRY is created too early" + + check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[0], + { + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI', + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[1], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_KEY': self.vr_map[vnet_name].get('ing'), + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_VALUE': vni_id, + } + ) + + check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[1], + { + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID', + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[0], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vni_id, + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_VALUE': self.vr_map[vnet_name].get('egr'), + } + ) + + self.tunnel_map_entry_ids.update(tunnel_map_entry_id) + + def check_vnet_entry(self, dvs, name, peer_list=[]): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + #Check virtual router objects + assert how_many_entries_exist(asic_db, self.ASIC_VRF_TABLE) == (len(self.vnet_vr_ids) + 2),\ + "The VR objects are not created" + + new_vr_ids = get_created_entries(asic_db, self.ASIC_VRF_TABLE, self.vnet_vr_ids, 2) + + self.vnet_vr_ids.update(new_vr_ids) + self.vr_map[name] = { 'ing':new_vr_ids[0], 'egr':new_vr_ids[1], 'peer':peer_list } + + def vnet_route_ids(self, dvs, name, local=False): + vr_set = set() + + if local: + vr_set.add(self.vr_map[name].get('egr')) + else: + vr_set.add(self.vr_map[name].get('ing')) + + try: + for peer in self.vr_map[name].get('peer'): + vr_set.add(self.vr_map[peer].get('ing')) + except IndexError: + pass + + return vr_set + + def check_router_interface(self, dvs, name, vlan_oid=0): + # Check RIF in ingress VRF + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + global switch_mac + + expected_attr = { + "SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID": self.vr_map[name].get('ing'), + "SAI_ROUTER_INTERFACE_ATTR_SRC_MAC_ADDRESS": switch_mac, + "SAI_ROUTER_INTERFACE_ATTR_MTU": "9100", + } + + if vlan_oid: + expected_attr.update({'SAI_ROUTER_INTERFACE_ATTR_TYPE': 'SAI_ROUTER_INTERFACE_TYPE_VLAN'}) + expected_attr.update({'SAI_ROUTER_INTERFACE_ATTR_VLAN_ID': vlan_oid}) + else: + expected_attr.update({'SAI_ROUTER_INTERFACE_ATTR_TYPE': 'SAI_ROUTER_INTERFACE_TYPE_PORT'}) + + new_rif = get_created_entry(asic_db, self.ASIC_RIF_TABLE, self.rifs) + check_object(asic_db, self.ASIC_RIF_TABLE, new_rif, expected_attr) + + #IP2ME and subnet routes will be created with every router interface + new_route = get_created_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, 2) + + self.rifs.add(new_rif) + self.routes.update(new_route) + + def check_vnet_local_routes(self, dvs, name): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + vr_ids = self.vnet_route_ids(dvs, name, True) + count = len(vr_ids) + + new_route = get_created_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, count) + + #Check if the route is duplicated to egress VRF + asic_vrs = set() + for idx in range(count): + rt_key = json.loads(new_route[idx]) + asic_vrs.add(rt_key['vr']) + + assert asic_vrs == vr_ids + + self.routes.update(new_route) + + def check_vnet_routes(self, dvs, name, endpoint, tunnel, mac="", vni=0): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + vr_ids = self.vnet_route_ids(dvs, name) + count = len(vr_ids) + + # Check routes in ingress VRF + expected_attr = { + "SAI_NEXT_HOP_ATTR_TYPE": "SAI_NEXT_HOP_TYPE_TUNNEL_ENCAP", + "SAI_NEXT_HOP_ATTR_IP": endpoint, + "SAI_NEXT_HOP_ATTR_TUNNEL_ID": self.tunnel[tunnel], + } + + if vni: + expected_attr.update({'SAI_NEXT_HOP_ATTR_TUNNEL_VNI': vni}) + + if mac: + expected_attr.update({'SAI_NEXT_HOP_ATTR_TUNNEL_MAC': mac}) + + if endpoint in self.nh_ids: + new_nh = self.nh_ids[endpoint] + else: + new_nh = get_created_entry(asic_db, self.ASIC_NEXT_HOP, self.nhops) + self.nh_ids[endpoint] = new_nh + self.nhops.add(new_nh) + + check_object(asic_db, self.ASIC_NEXT_HOP, new_nh, expected_attr) + new_route = get_created_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, count) + + #Check if the route is in expected VRF + asic_vrs = set() + for idx in range(count): + check_object(asic_db, self.ASIC_ROUTE_ENTRY, new_route[idx], + { + "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID": new_nh, + } + ) + rt_key = json.loads(new_route[idx]) + asic_vrs.add(rt_key['vr']) + + assert asic_vrs == vr_ids + + self.routes.update(new_route) + +class TestVnetOrch(object): + + ''' + Test 1 - Create Vlan Interface, Tunnel and Vnet + ''' + def test_vnet_orch_1(self, dvs, testlog): + vnet_obj = VnetVxlanVrfTunnel() + + tunnel_name = 'tunnel_1' + + vnet_obj.fetch_exist_entries(dvs) + + create_vxlan_tunnel(dvs, tunnel_name, '10.10.10.10') + create_vnet_entry(dvs, 'Vnet_2000', tunnel_name, '2000', "") + + vnet_obj.check_vnet_entry(dvs, 'Vnet_2000') + vnet_obj.check_vxlan_tunnel_entry(dvs, tunnel_name, 'Vnet_2000', '2000') + + vnet_obj.check_vxlan_tunnel(dvs, tunnel_name, '10.10.10.10') + + vid = create_vlan_interface(dvs, "Vlan100", "Ethernet24", "Vnet_2000", "100.100.3.1/24") + vnet_obj.check_router_interface(dvs, 'Vnet_2000', vid) + + vid = create_vlan_interface(dvs, "Vlan101", "Ethernet28", "Vnet_2000", "100.100.4.1/24") + vnet_obj.check_router_interface(dvs, 'Vnet_2000', vid) + + create_vnet_routes(dvs, "100.100.1.1/32", 'Vnet_2000', '10.10.10.1') + vnet_obj.check_vnet_routes(dvs, 'Vnet_2000', '10.10.10.1', tunnel_name) + + create_vnet_local_routes(dvs, "100.100.3.0/24", 'Vnet_2000', 'Vlan100') + vnet_obj.check_vnet_local_routes(dvs, 'Vnet_2000') + + create_vnet_local_routes(dvs, "100.100.4.0/24", 'Vnet_2000', 'Vlan101') + vnet_obj.check_vnet_local_routes(dvs, 'Vnet_2000') + + #Create Physical Interface in another Vnet + + create_vnet_entry(dvs, 'Vnet_2001', tunnel_name, '2001', "") + + vnet_obj.check_vnet_entry(dvs, 'Vnet_2001') + vnet_obj.check_vxlan_tunnel_entry(dvs, tunnel_name, 'Vnet_2001', '2001') + + create_phy_interface(dvs, "Ethernet4", "Vnet_2001", "100.102.1.1/24") + vnet_obj.check_router_interface(dvs, 'Vnet_2001') + + create_vnet_routes(dvs, "100.100.2.1/32", 'Vnet_2001', '10.10.10.2', "00:12:34:56:78:9A") + vnet_obj.check_vnet_routes(dvs, 'Vnet_2001', '10.10.10.2', tunnel_name, "00:12:34:56:78:9A") + + create_vnet_local_routes(dvs, "100.102.1.0/24", 'Vnet_2001', 'Ethernet4') + vnet_obj.check_vnet_local_routes(dvs, 'Vnet_2001') + + ''' + Test 2 - Two VNets, One HSMs per VNet + ''' + def test_vnet_orch_2(self, dvs, testlog): + vnet_obj = VnetVxlanVrfTunnel() + + tunnel_name = 'tunnel_2' + + vnet_obj.fetch_exist_entries(dvs) + + create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + create_vnet_entry(dvs, 'Vnet_1', tunnel_name, '1111', "") + + vnet_obj.check_vnet_entry(dvs, 'Vnet_1') + vnet_obj.check_vxlan_tunnel_entry(dvs, tunnel_name, 'Vnet_1', '1111') + + tun_id = vnet_obj.check_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + + vid = create_vlan_interface(dvs, "Vlan1001", "Ethernet0", "Vnet_1", "1.1.10.1/24") + vnet_obj.check_router_interface(dvs, 'Vnet_1', vid) + + create_vnet_routes(dvs, "1.1.1.10/32", 'Vnet_1', '100.1.1.10') + vnet_obj.check_vnet_routes(dvs, 'Vnet_1', '100.1.1.10', tunnel_name) + + create_vnet_routes(dvs, "1.1.1.11/32", 'Vnet_1', '100.1.1.10') + vnet_obj.check_vnet_routes(dvs, 'Vnet_1', '100.1.1.10', tunnel_name) + + create_vnet_routes(dvs, "1.1.1.12/32", 'Vnet_1', '200.200.1.200') + vnet_obj.check_vnet_routes(dvs, 'Vnet_1', '200.200.1.200', tunnel_name) + + create_vnet_routes(dvs, "1.1.1.14/32", 'Vnet_1', '200.200.1.201') + vnet_obj.check_vnet_routes(dvs, 'Vnet_1', '200.200.1.201', tunnel_name) + + create_vnet_local_routes(dvs, "1.1.10.0/24", 'Vnet_1', 'Vlan1001') + vnet_obj.check_vnet_local_routes(dvs, 'Vnet_1') + + create_vnet_entry(dvs, 'Vnet_2', tunnel_name, '2222', "") + + vnet_obj.check_vnet_entry(dvs, 'Vnet_2') + vnet_obj.check_vxlan_tunnel_entry(dvs, tunnel_name, 'Vnet_2', '2222') + + vid = create_vlan_interface(dvs, "Vlan1002", "Ethernet4", "Vnet_2", "2.2.10.1/24") + vnet_obj.check_router_interface(dvs, 'Vnet_2', vid) + + create_vnet_routes(dvs, "2.2.2.10/32", 'Vnet_2', '100.1.1.20') + vnet_obj.check_vnet_routes(dvs, 'Vnet_2', '100.1.1.20', tunnel_name) + + create_vnet_routes(dvs, "2.2.2.11/32", 'Vnet_2', '100.1.1.20') + vnet_obj.check_vnet_routes(dvs, 'Vnet_2', '100.1.1.20', tunnel_name) + + create_vnet_local_routes(dvs, "2.2.10.0/24", 'Vnet_2', 'Vlan1002') + vnet_obj.check_vnet_local_routes(dvs, 'Vnet_2') + + ''' + Test 3 - Two VNets, One HSMs per VNet, Peering + ''' + def test_vnet_orch_3(self, dvs, testlog): + vnet_obj = VnetVxlanVrfTunnel() + + tunnel_name = 'tunnel_3' + + vnet_obj.fetch_exist_entries(dvs) + + create_vxlan_tunnel(dvs, tunnel_name, '7.7.7.7') + + create_vnet_entry(dvs, 'Vnet_10', tunnel_name, '1111', "Vnet_20") + + vnet_obj.check_vnet_entry(dvs, 'Vnet_10', ['Vnet_20']) + vnet_obj.check_vxlan_tunnel_entry(dvs, tunnel_name, 'Vnet_10', '1111') + + create_vnet_entry(dvs, 'Vnet_20', tunnel_name, '2222', "Vnet_10") + + vnet_obj.check_vnet_entry(dvs, 'Vnet_20', ['Vnet_10']) + vnet_obj.check_vxlan_tunnel_entry(dvs, tunnel_name, 'Vnet_20', '2222') + + tun_id = vnet_obj.check_vxlan_tunnel(dvs, tunnel_name, '7.7.7.7') + + vid = create_vlan_interface(dvs, "Vlan2001", "Ethernet8", "Vnet_10", "5.5.10.1/24") + vnet_obj.check_router_interface(dvs, 'Vnet_10', vid) + + vid = create_vlan_interface(dvs, "Vlan2002", "Ethernet12", "Vnet_20", "8.8.10.1/24") + vnet_obj.check_router_interface(dvs, 'Vnet_20', vid) + + create_vnet_routes(dvs, "5.5.5.10/32", 'Vnet_10', '50.1.1.10') + vnet_obj.check_vnet_routes(dvs, 'Vnet_10', '50.1.1.10', tunnel_name) + + create_vnet_routes(dvs, "8.8.8.10/32", 'Vnet_20', '80.1.1.20') + vnet_obj.check_vnet_routes(dvs, 'Vnet_10', '80.1.1.20', tunnel_name) + + create_vnet_local_routes(dvs, "5.5.10.0/24", 'Vnet_10', 'Vlan2001') + vnet_obj.check_vnet_local_routes(dvs, 'Vnet_10') + + create_vnet_local_routes(dvs, "8.8.10.0/24", 'Vnet_20', 'Vlan2002') + vnet_obj.check_vnet_local_routes(dvs, 'Vnet_20') diff --git a/tests/test_vxlan_tunnel.py b/tests/test_vxlan_tunnel.py index fd6673de55fd..f024646b0a11 100644 --- a/tests/test_vxlan_tunnel.py +++ b/tests/test_vxlan_tunnel.py @@ -101,30 +101,8 @@ def create_vlan(dvs, vlan_name, vlan_ids): return -def create_vxlan_tunnel(dvs, name, src_ip, dst_ip, tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, lo_id, skip_dst_ip=False): +def check_vxlan_tunnel(dvs, src_ip, dst_ip, tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, lo_id): asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - # check the source information - assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP") == len(tunnel_map_ids), "The initial state is incorrect" - assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY") == len(tunnel_map_entry_ids), "The initial state is incorrect" - assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL") == len(tunnel_ids), "The initial state is incorrect" - assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY") == len(tunnel_term_ids), "The initial state is incorrect" - - attrs = [ - ("src_ip", src_ip), - ] - - if not skip_dst_ip: - attrs.append(("dst_ip", dst_ip)) - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - conf_db, - "VXLAN_TUNNEL", '|', name, - attrs, - ) - tunnel_map_id = get_created_entry(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP", tunnel_map_ids) tunnel_id = get_created_entry(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL", tunnel_ids) tunnel_term_id = get_created_entry(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY", tunnel_term_ids) @@ -171,7 +149,33 @@ def create_vxlan_tunnel(dvs, name, src_ip, dst_ip, tunnel_map_ids, tunnel_map_en return tunnel_map_id -def create_vxlan_tunnel_entry(dvs, tunnel_name, tunnel_map_entry_name, tunnel_map_id, vlan, vni_id, + +def create_vxlan_tunnel(dvs, name, src_ip, dst_ip, tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, lo_id, skip_dst_ip=False): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + # check the source information + assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP") == len(tunnel_map_ids), "The initial state is incorrect" + assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY") == len(tunnel_map_entry_ids), "The initial state is incorrect" + assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL") == len(tunnel_ids), "The initial state is incorrect" + assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY") == len(tunnel_term_ids), "The initial state is incorrect" + + attrs = [ + ("src_ip", src_ip), + ] + + if not skip_dst_ip: + attrs.append(("dst_ip", dst_ip)) + + # create the VXLAN tunnel Term entry in Config DB + create_entry_tbl( + conf_db, + "VXLAN_TUNNEL", '|', name, + attrs, + ) + + +def create_vxlan_tunnel_entry(dvs, tunnel_name, tunnel_map_entry_name, tunnel_map_map, vlan, vni_id, tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids): asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) @@ -192,13 +196,15 @@ def create_vxlan_tunnel_entry(dvs, tunnel_name, tunnel_map_entry_name, tunnel_ma ], ) + if (tunnel_map_map.get(tunnel_name) is None): + tunnel_map_id = get_created_entry(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP", tunnel_map_ids) + else: + tunnel_map_id = tunnel_map_map[tunnel_name] + tunnel_map_entry_id = get_created_entry(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY", tunnel_map_entry_ids) # check that the vxlan tunnel termination are there - assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP") == len(tunnel_map_ids), "The TUNNEL_MAP wasn't created" assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY") == (len(tunnel_map_entry_ids) + 1), "The TUNNEL_MAP_ENTRY is created too early" - assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL") == len(tunnel_ids), "The TUNNEL wasn't created" - assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY") == len(tunnel_term_ids), "The TUNNEL_TERM_TABLE_ENTRY wasm't created" vlan_id = vlan[4:] check_object(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY", tunnel_map_entry_id, @@ -253,38 +259,50 @@ def test_vxlan_term_orch(dvs, testlog): create_vlan(dvs, "Vlan56", vlan_ids) create_vlan(dvs, "Vlan57", vlan_ids) - tunnel_map_map['tunnel_1'] = create_vxlan_tunnel(dvs, 'tunnel_1', '10.0.0.1', '100.100.100.1', - tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) + create_vxlan_tunnel(dvs, 'tunnel_1', '10.0.0.1', '100.100.100.1', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) - create_vxlan_tunnel_entry(dvs, 'tunnel_1', 'entry_1', tunnel_map_map['tunnel_1'], 'Vlan50', '850', + create_vxlan_tunnel_entry(dvs, 'tunnel_1', 'entry_1', tunnel_map_map, 'Vlan50', '850', tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) - tunnel_map_map['tunnel_2'] = create_vxlan_tunnel(dvs, 'tunnel_2', '11.0.0.2', '101.101.101.2', - tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) + tunnel_map_map['tunnel_1'] = check_vxlan_tunnel(dvs,'10.0.0.1', '100.100.100.1', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) + + create_vxlan_tunnel(dvs, 'tunnel_2', '11.0.0.2', '101.101.101.2', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) - create_vxlan_tunnel_entry(dvs, 'tunnel_2', 'entry_1', tunnel_map_map['tunnel_2'], 'Vlan51', '851', + create_vxlan_tunnel_entry(dvs, 'tunnel_2', 'entry_1', tunnel_map_map, 'Vlan51', '851', tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) - tunnel_map_map['tunnel_3'] = create_vxlan_tunnel(dvs, 'tunnel_3', '12.0.0.3', '0.0.0.0', - tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) + tunnel_map_map['tunnel_2'] = check_vxlan_tunnel(dvs,'11.0.0.2', '101.101.101.2', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) - create_vxlan_tunnel_entry(dvs, 'tunnel_3', 'entry_1', tunnel_map_map['tunnel_3'], 'Vlan52', '852', + create_vxlan_tunnel(dvs, 'tunnel_3', '12.0.0.3', '0.0.0.0', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) + + create_vxlan_tunnel_entry(dvs, 'tunnel_3', 'entry_1', tunnel_map_map, 'Vlan52', '852', tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) - tunnel_map_map['tunnel_4'] = create_vxlan_tunnel(dvs, 'tunnel_4', '15.0.0.5', '0.0.0.0', - tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id, True) + tunnel_map_map['tunnel_3'] = check_vxlan_tunnel(dvs, '12.0.0.3', '0.0.0.0', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) + + create_vxlan_tunnel(dvs, 'tunnel_4', '15.0.0.5', '0.0.0.0', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id, True) - create_vxlan_tunnel_entry(dvs, 'tunnel_4', 'entry_1', tunnel_map_map['tunnel_4'], 'Vlan53', '853', + create_vxlan_tunnel_entry(dvs, 'tunnel_4', 'entry_1', tunnel_map_map, 'Vlan53', '853', tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) - create_vxlan_tunnel_entry(dvs, 'tunnel_1', 'entry_2', tunnel_map_map['tunnel_1'], 'Vlan54', '854', + tunnel_map_map['tunnel_4'] = check_vxlan_tunnel(dvs, '15.0.0.5', '0.0.0.0', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) + + create_vxlan_tunnel_entry(dvs, 'tunnel_1', 'entry_2', tunnel_map_map, 'Vlan54', '854', tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) - create_vxlan_tunnel_entry(dvs, 'tunnel_2', 'entry_2', tunnel_map_map['tunnel_2'], 'Vlan55', '855', + create_vxlan_tunnel_entry(dvs, 'tunnel_2', 'entry_2', tunnel_map_map, 'Vlan55', '855', tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) - create_vxlan_tunnel_entry(dvs, 'tunnel_3', 'entry_2', tunnel_map_map['tunnel_3'], 'Vlan56', '856', + create_vxlan_tunnel_entry(dvs, 'tunnel_3', 'entry_2', tunnel_map_map, 'Vlan56', '856', tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) - create_vxlan_tunnel_entry(dvs, 'tunnel_4', 'entry_2', tunnel_map_map['tunnel_4'], 'Vlan57', '857', + create_vxlan_tunnel_entry(dvs, 'tunnel_4', 'entry_2', tunnel_map_map, 'Vlan57', '857', tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids)