Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dash] Add outbound Privatelink traffic test #14757

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 175 additions & 0 deletions tests/dash/configs/privatelink_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import base64
import socket
import uuid
from ipaddress import ip_address as IP

from dash_api.appliance_pb2 import Appliance
from dash_api.vnet_pb2 import Vnet
from dash_api.eni_pb2 import Eni, State
from dash_api.eni_route_pb2 import EniRoute
from dash_api.route_pb2 import Route
from dash_api.vnet_mapping_pb2 import VnetMapping
from dash_api.route_type_pb2 import RouteType, RoutingType, ActionType, EncapType
from dash_api.route_group_pb2 import RouteGroup
from google.protobuf.json_format import ParseDict

VNET_ENCAP = "vnet_encap"
VNET_DIRECT = "vnet_direct"
PRIVATELINK = "privatelink"
DECAP = "decap"

SIP = "10.2.0.1"
INBOUND_UNDERLAY_IP = "25.1.1.1"
OUTBOUND_UNDERLAY_IP = "101.1.2.3"
VNET_MAP_IP1 = "10.1.1.5"
VNET_MAP_IP2 = "10.1.2.5"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that we don't use it

OUTBOUND_ROUTE_PREFIX1 = "10.1.1.0/24"
OUTBOUND_ROUTE_PREFIX2 = "10.1.2.0/24"
OVERLAY_IP = "10.0.0.6"
PL_ENCODING_IP = "::56b2:0:ff71:0:0"
PL_ENCODING_MASK = "::ffff:ffff:ffff:0:0"
PL_UNDERLAY_SIP1 = "55.1.2.3"
PL_UNDERLAY_SIP2 = "55.2.3.4"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that we don't use it

PL_OVERLAY_SIP = "fd41:108:20:abc:abc::0"
PL_OVERLAY_SIP_MASK = "ffff:ffff:ffff:ffff:ffff:ffff::"
PL_OVERLAY_DIP = "2603:10e1:100:2::3401:203"
PL_OVERLAY_DIP_MASK = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"

APPLIANCE_ID = "100"
VM_VNI = "4321"
ENCAP_VNI = 100
VNET1 = "Vnet1"
VNET1_VNI = "45654"
VNET1_GUID = "559c6ce8-26ab-4193-b946-ccc6e8f930b2"
ENI_MAC = "F4:93:9F:EF:C4:7E"
ENI_MAC_STRING = ENI_MAC.replace(":", "")
REMOTE_MAC = "43:BE:65:25:FA:67"
REMOTE_MAC_STRING = REMOTE_MAC.replace(":", "")
ENI_ID = "497f23d7-f0ac-4c99-a98f-59b470e8c7bd"
ROUTE_GROUP1 = "RouteGroup1"
ROUTE_GROUP2 = "RouteGroup2"
ROUTE_GROUP1_GUID = "48af6ce8-26cc-4293-bfa6-0126e8fcdeb2"
ROUTE_GROUP2_GUID = "58cf62e0-22cc-4693-baa6-012358fcdec9"

APPLIANCE_CONFIG = {
f"DASH_APPLIANCE_TABLE:{APPLIANCE_ID}":
ParseDict({
"sip": {
"ipv4": socket.htonl(int(IP(SIP)))
},
"vm_vni": int(VM_VNI)
}, Appliance())
}

VNET_CONFIG = {
f"DASH_VNET_TABLE:{VNET1}":
ParseDict({
"vni": VNET1_VNI,
"guid": {
"value": base64.b64encode(bytes.fromhex(uuid.UUID(VNET1_GUID).hex))
}
}, Vnet())
}

ENI_CONFIG = {
f"DASH_ENI_TABLE:{ENI_ID}":
ParseDict({
"vnet": VNET1,
"underlay_ip": {
"ipv4": socket.htonl(int(IP(INBOUND_UNDERLAY_IP)))
},
"mac_address": base64.b64encode(bytes.fromhex(ENI_MAC_STRING)),
"eni_id": ENI_ID,
"admin_state": State.STATE_ENABLED,
"pl_underlay_sip": {
"ipv4": socket.htonl(int(IP(PL_UNDERLAY_SIP1)))
},
"pl_sip_encoding": {
"ip": {
"ipv6": base64.b64encode(IP(PL_ENCODING_IP).packed)
},
"mask": {
"ipv6": base64.b64encode(IP(PL_ENCODING_MASK).packed)
}
}
}, Eni())
}

VNET_MAPPING_CONFIG = {
f"DASH_VNET_MAPPING_TABLE:{VNET1}:{VNET_MAP_IP1}":
ParseDict({
"mac_address": base64.b64encode(bytes.fromhex(REMOTE_MAC_STRING)),
"routing_type": RoutingType.ROUTING_TYPE_PRIVATELINK,
"underlay_ip": {
"ipv4": socket.htonl(int(IP(OUTBOUND_UNDERLAY_IP)))
},
"overlay_sip_prefix": {
"ip": {
"ipv6": base64.b64encode(IP(PL_OVERLAY_SIP).packed)
},
"mask": {
"ipv6": base64.b64encode(IP(PL_OVERLAY_SIP_MASK).packed)
}
},
"overlay_dip_prefix": {
"ip": {
"ipv6": base64.b64encode(IP(PL_OVERLAY_DIP).packed)
},
"mask": {
"ipv6": base64.b64encode(IP(PL_OVERLAY_DIP_MASK).packed)
}
},
}, VnetMapping())
}

ROUTE_VNET_CONFIG = {
f"DASH_ROUTE_TABLE:{ROUTE_GROUP1}:{OUTBOUND_ROUTE_PREFIX1}":
ParseDict({
"routing_type": RoutingType.ROUTING_TYPE_VNET,
"vnet": VNET1,
}, Route())
}

ROUTE_VNET_CONFIG_UNDERLAY_SIP = {
f"DASH_ROUTE_TABLE:{ROUTE_GROUP2}:{OUTBOUND_ROUTE_PREFIX2}":
ParseDict({
"routing_type": RoutingType.ROUTING_TYPE_VNET,
"vnet": VNET1,
"underlay_sip": {
"ipv4": socket.htonl(int(IP(PL_UNDERLAY_SIP2)))
}
}, Route())
}

ROUTING_TYPE_PL_CONFIG = {
f"DASH_ROUTING_TYPE_TABLE:{PRIVATELINK}":
ParseDict({
"items": [
{
"action_name": "action1",
"action_type": ActionType.ACTION_TYPE_4_to_6
},
{
"action_name": "action2",
"action_type": ActionType.ACTION_TYPE_STATICENCAP,
"encap_type": EncapType.ENCAP_TYPE_NVGRE,
"vni": ENCAP_VNI
}
]
}, RouteType())
}

ROUTE_GROUP1_CONFIG = {
f"DASH_ROUTE_GROUP_TABLE:{ROUTE_GROUP1}":
ParseDict({
"guid": ROUTE_GROUP1_GUID,
"version": "rg_version"
}, RouteGroup())
}

ENI_ROUTE_GROUP1_CONFIG = {
f"DASH_ENI_ROUTE_TABLE:{ENI_ID}":
ParseDict({
"group_id": ROUTE_GROUP1
}, EniRoute())
}
39 changes: 36 additions & 3 deletions tests/dash/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,36 @@ def get_intf_from_ip(local_ip, config_facts):
if str(intf_ip.ip) == local_ip:
return intf, intf_ip

for intf, config in list(config_facts["PORTCHANNEL_INTERFACE"].items()):
for ip in config:
intf_ip = ip_interface(ip)
if str(intf_ip.ip) == local_ip:
return intf, intf_ip


@pytest.fixture(params=["no-underlay-route", "with-underlay-route"])
def use_underlay_route(request):
return request.param == "with-underlay-route"


@pytest.fixture
def dash_pl_config(duthost, config_facts, minigraph_facts):
dash_info = {
DUT_MAC: config_facts["DEVICE_METADATA"]["localhost"]["mac"],
LOCAL_CA_IP: "10.2.2.2",
}

neigh_table = duthost.switch_arptable()['ansible_facts']['arptable']
for neigh_ip, config in list(config_facts["BGP_NEIGHBOR"].items()):
if ip_interface(neigh_ip).version == 4:
if config["name"].endswith("T0"):
intf, _ = get_intf_from_ip(config['local_addr'], config_facts)
dash_info[LOCAL_PTF_INTF] = minigraph_facts["minigraph_ptf_indices"][intf]
dash_info[LOCAL_PTF_MAC] = neigh_table["v4"][neigh_ip]["macaddress"]
break
return dash_info


@pytest.fixture(scope="function")
def dash_config_info(duthost, config_facts, minigraph_facts, tbinfo):
dash_info = {
Expand Down Expand Up @@ -148,11 +172,13 @@ def dash_config_info(duthost, config_facts, minigraph_facts, tbinfo):
# Take neighbor 1 as local PA, take neighbor 2 as remote PA
if ip_interface(neigh_ip).version == 4:
if LOCAL_PA_IP not in dash_info:
dash_info[LOCAL_PA_IP] = neigh_ip
intf, _ = get_intf_from_ip(config['local_addr'], config_facts)
if "PortChannel" in intf:
continue
dash_info[LOCAL_PA_IP] = neigh_ip
dash_info[LOCAL_PTF_INTF] = minigraph_facts["minigraph_ptf_indices"][intf]
dash_info[LOCAL_PTF_MAC] = neigh_table["v4"][neigh_ip]["macaddress"]
if topo == 'dpu-1' and REMOTE_PA_IP not in dash_info:
if (topo == 'dpu-1' or topo == "t1-28-lag") and REMOTE_PA_IP not in dash_info:
# For DPU with only one single port, we just have one neighbor (neighbor 1).
# So, we take neighbor 1 as the local PA. For the remote PA,
# we take the original neighbor 2's IP as the remote PA IP,
Expand All @@ -170,8 +196,10 @@ def dash_config_info(duthost, config_facts, minigraph_facts, tbinfo):
dash_info[REMOTE_PA_PREFIX] = fake_neighbor_2_prefix
break
elif REMOTE_PA_IP not in dash_info:
dash_info[REMOTE_PA_IP] = neigh_ip
intf, intf_ip = get_intf_from_ip(config['local_addr'], config_facts)
if "PortChannel" in intf:
continue
dash_info[REMOTE_PA_IP] = neigh_ip
dash_info[REMOTE_PTF_INTF] = minigraph_facts["minigraph_ptf_indices"][intf]
dash_info[REMOTE_PTF_MAC] = neigh_table["v4"][neigh_ip]["macaddress"]
dash_info[REMOTE_PA_PREFIX] = str(intf_ip.network)
Expand Down Expand Up @@ -354,3 +382,8 @@ def acl_default_rule(localhost, duthost, ptfhost, dash_config_info):
default_acl_rule.teardown()
del default_acl_group
time.sleep(WAIT_AFTER_CONFIG)


@pytest.fixture(scope="module")
def dpu_index():
return 0
50 changes: 45 additions & 5 deletions tests/dash/gnmi_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ def __init__(self, duthost):
self.gnmi_client_cert = "gnmiclient.crt"
self.gnmi_client_key = "gnmiclient.key"
self.gnmi_server_start_wait_time = 30
self.enable_zmq = duthost.shell("netstat -na | grep -w 8100", module_ignore_errors=True)['rc'] == 0
# self.enable_zmq = duthost.shell("netstat -na | grep -w 8100", module_ignore_errors=True)['rc'] == 0
self.enable_zmq = True
cmd = "docker images | grep -w sonic-gnmi"
if duthost.shell(cmd, module_ignore_errors=True)['rc'] == 0:
cmd = "docker ps | grep -w gnmi"
Expand Down Expand Up @@ -267,6 +268,7 @@ def gnmi_set(duthost, ptfhost, delete_list, update_list, replace_list):
cmd += '--xpath ' + xpath
cmd += ' '
cmd += '--value ' + xvalue
logger.info(f"PTF GNMI command: {cmd}")
output = ptfhost.shell(cmd, module_ignore_errors=True)
error = "GRPC error\n"
if error in output['stdout']:
Expand Down Expand Up @@ -324,6 +326,40 @@ def gnmi_get(duthost, ptfhost, path_list):
raise Exception("error:" + msg)


def apply_messages(
localhost,
duthost,
ptfhost,
messages,
dpu_index,
set=True,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'set' is a Python built-in name, better to use another name.

wait_after_apply=5,
max_updates_in_single_cmd=1024,
):
env = GNMIEnvironment(duthost)
update_list = []
delete_list = []
for i, (key, message) in enumerate(messages.items()):
keys = key.split(":", 1)
k = keys[0] + "[key=" + keys[1] + "]"
filename = f"update{i}"

if set:
if proto_utils.ENABLE_PROTO:
path = f"/APPL_DB/dpu{dpu_index}/{k}:$/root/{filename}"
else:
path = f"/APPL_DB/dpu{dpu_index}/{k}:@/root/{filename}"
with open(env.work_dir + filename, "wb") as file:
file.write(message.SerializeToString())
update_list.append(path)
else:
path = f"/APPL_DB/dpu{dpu_index}/{filename}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be gnmi_key instead of filename

delete_list.append(path)

write_gnmi_files(localhost, duthost, ptfhost, env, delete_list, update_list, max_updates_in_single_cmd)
time.sleep(wait_after_apply)


def apply_gnmi_file(localhost, duthost, ptfhost, dest_path=None, config_json=None,
wait_after_apply=5, max_updates_in_single_cmd=1024):
"""
Expand Down Expand Up @@ -371,20 +407,25 @@ def apply_gnmi_file(localhost, duthost, ptfhost, dest_path=None, config_json=Non
keys = k.split(":", 1)
k = keys[0] + "[key=" + keys[1] + "]"
if proto_utils.ENABLE_PROTO:
path = "/APPL_DB/localhost/%s:$/root/%s" % (k, filename)
path = "/APPL_DB/dpu1/%s:$/root/%s" % (k, filename)
else:
path = "/APPL_DB/localhost/%s:@/root/%s" % (k, filename)
path = "/APPL_DB/dpu1/%s:@/root/%s" % (k, filename)
update_list.append(path)
elif operation["OP"] == "DEL":
for k, v in operation.items():
if k == "OP":
continue
keys = k.split(":", 1)
k = keys[0] + "[key=" + keys[1] + "]"
path = "/APPL_DB/localhost/%s" % (k)
path = "/APPL_DB/dpu1/%s" % (k)
delete_list.append(path)
else:
logger.info("Invalid operation %s" % operation["OP"])
write_gnmi_files(localhost, duthost, ptfhost, env, delete_list, update_list, max_updates_in_single_cmd)
time.sleep(wait_after_apply)


def write_gnmi_files(localhost, duthost, ptfhost, env, delete_list, update_list, max_updates_in_single_cmd):
localhost.shell(f'tar -zcvf /tmp/updates.tar.gz -C {env.work_dir} .')
ptfhost.copy(src='/tmp/updates.tar.gz', dest='~')
ptfhost.shell('tar -xf updates.tar.gz')
Expand All @@ -410,4 +451,3 @@ def _devide_list(operation_list):
ptfhost.shell('rm -f updates.tar.gz')
localhost.shell(f'rm -f {env.work_dir}update*')
ptfhost.shell('rm -f update*')
time.sleep(wait_after_apply)
Loading
Loading