diff --git a/.github/workflows/dash-bmv2-ci.yml b/.github/workflows/dash-bmv2-ci.yml index 4aabd09b8..65971c75e 100644 --- a/.github/workflows/dash-bmv2-ci.yml +++ b/.github/workflows/dash-bmv2-ci.yml @@ -11,7 +11,8 @@ on: - 'test/**.sh' - 'test/**.yml' - 'dash-pipeline/**' - - '!dash-pipeline/docker files/Dockerfile.*' + - '!dash-pipeline/dockerfiles/Dockerfile.*' + - 'dash-pipeline/dockerfiles/*.env' - '!dash-pipeline/.dockerignore' - '!dash-pipeline/**.md' - '!dash-pipeline/**.svg' @@ -27,7 +28,8 @@ on: - 'test/**.sh' - 'test/**.yml' - 'dash-pipeline/**' - - '!dash-pipeline/docker files/Dockerfile.*' + - '!dash-pipeline/dockerfiles/Dockerfile.*' + - 'dash-pipeline/dockerfiles/*.env' - '!dash-pipeline/.dockerignore' - '!dash-pipeline/**.md' - '!dash-pipeline/**.svg' @@ -82,4 +84,6 @@ jobs: run: DOCKER_FLAGS=$docker_fg_flags make deploy-ixiac - name: Run Pytests run: DOCKER_FLAGS=$docker_fg_root_flags make run-saithrift-pytests + - name: Run SAI-Challenger Tests + run: DOCKER_FLAGS=$docker_fg_root_flags make run-saichallenger-tests diff --git a/.github/workflows/dash-saichallenger-client-docker-acr.yml b/.github/workflows/dash-saichallenger-client-docker-acr.yml new file mode 100644 index 000000000..7e867cba4 --- /dev/null +++ b/.github/workflows/dash-saichallenger-client-docker-acr.yml @@ -0,0 +1,54 @@ +name: DASH-docker-saichallenger-client-build-publish-acr + +on: + push: + branches: [ "**" ] + paths: + - '.github/workflows/dash-saichallenger-client-docker-acr.yml' + - 'dash-pipeline/Makefile' + - 'dash-pipeline/dockerfiles/Dockerfile.saichallenger-client' + - 'dash-pipeline/.dockerignore' + - 'dash-pipeline/dockerfiles/.dockerignore' + pull_request: + branches: [ "**" ] + paths: + - '.github/workflows/dash-saichallenger-client-docker-acr.yml' + - 'dash-pipeline/Makefile' + - 'dash-pipeline/dockerfiles/Dockerfile.saichallenger-client' + - 'dash-pipeline/.dockerignore' + - 'dash-pipeline/dockerfiles/.dockerignore' + workflow_dispatch: + +jobs: + build: + # Can only publish from within DASH repo (need credentials from secrets) + if: github.repository == 'Azure/DASH' + name: Build and publish dash-saichallenger-client image + runs-on: ubuntu-20.04 + env: + docker_fg_flags: -u root --privileged + docker_bg_flags: -d -u root --privileged + defaults: + run: + working-directory: ./dash-pipeline + steps: + - uses: actions/checkout@v3 + - name: Pull docker p4c image + run: make docker-pull-dash-p4c + - name: Build P4 software switch (bmv2) and P4Info + run: DOCKER_FLAGS=$docker_fg_flags make p4 + - name: Install SAI submodule + run: git submodule update --init + - name: Generate SAI API + run: DOCKER_FLAGS=$docker_fg_flags make sai + - name: Generate SAI-Thrift client and server code and libs + run: DOCKER_FLAGS=$docker_fg_flags make saithrift-server + - uses: azure/docker-login@v1 + with: + login-server: sonicdash.azurecr.io + username: ${{ secrets.DASH_ACR_USERNAME }} + password: ${{ secrets.DASH_ACR_PASSWORD }} + - name: Build SAI-Challenger client docker image + run: DOCKER_FLAGS=$docker_fg_flags make docker-saichallenger-client + - name: Publish SAI-Challenger client docker image + run: make docker-publish-saichallenger-client \ No newline at end of file diff --git a/.github/workflows/dash-saichallenger-client-docker.yml b/.github/workflows/dash-saichallenger-client-docker.yml new file mode 100644 index 000000000..77ce64d02 --- /dev/null +++ b/.github/workflows/dash-saichallenger-client-docker.yml @@ -0,0 +1,45 @@ +name: DASH-docker-saichallenger-client-img + +on: + push: + branches: [ "**" ] + paths: + - '.github/workflows/dash-saichallenger-client-docker.yml' + - 'dash-pipeline/Makefile' + - 'dash-pipeline/dockerfiles/Dockerfile.saichallenger-client' + - 'dash-pipeline/.dockerignore' + - 'dash-pipeline/dockerfiles/.dockerignore' + pull_request: + branches: [ "**" ] + paths: + - '.github/workflows/dash-saichallenger-client-docker.yml' + - 'dash-pipeline/Makefile' + - 'dash-pipeline/dockerfiles/Dockerfile.saichallenger-client' + - 'dash-pipeline/.dockerignore' + - 'dash-pipeline/dockerfiles/.dockerignore' + workflow_dispatch: + +jobs: + build: + name: Build dash-saichallenger-client-image + runs-on: ubuntu-20.04 + env: + docker_fg_flags: -u root --privileged + docker_bg_flags: -d -u root --privileged + defaults: + run: + working-directory: ./dash-pipeline + steps: + - uses: actions/checkout@v3 + - name: Pull docker p4c image + run: make docker-pull-dash-p4c + - name: Build P4 software switch (bmv2) and P4Info + run: DOCKER_FLAGS=$docker_fg_flags make p4 + - name: Install SAI submodule + run: git submodule update --init + - name: Generate SAI API + run: DOCKER_FLAGS=$docker_fg_flags make sai + - name: Generate SAI-Thrift client and server code and libs + run: DOCKER_FLAGS=$docker_fg_flags make saithrift-server + - name: Build SAI-Challenger client docker image + run: DOCKER_FLAGS=$docker_fg_flags make docker-saichallenger-client diff --git a/.github/workflows/dash-saithrift-client-bldr-docker-acr.yml b/.github/workflows/dash-saithrift-client-bldr-docker-acr.yml index eac589a5c..30b3244cf 100644 --- a/.github/workflows/dash-saithrift-client-bldr-docker-acr.yml +++ b/.github/workflows/dash-saithrift-client-bldr-docker-acr.yml @@ -49,8 +49,8 @@ jobs: with: login-server: sonicdash.azurecr.io username: ${{ secrets.DASH_ACR_USERNAME }} - password: ${{ secrets.DASH_ACR_PASSWORD }} - - name: Build dash-saithrift-client-bldr image + password: ${{ secrets.DASH_ACR_PASSWORD }} + - name: Build dash-saithrift-client-bldr image run: DOCKER_FLAGS=$docker_fg_flags make docker-saithrift-client-bldr - name: Publish dash-saithrift-client-bldr docker image run: make docker-publish-saithrift-client-bldr diff --git a/.gitmodules b/.gitmodules index cb32895b5..f10d24641 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,7 @@ path = dash-pipeline/SAI/SAI url = https://github.com/reshmaintel/SAI.git branch = dash-ptf-ci +[submodule "test/SAI-Challenger"] + path = test/SAI-Challenger + url = https://github.com/opencomputeproject/SAI-Challenger + branch = multiple-api-support diff --git a/.wordlist.txt b/.wordlist.txt index 5283022a4..f080de187 100644 --- a/.wordlist.txt +++ b/.wordlist.txt @@ -296,6 +296,7 @@ LLDP lldp loadbalancer lookups +LOOKUPs loopback LPM lts @@ -304,6 +305,7 @@ makefile Makefile Makefiles makefiles +MAPs MatchedHalfOpenFlow MatchedOtherFlow MatchedTcpFlow @@ -421,6 +423,7 @@ qos Radv rdpty reachability +reconvergence README READMEs README's @@ -452,12 +455,14 @@ runtime rx SAI sai +saigen sairedis SAIRPC saiserver saithrift sata scalability +scalable Scalable scapy Schemas diff --git a/dash-pipeline/Makefile b/dash-pipeline/Makefile index 4ba85015c..fd0590b8a 100644 --- a/dash-pipeline/Makefile +++ b/dash-pipeline/Makefile @@ -6,7 +6,7 @@ mkfile_dir := $(dir $(mkfile_path)) # "All" type targets for convenience all:p4 sai saithrift-server docker-saithrift-client test -run-all-tests:run-libsai-test run-saithrift-client-tests +run-all-tests:run-libsai-test run-saithrift-client-tests run-saichallenger-tests run-saithrift-client-tests: run-saithrift-ptftests run-saithrift-pytests run-saithrift-client-dev-tests: run-saithrift-dev-ptftests run-saithrift-dev-pytests @@ -14,7 +14,7 @@ run-saithrift-client-dev-tests: run-saithrift-dev-ptftests run-saithrift-dev-pyt clean: kill-all p4-clean sai-clean test-clean network-clean saithrift-server-clean rm -rf $(P4_OUTDIR) -kill-all: kill-saithrift-server kill-switch undeploy-ixiac +kill-all: kill-saithrift-server kill-switch undeploy-ixiac kill-saichallenger-client PWD := $(realpath $(mkfile_dir)) DASH_USER ?=dashuser @@ -49,6 +49,10 @@ include dockerfiles/DOCKER_SAITHRIFT_CLIENT_BLDR_IMG.env # TODO: consider some other tagging scheme DOCKER_SAITHRIFT_CLIENT_IMG ?= local/dash-saithrift-client:latest +# Base image with test frameworks, DASH client libs not installed +# include file defines DOCKER_SAI_CHALLENGER_CLIENT_IMG +include dockerfiles/DOCKER_SAI_CHALLENGER_CLIENT_IMG.env + # Set differently in CI scripts as needed, e.g. run switch container in -d mode DOCKER_FLAGS ?=-it @@ -122,7 +126,7 @@ DOCKER_RUN_SAITHRIFT_BLDR =\ sai: sai-clean sai-headers sai-meta libsai -sai-headers: p4 | SAI/SAI +sai-headers: p4 | SAI/SAI @echo "Generate SAI library headers and implementation..." mkdir -p SAI/lib && chmod -R o+w SAI && \ $(DOCKER_RUN) \ @@ -131,7 +135,7 @@ sai-headers: p4 | SAI/SAI -w /SAI $(DOCKER_SAITHRIFT_BLDR_IMG) \ ./generate_dash_api.sh -sai-meta: +sai-meta: @echo "Generate SAI metadata..." # hack - remove scripts which cause Git ownership failures in CI pipelines # We don't need them, they're to check that SAI headers didn't experience enum changes etc. @@ -146,7 +150,7 @@ sai-meta: make # TODO - add SAI header dependencies -libsai: +libsai: @echo "build libsai.so..." $(DOCKER_RUN) \ $(DOCKER_FLAGS) \ @@ -155,7 +159,7 @@ libsai: $(DOCKER_BMV2_BLDR_IMG) \ make -libsai-clean: +libsai-clean: -rm -rf SAI/lib/* .PHONY:sai-clean @@ -354,7 +358,7 @@ docker-publish-saithrift-client-bldr: docker push $(DOCKER_SAITHRIFT_CLIENT_BLDR_IMG) ############################### - + # Client image, rebuild any time SAI interface changes # TODO - add sai header (inc/ and experimental) dependencies docker-saithrift-client: @@ -512,6 +516,73 @@ deploy-ixiac: undeploy-ixiac: cd ../test/third-party/traffic_gen && ./undeploy_ixiac.sh +############################### +# SAI-CHALLENGER TARGETS +############################### + +SAI_CHALLENGER_PATH = $(PWD)/../test/SAI-Challenger + +CONTAINER_SAI_CHALLENGER_CLIENT_NAME = dash-saichallenger-client-$(USER) + +# Add passing parameters to the run-saichallenger-tests target. +ifeq (run-saichallenger-tests,$(firstword $(MAKECMDGOALS))) + SAI_CHALLENGER_SETUP_FILE := $(wordlist 2, 2, $(MAKECMDGOALS)) + SAI_CHALLENGER_TEST := $(wordlist 3, 3, $(MAKECMDGOALS)) + $(eval $(SAI_CHALLENGER_SETUP_FILE):;@:) + $(eval $(SAI_CHALLENGER_TEST):;@:) +endif +# If setup file's isn't passed, set it to the default. +ifeq ($(SAI_CHALLENGER_SETUP_FILE),) +SAI_CHALLENGER_SETUP_FILE := sai_dpu_client_server_snappi.json +endif +# If test's name isn't passed, set it to the default. +ifeq ($(SAI_CHALLENGER_TEST),) +SAI_CHALLENGER_TEST := test_sai_vnet_*.py +endif + +docker-saichallenger-client: + cd $(SAI_CHALLENGER_PATH) && ./build.sh -i client + docker build \ + -f dockerfiles/Dockerfile.saichallenger-client \ + -t $(DOCKER_SAI_CHALLENGER_CLIENT_IMG) \ + . + +docker-publish-saichallenger-client: + @echo "Publish $(DOCKER_SAI_CHALLENGER_CLIENT_IMG) - requires credentials, can only do from DASH repo, not a fork" + docker push $(DOCKER_SAI_CHALLENGER_CLIENT_IMG) + +DOCKER_RUN_SAI_CHALLENGER_CLIENT=docker run \ + -v $(SAI_CHALLENGER_PATH):/sai-challenger \ + -v $(SAI_CHALLENGER_PATH)/../test-cases/scale/saic/:/sai-challenger/dash_tests \ + -v $(PWD)/../:/dash \ + --cap-add=NET_ADMIN \ + --device /dev/net/tun:/dev/net/tun \ + --rm \ + --network=host \ + --name $(CONTAINER_SAI_CHALLENGER_CLIENT_NAME) + +run-saichallenger-client: deploy-ixiac + $(DOCKER_RUN_SAI_CHALLENGER_CLIENT) \ + -d \ + --name $(CONTAINER_SAI_CHALLENGER_CLIENT_NAME) \ + $(DOCKER_SAI_CHALLENGER_CLIENT_IMG) + +run-saichallenger-client-bash: deploy-ixiac + $(DOCKER_RUN_SAI_CHALLENGER_CLIENT) \ + -w /sai-challenger/dash_tests \ + $(DOCKER_FLAGS) \ + $(DOCKER_SAI_CHALLENGER_CLIENT_IMG) \ + /bin/bash + +kill-saichallenger-client: + -docker kill $(CONTAINER_SAI_CHALLENGER_CLIENT_NAME) + +run-saichallenger-tests: deploy-ixiac + $(DOCKER_RUN_SAI_CHALLENGER_CLIENT) \ + -w /sai-challenger/dash_tests \ + $(DOCKER_FLAGS) \ + $(DOCKER_SAI_CHALLENGER_CLIENT_IMG) \ + ./run_vnet_tests.sh --setup=$(SAI_CHALLENGER_SETUP_FILE) $(SAI_CHALLENGER_TEST) ############################### # ENVIRONMENT SETUP TARGETS diff --git a/dash-pipeline/SAI/templates/utils.cpp.j2 b/dash-pipeline/SAI/templates/utils.cpp.j2 index 76e2fff77..93b2c6188 100644 --- a/dash-pipeline/SAI/templates/utils.cpp.j2 +++ b/dash-pipeline/SAI/templates/utils.cpp.j2 @@ -217,20 +217,77 @@ sai_status_t sai_create_switch_dummy( return SAI_STATUS_SUCCESS; } -sai_status_t sai_get_switch_attribute_dummy( +#define DASH_BMV2_NUM_PORTS 2 +#define DASH_BMV2_DEFAULT_VLAN_ID (sai_object_id_t)((SAI_OBJECT_TYPE_VLAN<<48)+1) +#define DASH_BMV2_DEFAULT_VRF_ID (sai_object_id_t)((SAI_OBJECT_TYPE_VIRTUAL_ROUTER<<48)+1) +#define DASH_BMV2_DEFAULT_1Q_BRIDGE_ID (sai_object_id_t)((SAI_OBJECT_TYPE_BRIDGE<<48)+1) + +sai_object_id_t port_list[DASH_BMV2_NUM_PORTS] = { + (sai_object_id_t)((SAI_OBJECT_TYPE_PORT<<48) + 1), + (sai_object_id_t)((SAI_OBJECT_TYPE_PORT<<48) + 2) +}; + +sai_status_t sai_get_switch_attribute( _In_ sai_object_id_t switch_id, _In_ uint32_t attr_count, _Inout_ sai_attribute_t *attr_list) { - fprintf(stderr, "sai_get_switch_attribute_dummy()\n"); - return SAI_STATUS_SUCCESS; + fprintf(stderr, "sai_get_switch_attribute()\n"); + int i; + sai_attribute_t *attr = attr_list; + sai_object_list_t port_obj_list; + sai_object_id_t *objlist; + for (i = 0; i < attr_count ; i++, attr++) { + switch(attr->id) { + + case SAI_SWITCH_ATTR_NUMBER_OF_ACTIVE_PORTS: + attr->value.u32 = DASH_BMV2_NUM_PORTS; + fprintf(stderr, " sai_get_switch_attribute() [%d] attr %d SAI_SWITCH_ATTR_NUMBER_OF_ACTIVE_PORTS = %d\n", + i, attr->id, attr->value.u32); + return SAI_STATUS_SUCCESS; + + case SAI_SWITCH_ATTR_PORT_LIST: + // make a tmp port list, saiserver will free the memory + objlist = (sai_object_id_t *)malloc(sizeof(port_list)); + memcpy(objlist, port_list, sizeof(port_list)); + port_obj_list = { + .count = DASH_BMV2_NUM_PORTS, + .list = objlist + }; + attr->value.objlist = port_obj_list; + fprintf(stderr, " sai_get_switch_attribute() [%d] attr %d SAI_SWITCH_ATTR_PORT_LIST = [%d objids]\n", + i, attr->id, DASH_BMV2_NUM_PORTS); + return SAI_STATUS_SUCCESS; + + case SAI_SWITCH_ATTR_DEFAULT_VLAN_ID: + attr->value.oid = DASH_BMV2_DEFAULT_VLAN_ID; + fprintf(stderr, " sai_get_switch_attribute() [%d] attr %d SAI_SWITCH_ATTR_DEFAULT_VLAN_ID = %lx\n", + i, attr->id, attr->value.oid); + return SAI_STATUS_SUCCESS; + + case SAI_SWITCH_ATTR_DEFAULT_VIRTUAL_ROUTER_ID: + attr->value.oid = DASH_BMV2_DEFAULT_VRF_ID; + fprintf(stderr, " sai_get_switch_attribute() [0] attr %d SAI_SWITCH_ATTR_DEFAULT_VIRTUAL_ROUTER_ID = %lx\n", attr->id, attr->value.oid); + return SAI_STATUS_SUCCESS; + + case SAI_SWITCH_ATTR_DEFAULT_1Q_BRIDGE_ID: + attr->value.oid = DASH_BMV2_DEFAULT_1Q_BRIDGE_ID; + fprintf(stderr, " sai_get_switch_attribute() [0] attr %d SAI_SWITCH_ATTR_DEFAULT_1Q_BRIDGE_ID = %lx\n", attr->id, attr->value.oid); + return SAI_STATUS_SUCCESS; + + default: + fprintf(stderr, " sai_get_switch_attribute() [0] attr %d is NOT SUPPORTED - returning SAI_STATUS_SUCCESS anyway\n", attr->id); + return SAI_STATUS_NOT_SUPPORTED; + } + } + return SAI_STATUS_NOT_SUPPORTED; } sai_switch_api_t sai_switch_api_impl = { .create_switch = sai_create_switch_dummy, .remove_switch = (void *)0, .set_switch_attribute = (void *)0, - .get_switch_attribute = sai_get_switch_attribute_dummy, + .get_switch_attribute = sai_get_switch_attribute, .get_switch_stats = (void *)0, .get_switch_stats_ext = (void *)0, .clear_switch_stats = (void *)0, diff --git a/dash-pipeline/dockerfiles/DOCKER_SAI_CHALLENGER_CLIENT_IMG.env b/dash-pipeline/dockerfiles/DOCKER_SAI_CHALLENGER_CLIENT_IMG.env new file mode 100644 index 000000000..3f8abcca8 --- /dev/null +++ b/dash-pipeline/dockerfiles/DOCKER_SAI_CHALLENGER_CLIENT_IMG.env @@ -0,0 +1,4 @@ +# Define docker image repo/name:tag +# Changing this will cause build/publish to occur in CI actions +export DASH_ACR_REGISTRY=sonicdash.azurecr.io +export DOCKER_SAI_CHALLENGER_CLIENT_IMG=${DASH_ACR_REGISTRY}/dash-saichallenger-client:221104 diff --git a/dash-pipeline/dockerfiles/Dockerfile.saichallenger-client b/dash-pipeline/dockerfiles/Dockerfile.saichallenger-client new file mode 100644 index 000000000..251ae9be1 --- /dev/null +++ b/dash-pipeline/dockerfiles/Dockerfile.saichallenger-client @@ -0,0 +1,40 @@ +FROM sc-client + +ENV SAI_CHALLENGER_PATH /sai-challenger +ENV DASH_PATH /dash + +ADD tests/ /tests/ + +# Copy distro PTF submodule and tools from SAI repo +ADD SAI/SAI/test/ptf /SAI/test/ptf + +# Install PTF test framework & test-cases from SAI repo +ADD SAI/SAI/ptf /SAI/ptf/ + +# Copy thrift python distro +ADD SAI/rpc/thrift-0.11.0.tar.gz / + +# Copy autogenerated saithrift library built from SAI headers for DASH dataplane +ADD SAI/rpc/saithrift-0.9.tar.gz / + +# Install the python libraries +RUN python3 -m pip install -r /tests/requirements.txt && \ + pip3 install scapy \ + dpugen>=0.0.3 \ + pysubnettree \ + macaddress \ + munch && \ + cd /saithrift-0.9 && \ + python3 setup.py install && \ + cd / && \ + rm -rf saithrift-0.9 &&\ + cd thrift-0.11.0 && \ + python3 setup.py install && \ + cd / &&\ + rm -rf thrift-0.11.0 && \ + cd /SAI/test/ptf && \ + python3 setup.py install && \ + ln -s ${SAI_CHALLENGER_PATH} /usr/local/lib/python3.7/dist-packages/saichallenger && \ + ln -s ${DASH_PATH}/test/test-cases/scale/saic ${SAI_CHALLENGER_PATH}/dash_tests + +CMD ["/usr/bin/supervisord"] diff --git a/dash-pipeline/tests/saithrift/pytest/pytest.ini b/dash-pipeline/tests/saithrift/pytest/pytest.ini index 42107e96f..4443adf18 100644 --- a/dash-pipeline/tests/saithrift/pytest/pytest.ini +++ b/dash-pipeline/tests/saithrift/pytest/pytest.ini @@ -2,5 +2,6 @@ markers = bmv2: test DASH bmv2 model saithrift: test DASH using saithrift API + switch: test DASH SAI switch APIs vnet: test DASH vnet scenarios \ No newline at end of file diff --git a/dash-pipeline/tests/saithrift/pytest/switch/test_saithrift_switch.py b/dash-pipeline/tests/saithrift/pytest/switch/test_saithrift_switch.py index f36cb7012..5ef43afca 100644 --- a/dash-pipeline/tests/saithrift/pytest/switch/test_saithrift_switch.py +++ b/dash-pipeline/tests/saithrift/pytest/switch/test_saithrift_switch.py @@ -6,10 +6,32 @@ @pytest.mark.saithrift @pytest.mark.bmv2 -def test_sai_thrift_get_switch_attribute(saithrift_client): +@pytest.mark.switch +def test_sai_thrift_get_switch_attributes(saithrift_client): attr = sai_thrift_get_switch_attribute( saithrift_client, number_of_active_ports=True) - print ("switch_attributes = %s" % attr) - print ("test_sai_thrift_get_switch_attribute OK") + number_of_active_ports = attr['number_of_active_ports'] + print ("number_of_active_ports = %d" % number_of_active_ports) + assert(number_of_active_ports != 0) + attr = sai_thrift_get_switch_attribute( + saithrift_client, + port_list=sai_thrift_object_list_t(idlist=[], count=int(number_of_active_ports))) + assert(number_of_active_ports == attr['port_list'].count) + port_list = attr['port_list'].idlist + print ("port list = ", port_list) + + attr = sai_thrift_get_switch_attribute( + saithrift_client, default_vlan_id=True) + default_vlan_id = attr['default_vlan_id'] + print ("default_vlan_id = %d" % default_vlan_id) + assert(default_vlan_id != 0) + + attr = sai_thrift_get_switch_attribute( + saithrift_client, default_virtual_router_id=True) + default_virtual_router_id = attr['default_virtual_router_id'] + print ("default_virtual_router_id = %d" % default_virtual_router_id) + assert(default_virtual_router_id != 0) + + print ("test_sai_thrift_get_switch_attribute OK") \ No newline at end of file diff --git a/test/SAI-Challenger b/test/SAI-Challenger new file mode 160000 index 000000000..e1f1d5bac --- /dev/null +++ b/test/SAI-Challenger @@ -0,0 +1 @@ +Subproject commit e1f1d5bac518f92dae4d0700931e65a280c7bb99 diff --git a/test/docs/README.md b/test/docs/README.md index c63dbe53a..e88335a61 100644 --- a/test/docs/README.md +++ b/test/docs/README.md @@ -2,13 +2,14 @@ | Document | Description | |----------|-------------| -| [High-Level Description (HLD) Test Specification](dash-test-HLD.md) | High-level design for the testing of devices which conform to the SONiC-DASH requirements.| -| [Dash Test Maturity Stages](dash-test-maturity-stages.md) | Describes a progressive approach to DASH testing.| +| [High-Level Description (HLD) Test Specification](dash-test-HLD.md) | High-level design for the testing of devices which conform to the SONiC-DASH requirements.| +| [Dash Test Maturity Stages](dash-test-maturity-stages.md) | Describes a progressive approach to DASH testing.| | [DASH SAI-Thrift Test Workflow](dash-test-workflow-saithrift.md) | DASH test workflow with SAI-thrift. | | [SAI PTF Design](https://github.com/reshmaintel/SAI/blob/dash-ptf/doc/SAI-Proposal-SAI-PTF.md) | SAI Thrift auto-generated Python based testing framework doc. | | [SAI PTF User Guide](https://github.com/opencomputeproject/SAI/blob/master/ptf/SAI_PTF_user-guide.md) | SAI Thrift Server User Guide to autogenerate test frame work. | | [DASH P4 SAI-Thrift Test Workflow](dash-test-workflow-p4-saithrift.md) | Use of P4-based simulators or SW data planes to verify DASH behavior, using saithrift API. | -| [Testbed](testbed/README.md) | Describes the setup and configuration of a DASH testbed.| +| [Testbed](testbed/README.md) | Describes the setup and configuration of a DASH testbed.| +| [snappi and SAI-Challenger based tests](dash-test-sai-challenger.md) | How to run scalable tests using SAI-Challenger and snappi. The scalability is achieved with additional DASH/SAI abstraction level in test code to simplify high scale DUT configuration. | -You can start with the [High-Level Description (HLD) Test Specification](dash-test-HLD.md). +You can start with the [High-Level Description (HLD) Test Specification](dash-test-HLD.md). diff --git a/test/docs/dash-test-sai-challenger.md b/test/docs/dash-test-sai-challenger.md new file mode 100644 index 000000000..910d69e95 --- /dev/null +++ b/test/docs/dash-test-sai-challenger.md @@ -0,0 +1,94 @@ +# General changes +* Added [SAI-Challenger](https://github.com/opencomputeproject/SAI-Challenger.OCP) submodule by path: `DASH/test/SAI-Challenger`. +* Added test cases for SAI-Challenger by path: `DASH/test/test-cases/scale/saic` + +# New make targets: +**`docker-saichallenger-client`**: Build SAI-Challenger docker image and docker image based on SAI-Challenger client docker image with sai_thrift, saigen and DASH files. + +**`run-saichallenger-client`**: Start Ixia-C and docker container `sc-client-thrift-run` from image built on `docker-saichallenger-client` target. To the original SAI-Challenger tests (`DASH/test/SAI-Challenger/tests`) folder a new folder `dash_tests` mounted from `DASH/test/test-cases/scale/saic` folder inside of container. Bound mount volume with DASH folder. + +**`kill-saichallenger-client`**: Stop Ixia-C and `sc-client-thrift-run` container. + +**`run-saichallenger-tests`**: Run test manually. This target may be triggered with passing parameters, or with default parameters. +Run with default parameters(Setup file: `sai_dpu_client_server_snappi.json`; Test: `test_sai_vnet_*.py`): +``` +make run-saichallenger-tests +``` + +Run with setup parameter and default test parameter (All tests): +``` +make run-saichallenger-tests +``` + +Run with setup parameter and test parameter: +``` +make run-saichallenger-tests +``` + +# How to start + +## Environment +Install dependencies listed [**here**](../../dash-pipeline/README.md#prerequisites). + +## Prepare repository +``` +git clone https://github.com/PLVision/DASH.git +cd DASH && git checkout test-framework-extension +git submodule update --init --recursive +``` + +## Build environment +``` +cd dash-pipeline +make clean ;# skip on a fresh setup as it will fail +make all + +pwd +``` + +## Start environment +Run in the 3 separate windows/tabs. +- take the output of `pwd` from previous step and do `cd ` in each window +- window 1: `make run-switch` +- window 2: `make run-saithrift-server` +- window 3: will be used to run the test as per instructions bellow + +## Run tests manually + +### Using make target +Run all available VNET tests: +```sh +make run-saichallenger-tests +``` + +Run tests in DASH configuration format with the custom options: +```sh +make run-saichallenger-tests sai_dpu_client_server_snappi.json test_sai_vnet_inbound.py +make run-saichallenger-tests sai_dpu_client_server_snappi.json test_sai_vnet_outbound.py +``` + +Run tests in SAI configuration format with custom options: +```sh +make run-saichallenger-tests sai_dpu_client_server_snappi.json test_vnet_inbound.py +make run-saichallenger-tests sai_dpu_client_server_snappi.json test_vnet_outbound.py +``` + +### Manually from the docker (developers mode) +Run the `dash-saichallenger-client-$USER` container. +```sh +make run-saichallenger-client-bash +``` + +And execute tests in DASH configuration format (inside the container): +```sh +pytest -sv --setup=sai_dpu_client_server_snappi.json test_sai_vnet_inbound.py +pytest -sv --setup=sai_dpu_client_server_snappi.json test_sai_vnet_outbound.py +``` + +Or in SAI configuration format: +```sh +pytest -sv --setup=sai_dpu_client_server_snappi.json test_vnet_inbound.py +pytest -sv --setup=sai_dpu_client_server_snappi.json test_vnet_outbound.py +``` + +# Known issues diff --git a/test/test-cases/scale/saic/README.md b/test/test-cases/scale/saic/README.md new file mode 100644 index 000000000..da69cdc66 --- /dev/null +++ b/test/test-cases/scale/saic/README.md @@ -0,0 +1,550 @@ + +# DASH configuration specification + +1. [Generator input format](#generator-input-format) + - [Sequence generator](#sequence-generator) + - [Example of generator output using scale configuration](#example-of-generator-output-using-scale-configuration) + - [Known issues](#known-issues) +1. [Generator output format (Unified SAI format)](#generator-output-format-unified-sai-format) + - [General description](#general-description) + - [Unified SAI commands examples](#unified-sai-commands-examples) +1. [Traffic scaling](#traffic-scaling) + - [VNET Outbound Routing scenario explanation](#vnet-outbound-routing-scenario-explanation) + - [DASH and traffic configuration visualization](#dash-and-traffic-configuration-visualization) + - [Resulting traffic configuration examples](#resulting-traffic-configuration-examples) + + +# Generator input format + +In general a DASH config consists of such entries: + +``` +DASH_OBJECT_TYPE: + object_name: + count: NUMBER_OF_OBJECTS + attribute_name: CONST_OR_GENERATOR +``` + +Where, +* `DASH_OBJECT_TYPE` - Can be **DASH_VNET**, **DASH_VIP**, **DASH_ENI**, etc. +* `object_name` - Name of the object +* `NUMBER_OF_OBJECTS` - Number of such objects to create +* `attribute_name` - Specific attributes of every object. Depend on `DASH_OBJECT_TYPE`. +* `CONST_OR_GENERATOR` - The value of the attribute. Could be a constant value or a sequence generator expression. + +If `NUMBER_OF_OBJECTS` is greater than the number of values generated by the sequence generator - after reaching the last value generator must produce values from the start again. + +If number of values generated by the sequence generator is greater than `NUMBER_OF_OBJECTS` - only first `NUMBER_OF_OBJECTS` values of the sequence generator should be used. + +## Sequence generator + +For scaling purposes sequence generator could be used for creating big number of objects. It produces a range of values according to the given attributes. + +### Sequence generator attributes: + +* `count` - The number of entries (default is 1) +* `start` - The first entry (no default values) +* `step` - Increment step (default is 1) +* `delay`[1](#known-issues) - Delay increment for `delay` iterations (default is 1, which means increment at every iteration) +* `list` - Use this specific list of values instead of the generated values. If this attribute is present `start`, `step`, `delay` must be ignored. `count` still will be used. + +### Sequence generator expressions examples: + +#### Integers + +```JSON +{ count: 4, start: 100, step: 10 } => 100, 110, 120, 130 +{ count: 4, start: 100, step: 5 } => 100, 105, 110, 115 +{ count: 4, start: 100, step: 10, delay: 2} => 100, 100, 105, 105 +{ count: 4, start: 100 } => 100, 101, 102, 103 +{ start: 100 } => 100 +``` + +#### IP-addresses and IP-prefixes + +```JSON +{ count: 4, start: "10.0.0.1", step: "0.0.0.1" } => "10.0.0.1", "10.0.0.2", "10.0.0.3", "10.0.0.4" +{ count: 4, start: "10.0.0.1", step: "0.0.1.0" } => "10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1" +{ count: 4, start: "10.0.0.1", step: "0.0.1.0", delay: 2 } => "10.0.0.1", "10.0.0.1", "10.0.1.1", "10.0.1.1" +{ count: 4, start: "10.0.0.1" } => "10.0.0.1", "10.0.0.2", "10.0.0.3", "10.0.0.4" +{ start: "10.0.0.1" } => "10.0.0.1" + +{ count: 4, start: "10.0.0.0/24", step: "0.0.1.0" } => "10.0.0.0/24", "10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24" +{ count: 4, start: "10.0.0.0/24", step: "0.1.1.0" } => "10.0.0.0/24", "10.1.1.0/24", "10.2.2.0/24", "10.3.3.0/24" +{ count: 4, start: "10.0.0.0/24", step: "0.1.1.0", delay: 2 } => "10.0.0.0/24", "10.0.0.0/24", "10.2.2.0/24", "10.2.2.0/24" +{ start: "10.0.0.1/24" } => "10.0.0.1/24" +``` + +#### MAC-addresses + +```JSON +{ count: 4, start: "00:CC:CC:CC:CC:00", step: "00:00:00:00:00:01" } => "00:CC:CC:CC:CC:00", "00:CC:CC:CC:CC:01", "00:CC:CC:CC:CC:02", "00:CC:CC:CC:CC:03" +{ count: 4, start: "00:CC:CC:CC:CC:00", step: "00:00:00:00:00:10" } => "00:CC:CC:CC:CC:00", "00:CC:CC:CC:CC:10", "00:CC:CC:CC:CC:20", "00:CC:CC:CC:CC:30" +{ count: 4, start: "00:CC:CC:CC:CC:00", step: "00:00:00:00:00:10", delay: 2 } => "00:CC:CC:CC:CC:00", "00:CC:CC:CC:CC:00", "00:CC:CC:CC:CC:10", "00:CC:CC:CC:CC:10" +{ count: 4, start: "00:CC:CC:CC:CC:00" } => "00:CC:CC:CC:CC:00", "00:CC:CC:CC:CC:01", "00:CC:CC:CC:CC:02", "00:CC:CC:CC:CC:03" +{ start: "00:CC:CC:CC:CC:00" } => "00:CC:CC:CC:CC:00" +``` + +#### Object IDs + +```JSON +{ count: 4, start: "DASH_VNET#vnet#0", step: 1 } => "$DASH_VNET#vnet#0", "$DASH_VNET#vnet#1", "$DASH_VNET#vnet#2", "$DASH_VNET#vnet#3" +{ count: 4, start: "DASH_VNET#vnet#0", step: 2 } => "$DASH_VNET#vnet#0", "$DASH_VNET#vnet#2", "$DASH_VNET#vnet#4", "$DASH_VNET#vnet#6" +{ count: 4, start: "DASH_VNET#vnet#0", step: 2, delay: 2} => "$DASH_VNET#vnet#0", "$DASH_VNET#vnet#0", "$DASH_VNET#vnet#2", "$DASH_VNET#vnet#2" +{ count: 4, start: "DASH_VNET#vnet#0"} => "$DASH_VNET#vnet#0", "$DASH_VNET#vnet#1", "$DASH_VNET#vnet#2", "$DASH_VNET#vnet#3" +{ start: "DASH_VNET#vnet#0"} => "$DASH_VNET#vnet#0" +``` + +#### Lists + +```JSON +{ count: 4, start: 100, step: 10, list: [3, 5, 7, 9] } => 3, 5, 7, 9 +{ count: 4, list: [3, 5, 7, 9] } => 3, 5, 7, 9 +{ count: 2, list: [3, 5, 7, 9] } => 3, 5 +{ count: 6, list: [3, 5, 7, 9] } => 3, 5, 7, 9, 3, 5 +``` + +## Example of generator output using scale configuration + +Here is an example of DASH_OUTBOUND_ROUTING scale configuration using DASH config + +### Input: + +```yaml +NUMBER_OF_ORE: 5 +NUMBER_OF_ENI: 4 +NUMBER_OF_DST: 3 +NUMBER_OF_VNET: 10 + +DASH_OUTBOUND_ROUTING: + ore: + count: NUMBER_OF_ORE + SWITCH_ID: $SWITCH_ID + ENI_ID: + count: NUMBER_OF_ENI + start: "$eni_#0" + delay: NUMBER_OF_DST + DESTINATION: + count: NUMBER_OF_DST + start: "10.1.0.0/24" + step: "0.0.1.0" + ACTION: ROUTE_VNET + DST_VNET_ID: + count: NUMBER_OF_VNET + start: "$vnet_#0" +``` + +### Output: + +```json + { + "name": "DASH_OUTBOUND_ROUTING#ore#0", + "op": "create", + "type": "SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "eni_id": "$DASH_ENI#eni#0", + "destination": "10.1.0.0/24" + }, + "attributes": [ + "SAI_OUTBOUND_ROUTING_ENTRY_ATTR_ACTION", "SAI_OUTBOUND_ROUTING_ENTRY_ACTION_ROUTE_VNET", + "SAI_OUTBOUND_ROUTING_ENTRY_ATTR_DST_VNET_ID", "$DASH_VNET#vnet#0" + ] + }, + { + "name": "DASH_OUTBOUND_ROUTING#ore#1", + "op": "create", + "type": "SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "eni_id": "$DASH_ENI#eni#0", + "destination": "10.1.1.0/24" + }, + "attributes": [ + "SAI_OUTBOUND_ROUTING_ENTRY_ATTR_ACTION", "SAI_OUTBOUND_ROUTING_ENTRY_ACTION_ROUTE_VNET", + "SAI_OUTBOUND_ROUTING_ENTRY_ATTR_DST_VNET_ID", "$DASH_VNET#vnet#1" + ] + }, + { + "name": "DASH_OUTBOUND_ROUTING#ore#2", + "op": "create", + "type": "SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "eni_id": "$DASH_ENI#eni#0", + "destination": "10.1.2.0/24" + }, + "attributes": [ + "SAI_OUTBOUND_ROUTING_ENTRY_ATTR_ACTION", "SAI_OUTBOUND_ROUTING_ENTRY_ACTION_ROUTE_VNET", + "SAI_OUTBOUND_ROUTING_ENTRY_ATTR_DST_VNET_ID", "$DASH_VNET#vnet#2" + ] + }, + { + "name": "DASH_OUTBOUND_ROUTING#ore#3", + "op": "create", + "type": "SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "eni_id": "$DASH_ENI#eni#1", + "destination": "10.0.1.0/24" + }, + "attributes": [ + "SAI_OUTBOUND_ROUTING_ENTRY_ATTR_ACTION", "SAI_OUTBOUND_ROUTING_ENTRY_ACTION_ROUTE_VNET", + "SAI_OUTBOUND_ROUTING_ENTRY_ATTR_DST_VNET_ID", "$DASH_VNET#vnet#3" + ] + }, + { + "name": "DASH_OUTBOUND_ROUTING#ore#4", + "op": "create", + "type": "SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "eni_id": "$DASH_ENI#eni#1", + "destination": "10.1.1.0/24" + }, + "attributes": [ + "SAI_OUTBOUND_ROUTING_ENTRY_ATTR_ACTION", "SAI_OUTBOUND_ROUTING_ENTRY_ACTION_ROUTE_VNET", + "SAI_OUTBOUND_ROUTING_ENTRY_ATTR_DST_VNET_ID", "$DASH_VNET#vnet#4" + ] + } +``` + +## Outbound DASH config example + +```YAML +NUMBER_OF_VIP: 3 +NUMBER_OF_DLE: 3 +NUMBER_OF_IN_ACL_GROUP: 10 +NUMBER_OF_OUT_ACL_GROUP: 10 +NUMBER_OF_VNET: 50 +NUMBER_OF_ENI: 10 +NUMBER_OF_EAM: NUMBER_OF_ENI * 2 +NUMBER_OF_ORE: 5 +NUMBER_OF_DST: 10 +NUMBER_OF_OCPE: 5 + +DASH_VIP: + vpe: NUMBER_OF_VIP + SWITCH_ID: $SWITCH_ID + IPv4: { count: NUMBER_OF_VIP, start: "172.1.0.100", step: "0.1.0.0" } + +DASH_DIRECTION_LOOKUP: + dle: NUMBER_OF_DLE + SWITCH_ID: $SWITCH_ID + VNI: { count: NUMBER_OF_DLE, start: 100, step: 100 } + ACTION: SET_OUTBOUND_DIRECTION + +DASH_ACL_GROUP: + in_acl_group_id: NUMBER_OF_IN_ACL_GROUP + ADDR_FAMILY: IPv4 + out_acl_group_id: NUMBER_OF_OUT_ACL_GROUP + ADDR_FAMILY: IPv4 + +DASH_VNET: + vnet: NUMBER_OF_VNET + VNI: { count: NUMBER_OF_VNET, start: 1000, step: 10 } + +DASH_ENI: + eni: NUMBER_OF_ENI + ACL_GROUP: + INBOUND: + STAGE1: { list: "DASH_ACL_GROUP#in_acl_group_id#0" } + STAGE2: { list: "DASH_ACL_GROUP#in_acl_group_id#0" } + STAGE3: { list: "DASH_ACL_GROUP#in_acl_group_id#0" } + STAGE4: { list: "DASH_ACL_GROUP#in_acl_group_id#0" } + STAGE5: { list: "DASH_ACL_GROUP#in_acl_group_id#0" } + OUTBOUND: + STAGE1: 0 + STAGE2: 0 + STAGE3: 0 + STAGE4: 0 + STAGE5: 0 + ADMIN_STATE: True + CPS: 10000 + FLOWS: 10000 + PPS: 100000 + VM_UNDERLAY_DIP: { count: NUMBER_OF_ENI, start: "172.16.1.0", step: "0.0.1.0" } + VM_VNI: { count: NUMBER_OF_ENI, start: 50 } + VNET_ID: { count: NUMBER_OF_ENI, start: "DASH_VNET#vnet#0" } + +DASH_ENI_ETHER_ADDRESS_MAP: + eam: NUMBER_OF_EAM + SWITCH_ID: $SWITCH_ID + MAC: { count: NUMBER_OF_EAM, start: "00:CC:CC:CC:00:00" } + ENI_ID: { count: NUMBER_OF_ENI, start: "DASH_ENI#eni#0" } + +DASH_OUTBOUND_ROUTING: + ore: NUMBER_OF_ORE + SWITCH_ID: $SWITCH_ID + ENI_ID: { count: NUMBER_OF_ENI, start: "DASH_ENI#eni#0", delay: NUMBER_OF_DST } + DESTINATION: { count: NUMBER_OF_DST, start: "10.1.0.0/24", step: "0.0.1.0" } + ACTION: ROUTE_VNET + DST_VNET_ID: { count: NUMBER_OF_VNET, start: "DASH_VNET#vnet#0" } + +DASH_OUTBOUND_CA_TO_PA: + ocpe: NUMBER_OF_OCPE + SWITCH_ID: $SWITCH_ID + DST_VNET_ID: { count: NUMBER_OF_VNET, start: "DASH_VNET#vnet#0" } + DIP: { count: NUMBER_OF_OCPE, start: "10.1.2.50", step: "0.0.1.0" } + UNDERLAY_DIP: { count: NUMBER_OF_OCPE, start: "172.16.1.20", step: "0.0.1.0" } + OVERLAY_DMAC: { count: NUMBER_OF_OCPE, start: "00:DD:DD:DD:00:00" } + USE_DST_VNET_VNI: True +``` + + +## Known issues + +1. "duration" key might be replaced with nested loops approach. + +# Generator output format (Unified SAI format) + +## General description + +Config generator generates a list of commands that should be applied to SAI. These commands consist of the following fields: + +- `op` - operation. (mandatory) +- `type` - object type. See *sai_object_type_t*. (mandatory when op is "create" or "bulk_create") +- `name` - name of the object. (mandatory when "op" is "remove", "bulk_remove" or "set") +- `key` - key of the specific object. Not all objects have key. See SAI headers for more information. (mandatory for some object types) +- `attributes` - a list of attributes and their values. See SAI headers for more information. (optional) + +Possible operations (`op`): +- create +- remove +- get +- set +- bulk_create *(currently not supported)* +- bulk_remove *(currently not supported)* + +If `op` is "bulk_create" or "bulk_remove" SAI expects `name` and `key` to be lists of values and `attributes` to be a list of lists. + +If you need to refer to some object in `attributes` or `key` you should use its name with a '$' sign at the beginning. Example: "$vnet". + +## Unified SAI commands examples + +Create an object without a key: + +```JSON + { + "op": "create", + "type": "SAI_OBJECT_TYPE_VNET", + "name": "vnet", + "attributes": [ + "SAI_VNET_ATTR_VNI", "100" + ] + } +``` + +Create an object with key: + +```JSON + { + "op": "create", + "type": "SAI_OBJECT_TYPE_VIP_ENTRY", + "name": "vpe", + "key": { + "switch_id": "$SWITCH_ID", + "vip": "172.16.1.100" + }, + "attributes": [ + "SAI_VIP_ENTRY_ATTR_ACTION", "SAI_VIP_ENTRY_ACTION_ACCEPT" + ] + } +``` + +Remove object: +```JSON + { + "name": "vnet", + "op": "remove" + } +``` +NOTE: For removal it's enough to provide only name and skip attributes. + +Bulk create: +```JSON + { + "op": "bulk_create", + "type": "SAI_OBJECT_TYPE_VIP_ENTRY", + "name": [ "vpe#0", "vpe#1" ], + "key": [ + { "switch_id": "$SWITCH_ID", + "vip": "172.16.1.100" }, + { "switch_id": "$SWITCH_ID", + "vip": "172.16.1.200" } + ], + "attributes": [ + [ "SAI_VIP_ENTRY_ATTR_ACTION", "SAI_VIP_ENTRY_ACTION_ACCEPT" ], + [ "SAI_VIP_ENTRY_ATTR_ACTION", "SAI_VIP_ENTRY_ACTION_ACCEPT" ] + ] + } +``` + +Bulk remove: +```JSON + { + "name": [ "vpe#0", "vpe#1" ], + "op": "bulk_remove" + } +``` + +# Traffic scaling + +**Scalable traffic configuration uses the same DASH config format and the same entities as it is described above.** + + +## VNET Outbound Routing scenario explanation + +The following picture explains: +1. Relations between packet and configuration parameters +1. Packet transformation + +![Outbound scenario explanation](images/outbound_scenario_explained.svg) + + +## DASH and traffic configuration visualization + +The following picture depicts configuration used in [test_sai_vnet_outbound_scale.py](./test_sai_vnet_outbound_scale.py) + +![Visualization of traffic scaling](images/vnet2vnet_outbound_traffic_scaling.svg) + + +For simplicity DIRECTION_LOOKUPs are mapped to ENIs as **one-to-one**. +ENIs are mapped to ENI_ETHER_ADDRESS_MAPs as one-to-one by design. + +VIPs are completely standalone entities. If user increases count of VIPs, the rest of configuration stays as is, but the traffic will by multiplied on number of underlay destination IPs. + +ENI scales to OUTBOUND_ROUTING in the ratio: two OUTBOUND_ROUTING entries per one ENI. + +OUTBOUND_ROUTING scales to OUTBOUND_CA_TO_PA in the ratio: two OUTBOUND_CA_TO_PA entries per one OUTBOUND_ROUTING. + + +### DASH configuration based on the picture above: +```python +NUMBER_OF_VIP = 1 +NUMBER_OF_DLE = 2 +NUMBER_OF_ENI = 2 +NUMBER_OF_EAM = NUMBER_OF_ENI +NUMBER_OF_ORE = 2 # Per ENI +NUMBER_OF_OCPE = 2 # Per ORE +NUMBER_OF_VNET = NUMBER_OF_ENI + (NUMBER_OF_ORE * NUMBER_OF_ENI) # Per ORE + +TEST_VNET_OUTBOUND_CONFIG_SCALE = { + + 'DASH_VIP': { + 'vpe': { + 'count': NUMBER_OF_VIP, + ... + } + }, + + 'DASH_DIRECTION_LOOKUP': { + 'dle': { + 'count': NUMBER_OF_DLE, + ... + } + }, + + 'DASH_VNET': { + 'vnet': { + 'VNI': { + 'count': NUMBER_OF_VNET, + ... + } + }, + + 'DASH_ENI': { + 'eni': { + 'count': NUMBER_OF_ENI, + ... + } + }, + + 'DASH_ENI_ETHER_ADDRESS_MAP': { + 'eam': { + 'count': NUMBER_OF_EAM, + ... + } + }, + + 'DASH_OUTBOUND_ROUTING': { + 'ore': { + 'count': NUMBER_OF_ENI * NUMBER_OF_ORE, # Full count: OREs per ENI and VNET + ... + } + }, + + 'DASH_OUTBOUND_CA_TO_PA': { + 'ocpe': { + 'count': (NUMBER_OF_ENI * NUMBER_OF_ORE) * NUMBER_OF_OCPE, # 2 Per ORE + ... + } + } +} +``` + +## Resulting traffic configuration examples + +These short snippets are applicable with [this config example](#dash-configuration-based-on-the-picture-above). + +#### **Example #1** + +Each DIRECTION_LOOKUP refers to a single ENI_ETHER_MAP. +```python +NUMBER_OF_DLE = 2 +NUMBER_OF_ENI = 2 +``` + +Resulting traffic flows configuration: +```YAML +VIP "172.16.1.100" + DIR_LOOKUP VNI 5000 + CA SMAC: "00:CC:CC:CC:00:00" + DIR_LOOKUP VNI 6000 + CA SMAC: "00:CC:CC:CC:00:01" +``` +This means that 2 flows will be send with the following properties: +1. Underlay DIP: 172.16.1.100, VNI: 5000, Overlay SRC MAC: "00:CC:CC:CC:00:00" +1. Underlay DIP: 172.16.1.100, VNI: 6000, Overlay SRC MAC: "00:CC:CC:CC:00:01" + +#### **Example #2** +Each DIRECTION_LOOKUP refers to two ENI_ETHER_MAP. +```python +NUMBER_OF_DLE = 2 +NUMBER_OF_ENI = 4 +``` + +Resulting traffic flows configuration (4 flows in total): +```YAML +VIP "172.16.1.100" + DIR_LOOKUP VNI 5000 + CA SMAC: "00:CC:CC:CC:00:00" + CA SMAC: "00:CC:CC:CC:00:01" + DIR_LOOKUP VNI 6000 + CA SMAC: "00:CC:CC:CC:00:02" + CA SMAC: "00:CC:CC:CC:00:03" +``` + +## **Example #3** + +According to the [scheme](#dash-and-traffic-configuration-visualization) +```python +NUMBER_OF_VIP = 1 +NUMBER_OF_DLE = 2 +NUMBER_OF_ENI = 2 +NUMBER_OF_EAM = NUMBER_OF_ENI +NUMBER_OF_ORE = 2 # Per ENI +NUMBER_OF_OCPE = 2 # Per ORE +NUMBER_OF_VNET = NUMBER_OF_ENI + (NUMBER_OF_ORE * NUMBER_OF_ENI) +``` + +Resulting traffic profile includes 2 flows each of 4 "sub-flows" for each possible CA DIP. +```YAML +VIP 172.16.1.100 + DIR_LOOKUP VNI 5000 + CA SMAC: 00:CC:CC:CC:00:00 + CA DIP 10.1.1.0, count: 4, step: 0.0.0.1 + DIR_LOOKUP VNI 6000 + CA SMAC: 00:CC:CC:CC:00:01 + CA DIP 10.1.1.0, count: 4, step: 0.0.0.1 +``` diff --git a/test/test-cases/scale/saic/__init__.py b/test/test-cases/scale/saic/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/test-cases/scale/saic/conftest.py b/test/test-cases/scale/saic/conftest.py new file mode 100644 index 000000000..42855c716 --- /dev/null +++ b/test/test-cases/scale/saic/conftest.py @@ -0,0 +1,82 @@ +import logging +import pytest + +from dpugen import saigen +from dpugen.saigen.confbase import ConfBase + +# import sys +# sys.path.insert(0, '/sai-challenger/common') # Needed for correct load_module + +from saichallenger.common.sai_dpu import SaiDpu +from saichallenger.common.sai_environment import init_setup + + +def pytest_addoption(parser): + parser.addoption("--traffic", action="store_true", default=False, help="run tests with traffic") + parser.addoption("--loglevel", action="store", default='NOTICE', help="syncd logging level") + parser.addoption("--setup", action="store", default=None, help="Setup description (Path to the json file).") + + +@pytest.fixture(scope="session") +def exec_params(request): + config_param = {} + config_param["setup"] = init_setup(request.config) + config_param["traffic"] = request.config.getoption("--traffic") + config_param["loglevel"] = request.config.getoption("--loglevel") + logging.getLogger().setLevel(getattr(logging, config_param["loglevel"].upper(), "INFO")) + return config_param + + +@pytest.fixture(scope="session") +def dpu(exec_params) -> SaiDpu: + dpu = exec_params["setup"]["DPU"][0] + if dpu is not None: + dpu.reset() + return dpu + + +@pytest.fixture(scope="session") +def dataplane_session(exec_params): + dataplane = exec_params["setup"]["DATAPLANE"][0] + # Set up the dataplane + dataplane.init() + yield dataplane + # Shutdown the dataplane + dataplane.remove() + + +@pytest.fixture(scope="function") +def dataplane(dataplane_session): + dataplane_session.setUp() + yield dataplane_session + dataplane_session.tearDown() + + +@pytest.fixture(scope="session") +def confgen(): + + class SaiConfig(ConfBase): + + def generate(self): + # Pass top-level params to sub-generators. + self.configs = [ + saigen.vips.Vips(self.params_dict), + saigen.direction_lookup.DirectionLookup(self.params_dict), + saigen.acl_groups.AclGroups(self.params_dict), + saigen.vnets.Vnets(self.params_dict), + saigen.enis.Enis(self.params_dict), + saigen.address_maps.AddressMaps(self.params_dict), + saigen.outbound_routing.OutboundRouting(self.params_dict), + saigen.outbound_ca_to_pa.OutboundCaToPa(self.params_dict), + # saigen.inbound_routing.InboundRouting(self.params_dict), + # saigen.pa_validation.PaValidation(self.params_dict), + ] + + def items(self, reverse=False): + result = [] + for c in self.configs: + result.extend(c.items()) + return result + + return SaiConfig() + # return SaiConfig({}, saigen.simple_params.simple_params) diff --git a/test/test-cases/scale/saic/dash_helper/vnet2vnet_helper.py b/test/test-cases/scale/saic/dash_helper/vnet2vnet_helper.py new file mode 100644 index 000000000..d7cb53ce4 --- /dev/null +++ b/test/test-cases/scale/saic/dash_helper/vnet2vnet_helper.py @@ -0,0 +1,303 @@ +import snappi +import saichallenger.dataplane.traffic_utils as tu +from collections import namedtuple + + +def configure_vnet_outbound_packet_flows(sai_dp, vip, dir_lookup, ca_smac, ca_dip, pkt_count=1, pps=50, duration=0): + """ + Define VNET Outbound routing flows. + + Parameters: + sai_dp: sai_dataplane. + vip (namedtuple(name, 'count start step')): DASH VIP. + dir_lookup (namedtuple(name, 'count start step')): direction lookup vni. + ca_smac (namedtuple(name, 'count start step')): client source MAC. + ca_dip (namedtuple(name, 'count start step')): client destination IP. + pkt_count (int): count of packets to send per each flow. Default is 1. + pps (int): packet per second for each flow. Default is 50. + duration (int): count in seconds to generate traffic for each flow. Default is 0. + """ + + print("\nTest config:") + print(f"{vip}\n{dir_lookup}\n{ca_smac}\n{ca_dip}\n") + + print("Adding flows {} > {}:".format(sai_dp.configuration.ports[0].name, sai_dp.configuration.ports[1].name)) + vip_val = vip.start + for vip_number in range(0, vip.count): + dir_lookup_val = dir_lookup.start + print(f"\tVIP {vip_val}") + + ca_smac_portion = ca_smac.count // dir_lookup.count + for dir_lookup_number in range(0, dir_lookup.count): + print(f"\t\tDIR_LOOKUP VNI {dir_lookup_val}") + ca_smac_val = ca_smac.start + + ca_smac_start_index = dir_lookup_number * ca_smac_portion + for ca_smac_number in range(ca_smac_start_index, ca_smac_start_index + ca_smac_portion): + print(f"\t\t\tCA SMAC: {tu.get_next_mac(ca_smac_val, step=ca_smac.step, number=ca_smac_number)}") + print(f"\t\t\t\tCA DIP {ca_dip.start}, count: {ca_dip.count}, step: {ca_dip.step}") + flow = sai_dp.add_flow("flow {} > {} |vip#{}|dir_lookup#{}|ca_mac#{}|ca_dip#{}".format( + sai_dp.configuration.ports[0].name, sai_dp.configuration.ports[1].name, + vip_number, dir_lookup_number, ca_smac_number, ca_dip.start), + packet_count=ca_dip.count * pkt_count, pps=pps, seconds_count=duration) + + sai_dp.add_ethernet_header(flow, dst_mac="00:00:02:03:04:05", src_mac="00:00:05:06:06:06") + sai_dp.add_ipv4_header(flow, dst_ip=vip_val, src_ip="172.16.1.1") + sai_dp.add_udp_header(flow, dst_port=80, src_port=11638) + sai_dp.add_vxlan_header(flow, vni=dir_lookup_val) + sai_dp.add_ethernet_header(flow, dst_mac="02:02:02:02:02:02", + src_mac=tu.get_next_mac(ca_smac_val, step=ca_smac.step, number=ca_smac_number)) + + sai_dp.add_ipv4_header(flow, dst_ip=ca_dip.start, src_ip="10.1.1.10", + dst_step=ca_dip.step, dst_count=ca_dip.count, + dst_choice=snappi.PatternFlowIpv4Dst.INCREMENT) + sai_dp.add_udp_header(flow) + + dir_lookup_val += dir_lookup.step + + vip_val = tu.get_next_ip(vip_val, vip.step) + + print(f">>> FLOWS: {len(sai_dp.flows)}") + for flow in sai_dp.flows: + print(f">>>: {flow.name}") + + +def scale_vnet_outbound_flows(sai_dp, test_conf: dict, packets_per_flow=1, pps_per_flow=0, flow_duration=0): + """ + Get scale options and define VNET Outbound routing flows. + + Parameters: + sai_dp: sai_dataplane. + test_conf (dict): test config. + packets_per_flow (int): count of packets to send per each flow. Default is 1. + pps_per_flow (int): packet per second for each flow. Default is 10. + flow_duration (int): count in seconds to generate traffic for each flow. Default is 0. + """ + + vip_tup = namedtuple('VIP', 'count start step') + dir_lookup_tup = namedtuple('DIRECTION_LOOKUP', 'count start step') + ca_smac_tup = namedtuple('CA_SMAC', 'count start step') + ca_dip_tup = namedtuple('CA_DIP', 'count start step') + + def dict_helper(named_tup, conf, def_step): + if type(conf) != dict: + return named_tup(1, conf, def_step) + else: + return named_tup(conf.get('count', 1), conf.get('start', def_step), conf.get('step', def_step)) + + vip = dict_helper(vip_tup, test_conf['DASH_VIP']['vpe']['IPV4'], "0.0.0.1") + dir_lookup = dict_helper(dir_lookup_tup, test_conf['DASH_DIRECTION_LOOKUP']['dle']['VNI'], 1) + ca_smac = dict_helper(ca_smac_tup, test_conf['DASH_ENI_ETHER_ADDRESS_MAP']['eam']['MAC'], "00:00:00:00:00:01") + ca_dip = dict_helper(ca_dip_tup, test_conf['DASH_OUTBOUND_CA_TO_PA']['ocpe']['DIP'], "0.0.0.1") + + configure_vnet_outbound_packet_flows(sai_dp, vip, dir_lookup, ca_smac, ca_dip, + pkt_count=packets_per_flow, pps=pps_per_flow, duration=flow_duration) + + +def check_flows_all_packets_metrics(sai_dp, flows, name="Flow group", exp_tx=None, exp_rx=None, show=False): + """ + Get packet count metrics on list of flows. + + Parameters: + sai_dp: sai_dataplane. + flows: list of flows to check. + name (str): flow list's name. Default is "Flow group". + exp_tx (int): expected count of TX packets. + exp_rx (int): expected count of RX packets. + show (bool): flag to show metrics. Default is False. + + Return: + tuple(bool, { int, int }): bool is True if all flow metrics are OK. First int is for + sum of TX packets from all flows. Second int is for sum of RX packets from all flows. + """ + + if not flows: + print("Flows None or empty") + return False, None + if not exp_tx: + # check if all flows are fixed_packets + # sum of bool list == count of True in this list + if sum([flow.duration.choice == snappi.FlowDuration.FIXED_PACKETS for flow in flows]) == len(flows): + exp_tx = sum([flow.duration.fixed_packets.packets for flow in flows]) + else: + print("{}: some flow in flow group doesn't configured to {}.".format( \ + name, snappi.FlowDuration.FIXED_PACKETS)) + return False, None + if not exp_rx: + exp_rx = exp_tx + + act_tx = 0 + act_rx = 0 + success = 0 + + for flow in flows: + tmp = check_flow_packets_metrics(sai_dp, flow) + success += tmp[0] + act_tx += tmp[1]['TX'] + act_rx += tmp[1]['RX'] + + success = success == len(flows) + + if show: + # flow group name | exp tx - act tx | exp rx - act rx + print(f"{name} | exp tx:{exp_tx} - tx:{act_tx} | exp rx:{exp_rx} - rx:{act_rx}") + + return success, { 'TX': act_tx, 'RX': act_rx } + + +def check_flow_packets_metrics(sai_dp, flow: snappi.Flow, exp_tx=None, exp_rx=None, show=False): + """ + Get packet count metrics on flow. + + Parameters: + sai_dp: sai_dataplane. + flow (snappi.Flow): flow to check. + exp_tx (int): expected count of TX packets. + exp_rx (int): expected count of RX packets. + show (bool): flag to show metrics. Default is False. + + Return: + tuple(bool, { int, int }): bool is result of metrics. First int is for TX packets, + second int is for RX packets. + """ + + if not exp_tx: + if flow.duration.choice == snappi.FlowDuration.FIXED_PACKETS: + exp_tx = flow.duration.fixed_packets.packets + else: + print("{}: check for packet count failed. Flow configured to {} instead of {}".format( \ + flow.name, flow.duration.choice, snappi.FlowDuration.FIXED_PACKETS)) + return False, None + if not exp_rx: + exp_rx = exp_tx + + req = sai_dp.api.metrics_request() + req.flow.flow_names = [ flow.name ] + req.flow.metric_names = [ snappi.FlowMetricsRequest.FRAMES_TX, snappi.FlowMetricsRequest.FRAMES_RX ] + res = sai_dp.api.get_metrics(req) + + act_tx = res.flow_metrics[0].frames_tx + act_rx = res.flow_metrics[0].frames_rx + + if show: + # flow group name | exp tx - act tx | exp rx - act rx + print(f"{flow.name} | exp tx:{exp_tx} - tx:{act_tx} | exp rx:{exp_rx} - rx:{act_rx}") + + if exp_tx == act_tx and exp_rx == act_rx and \ + res.flow_metrics[0].transmit == snappi.FlowMetric.STOPPED: + return True, { 'TX': act_tx, 'RX': act_rx } + + return False, { 'TX': act_tx, 'RX': act_rx } + + +def check_flows_all_seconds_metrics(sai_dp, flows, name="Flow group", seconds=None, exp_tx=None, exp_rx=None, delta=None, show=False): + """ + Get packet count metrics per given time on list of flows. + + Parameters: + sai_dp: sai dataplane. + flows: list of flow to check. + name (str): name of flow group. Default is "Flow group". + seconds (int): duration of traffic generating. + exp_tx (int): expected count of TX packets. + exp_rx (int): expected count of RX packets. + delta (int): permissible error in the calculation of TX/RX packets. + show (bool): flag to show metrics. Default is False. + + Return: + tuple(bool, { int, int }): bool is True if all flow metrics are OK. First int is for + sum of TX packets from all flows. Second int is for sum of RX packets from all flows. + """ + + if not flows: + print("Flows None or empty") + return False, None + if not seconds: + if sum([flow.duration.choice == snappi.FlowDuration.FIXED_SECONDS for flow in flows]) == len(flows): + seconds = sum([flow.duration.fixed_seconds.seconds for flow in flows]) // len(flows) + else: + print("{}: some flow in flow group doesn't configured to {}".format( + flow.name, snappi.FlowDuration.FIXED_SECONDS)) + return False, None + if not exp_tx: + exp_tx = sum([flow.rate.pps for flow in flows]) // len(flows) * seconds * len(flows) + if not exp_rx: + exp_rx = exp_tx + if not delta: + # default delta is 10% of exp_tx. If it 0 (seconds < 10) then delta == pps + tmp_delta = exp_tx // 10 + delta = tmp_delta if tmp_delta > 0 else (sum([flow.rate.pps for flow in flows]) // len(flows)) + + act_tx = 0 + act_rx = 0 + success = 0 + + for flow in flows: + tmp = check_flow_seconds_metrics(sai_dp, flow) + success += tmp[0] + act_tx += tmp[1]['TX'] + act_rx += tmp[1]['RX'] + + success = success == len(flows) + + if show: + # flow group name | [exp tx +-delta] - act tx | [exp rx +-delta] - act rx + print(f"{name} | exp tx:[{exp_tx - delta}, {exp_tx + delta}] - tx:{act_tx} | \ +exp rx:[{exp_rx - delta}, {exp_rx + delta}] - rx:{act_rx}") + + return success, { 'TX': act_tx, 'RX': act_rx } + + +def check_flow_seconds_metrics(sai_dp, flow: snappi.Flow, seconds=None, exp_tx=None, exp_rx=None, delta=None, show=False): + """ + Get packet count metrics per given time on flow. + + Parameters: + sai_dp: sai dataplane. + flow (snappi.Flow): flow to check. + seconds (int): duration of traffic generating. + exp_tx (int): expected count of TX packets. + exp_rx (int): expected count of RX packets. + delta (int): permissible error in the calculation of TX/RX packets. + show (bool): flag to show metrics. Default is False. + + Return: + tuple(bool, { int, int }): bool is result of metrics. First int is for TX packets, + second int is for RX packets. + """ + + if not seconds: + if flow.duration.choice == snappi.FlowDuration.FIXED_SECONDS: + seconds = flow.duration.fixed_seconds.seconds + else: + print("{}: check for packet count failed. Flow configured to {} instead of {}".format( + flow.name, flow.duration.choice, snappi.FlowDuration.FIXED_SECONDS)) + return False, None + if not exp_tx: + exp_tx = flow.rate.pps * seconds + if not exp_rx: + exp_rx = exp_tx + if not delta: + # default delta is 10% of exp_tx. If it 0 (seconds < 10) then delta == pps + tmp_delta = exp_tx // 10 + delta = tmp_delta if tmp_delta > 0 else flow.rate.pps + + req = sai_dp.api.metrics_request() + req.flow.flow_names = [ flow.name ] + req.flow.metric_names = [ snappi.FlowMetricsRequest.FRAMES_TX, snappi.FlowMetricsRequest.FRAMES_RX ] + res = sai_dp.api.get_metrics(req) + + act_tx = res.flow_metrics[0].frames_tx + act_rx = res.flow_metrics[0].frames_rx + + if show: + # flow group name | [exp tx +-delta] - act tx | [exp rx +-delta] - act rx + print(f"{flow.name} | exp tx:[{exp_tx - delta}, {exp_tx + delta}] - tx:{act_tx} | \ +exp rx:[{exp_rx - delta}, {exp_rx + delta}] - rx:{act_rx}") + + if act_tx in range(exp_tx - delta, exp_tx + delta) and \ + act_rx in range(exp_rx - delta, exp_rx + delta) and \ + res.flow_metrics[0].transmit == snappi.FlowMetric.STOPPED: + return True, { 'TX': act_tx, 'RX': act_rx } + + return False, { 'TX': act_tx, 'RX': act_rx } diff --git a/test/test-cases/scale/saic/images/outbound_scenario_explained.svg b/test/test-cases/scale/saic/images/outbound_scenario_explained.svg new file mode 100644 index 000000000..737e39685 --- /dev/null +++ b/test/test-cases/scale/saic/images/outbound_scenario_explained.svg @@ -0,0 +1,4 @@ + + + +
VIP

vip: 172.16.1.100
action: ACCEPT
VIP...
DIRECTION LOOKUP

vni: 100
action: SET_OUTBOUND_DIRECTION
DIRECTION LOOKUP...
VNET

vni:  1000
VNET...
ENI

vm_underlay_dip: 172.16.1.1
vm_vni: 9
vnet_id: $vnet
ENI...
ENI_MAC

address: 00:CC:CC:CC:CC:CC
eni_id: $eni
ENI_MAC...
OUTBOUND ROUTING

eni_id: $eni
destination: 10.1.0.0/16
action: ROUTE_VNET
vnet_id: $vnet
OUTBOUND ROUTING...
OUTBOUND CA TO PA

dip: 10.1.2.50
dst_vnet_id: $vnet
underlay_dip: 172.16.1.20
overlay_dmac: 00:DD:DD:DD:DD:DD
use_dst_vnet_vni: True
OUTBOUND CA TO PA...
00:DD:DD:DD:DD:DD
00:DD:DD:DD:DD:DD
00:CC:CC:CC:CC:CC
00:CC:CC:CC:CC:CC
10.1.1.10
10.1.1.10
10.1.2.50
10.1.2.50
00:00:00:00:00:00
00:00:00:00:00:00
00:00:00:00:00:00
00:00:00:00:00:00
172.16.1.100
172.16.1.100
172.16.1.20
172.16.1.20
1000
1000
02:02:02:02:02:02
02:02:02:02:02:02
00:CC:CC:CC:CC:CC
00:CC:CC:CC:CC:CC
10.1.1.10
10.1.1.10
10.1.2.50
10.1.2.50
00:00:02:03:04:05
00:00:02:03:04:05
00:00:05:06:06:06
00:00:05:06:06:06
172.16.1.1
172.16.1.1
172.16.1.100
172.16.1.100
100
100
DMAC
DMAC
SMAC
SMAC
SIP
SIP
DIP
DIP
VNI
VNI
DMAC
DMAC
SMAC
SMAC
SIP
SIP
DIP
DIP
DMAC
DMAC
SMAC
SMAC
SIP
SIP
DIP
DIP
VNI
VNI
DMAC
DMAC
SMAC
SMAC
SIP
SIP
DIP
DIP
match: ODIP
action: accept
match: ODIP...
match: VNI
action: set_outbound_direction
match: VNI...
match: ISMAC
action: set $eni
match: ISMAC...
match: $eni
action: set $eni attrs
match: $eni...
match: $eni, destination (IDIP)
action: set $vnet
match: $eni, destination (ID...
match: $vnet, IDIP
action: set_tunnel_mapping
match: $vnet, IDIP...
match: VNET
action: set vni
match: VNET...
Text is not SVG - cannot display
diff --git a/test/test-cases/scale/saic/images/vnet2vnet_outbound_traffic_scaling.svg b/test/test-cases/scale/saic/images/vnet2vnet_outbound_traffic_scaling.svg new file mode 100644 index 000000000..0d418a221 --- /dev/null +++ b/test/test-cases/scale/saic/images/vnet2vnet_outbound_traffic_scaling.svg @@ -0,0 +1,4 @@ + + + +
VIP #1
VIP #1
ENI #1
ENI #1
ENI MAC MAP #1
ENI MAC MAP #1
ENI #2
ENI #2
ENI MAC MAP #2
ENI MAC MAP #2
DIRECTION LOOKUP #1
DIRECTION LOOKUP #1
OUTBOUND ROUTING #1
OUTBOUND ROUTING #1
OUTBOUND ROUTING #2
OUTBOUND ROUTING #2
OUTBOUND ROUTING #3
OUTBOUND ROUTING #3
OUTBOUND ROUTING #4
OUTBOUND ROUTING #4
CA TO PA #1
CA TO PA #1
CA TO PA #2
CA TO PA #2
CA TO PA #3
CA TO PA #3
CA TO PA #4
CA TO PA #4
DIRECTION LOOKUP #2
DIRECTION LOOKUP #2
VNET #X
VNET #X
VNET #X
VNET #X
VNET #X
VNET #X
VNET #Y
VNET #Y
VNET #X
VNET #X
VNET #X
VNET #X
CA TO PA #5
CA TO PA #5
CA TO PA #6
CA TO PA #6
CA TO PA #7
CA TO PA #7
CA TO PA #8
CA TO PA #8
Text is not SVG - cannot display
\ No newline at end of file diff --git a/test/test-cases/scale/saic/pytest.ini b/test/test-cases/scale/saic/pytest.ini new file mode 100644 index 000000000..f9eb33c53 --- /dev/null +++ b/test/test-cases/scale/saic/pytest.ini @@ -0,0 +1,4 @@ +[pytest] +markers = + ptf: traffic tests supported by PTF. snappi should work as well. + snappi: traffic tests that requires snappi only. \ No newline at end of file diff --git a/test/test-cases/scale/saic/run_vnet_tests.sh b/test/test-cases/scale/saic/run_vnet_tests.sh new file mode 100755 index 000000000..32eeaadda --- /dev/null +++ b/test/test-cases/scale/saic/run_vnet_tests.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +PYTHONPATH=. pytest -sv $@ diff --git a/test/test-cases/scale/saic/sai_dpu_client_server_ptf.json b/test/test-cases/scale/saic/sai_dpu_client_server_ptf.json new file mode 100755 index 000000000..80c55496a --- /dev/null +++ b/test/test-cases/scale/saic/sai_dpu_client_server_ptf.json @@ -0,0 +1,41 @@ +{"DPU": [ + { + "alias": "dash", + "asic": "generic", + "target": "bmv2", + "type": null, + "sku": null, + "mode": "client-server", + "sai_server_ip": "127.0.0.1", + "client": { + "type": "thrift", + "config": { + "ip": "127.0.0.1", + "port": "9092" + } + }, + "port_groups": [{"1x10G": "Ethernet0", "init": "1x10G", "alias": 0}, + {"1x10G": "Ethernet1", "init": "1x10G", "alias": 1} + ], + "sai_dataplane": "ptf_nn" + } +], + +"DATAPLANE": [ + { + "alias": "ptf", + "type": "ptf", + "mode": "eth", + "port_groups": [{"10G": "veth1", "init": "10G", "alias": 0}, + {"10G": "veth2", "init": "10G", "alias": 1} + ] + } +], + +"CONNECTIONS": { + "ptf->dash": [[0, 0], + [1, 1] + ] +} + +} diff --git a/test/test-cases/scale/saic/sai_dpu_client_server_snappi.json b/test/test-cases/scale/saic/sai_dpu_client_server_snappi.json new file mode 100755 index 000000000..67abe70e4 --- /dev/null +++ b/test/test-cases/scale/saic/sai_dpu_client_server_snappi.json @@ -0,0 +1,42 @@ +{"DPU": [ + { + "alias": "dash", + "asic": "generic", + "target": "bmv2", + "type": null, + "sku": null, + "mode": "client-server", + "sai_server_ip": "127.0.0.1", + "client": { + "type": "thrift", + "config": { + "ip": "127.0.0.1", + "port": "9092" + } + }, + "port_groups": [{"1x10G": "Ethernet0", "init": "1x10G", "alias": 0}, + {"1x10G": "Ethernet1", "init": "1x10G", "alias": 1} + ], + "sai_dataplane": "ptf_nn" + } +], + +"DATAPLANE": [ + { + "alias": "ixia", + "type": "snappi", + "mode": "ixia_c", + "controller": "https://127.0.0.1:443", + "port_groups": [{"10G": "veth1", "init": "10G", "alias": 0}, + {"10G": "veth3", "init": "10G", "alias": 1} + ] + } +], + +"CONNECTIONS": { + "ixia->dash": [[0, 0], + [1, 1] + ] +} + +} diff --git a/test/test-cases/scale/saic/test_config_vnet_inbound.py b/test/test-cases/scale/saic/test_config_vnet_inbound.py new file mode 100644 index 000000000..22b4c74f9 --- /dev/null +++ b/test/test-cases/scale/saic/test_config_vnet_inbound.py @@ -0,0 +1,48 @@ +""" +Test module with an example of loading unified SAI config format from the file +""" + +import json +from pathlib import Path + +import pytest + + +class TestConfigVnetInboundRouting: + + @pytest.fixture(scope="class") + def vnet_in_config(self): + """ + Fixture returns the content of the file with SAI configuration commands. + scope=class - The file is loaded once for the whole test class + """ + current_file_dir = Path(__file__).parent + with (current_file_dir / 'vnet_inbound_setup_commands.json').open(mode='r') as config_file: + vnet_inbound_setup_commands = json.load(config_file) + return vnet_inbound_setup_commands + + def test_config_vnet_inbound_create(self, dpu, vnet_in_config): + """ + Apply configuration that is loaded from the file. + """ + + result = [*dpu.process_commands(vnet_in_config)] + # User may want to verify result. + + def test_config_vnet_inbound_remove(self, dpu, vnet_in_config): + """ + Remove configuration that is loaded from the file. + """ + # NOTE: vnet_in_config contains op="create". + # In the following loop "create" is replaced with "remove" + # Note that the order is reversed. + vnet_inbound_cleanup_commands = [] + for command in reversed(vnet_in_config): + command['op'] = 'remove' + vnet_inbound_cleanup_commands.append(command) + + # Another example of applying commands one by one. + # Extremely useful when you have a generator instead of the list + result = [] + for command in vnet_inbound_cleanup_commands: + result.append(dpu.command_processor.process_command(command)) diff --git a/test/test-cases/scale/saic/test_config_vnet_outbound.py b/test/test-cases/scale/saic/test_config_vnet_outbound.py new file mode 100644 index 000000000..9480b16c1 --- /dev/null +++ b/test/test-cases/scale/saic/test_config_vnet_outbound.py @@ -0,0 +1,37 @@ +""" +Test module with an example of parametrized test case +""" +import json +from pathlib import Path + +import pytest + + +# The following test function will be executed twice - against each cfg_type: simple and scale +@pytest.mark.parametrize('cfg_type', ['simple', 'scale']) +def test_config_vnet_outbound_parametrized(dpu, cfg_type): + + # Loading unified SAI commands from file based on cfg_type + current_file_dir = Path(__file__).parent + with (current_file_dir / f'vnet_outbound_setup_commands_{cfg_type}.json').open(mode='r') as config_file: + setup_commands = json.load(config_file) + + try: + # Execute all commands in one shot + result = [*dpu.process_commands(setup_commands)] + + finally: + # The idea of finally here to try remove something if apply failed in the middle... + # In other test suites remove is a part of the separate test case and is always executed. + # This specific function executes a different configuration on each iteration and it's important + # to keep remove commands next to the create ones. + + # Replace op="create" to op="remove" + # Note that the order is reversed. + cleanup_commands = [] + for command in reversed(setup_commands): + command['op'] = 'remove' + cleanup_commands.append(command) + + # Execute all remove commands + result = [*dpu.process_commands(cleanup_commands)] diff --git a/test/test-cases/scale/saic/test_sai_vnet_inbound.py b/test/test-cases/scale/saic/test_sai_vnet_inbound.py new file mode 100644 index 000000000..3ac8dc3f2 --- /dev/null +++ b/test/test-cases/scale/saic/test_sai_vnet_inbound.py @@ -0,0 +1,193 @@ +""" +Verify VNET Inbound Routing scenario +""" + +import json +from pathlib import Path +from pprint import pprint + +import pytest +from saichallenger.dataplane.ptf_testutils import (send_packet, + simple_udp_packet, + simple_vxlan_packet, + verify_packet) + +current_file_dir = Path(__file__).parent + +# Constants +VIP = "10.10.1.1" +SWITCH_ID = 1 +DIR_LOOKUP_VNI = 60 +VM_VNI = 9 +ENI_MAC = "00:00:00:09:03:14" +PA_VALIDATION_SIP = "10.10.2.10" +PA_VALIDATION_DIP = "10.10.2.20" +INBOUND_ROUTING_VNI = 2 +INNER_VM_IP = "172.19.1.100" +INNER_REMOTE_IP = "172.19.1.1" + +# TODO: Fix configuration once issue is addressed: https://github.com/Azure/DASH/issues/233 +TEST_VNET_INBOUND_CONFIG = { + + 'ACL_TABLE_COUNT': 1, + 'ENI_COUNT': 1, + 'ENI_START': 1, + 'IP_PER_ACL_RULE': 1, + 'IP_MAPPED_PER_ACL_RULE': 1, + 'IP_ROUTE_DIVIDER_PER_ACL_RULE': 8, + + 'DASH_VIP': [ + {'vip_1': {'IPv4': VIP}} + ], + + 'DASH_DIRECTION_LOOKUP': [ + {'direction_lookup': {'VNI': DIR_LOOKUP_VNI}} + ], + + 'DASH_ACL_GROUP': [ + {'acl_out_1': {'ADDR_FAMILY': 'IPv4'}} + ], + + 'DASH_ACL_RULE': [ + {'acl_rule_1': {'action': 'permit"', + 'dip': '10.0.0.1', + 'gid': '$acl_out_1', + 'priority': 10} + } + ], + + 'DASH_VNET': [ + {'vnet_1': {'VNI': DIR_LOOKUP_VNI}} + ], + + 'DASH_ENI': [ + {'eni_1': + {'ACL_GROUP': { + 'INBOUND': [{'STAGE1': '$acl_in_1'}, + {'STAGE2': '$acl_in_1'}, + {'STAGE3': '$acl_in_1'}, + {'STAGE4': '$acl_in_1'}, + {'STAGE5': '$acl_in_1'} + ], + 'OUTBOUND': [{'STAGE1': '$acl_out_1'}, + {'STAGE2': '$acl_out_1'}, + {'STAGE3': '$acl_out_1'}, + {'STAGE4': '$acl_out_1'}, + {'STAGE5': '$acl_out_1'} + ] + }, + 'ADMIN_STATE': True, + 'CPS': 10000, + 'FLOWS': 10000, + 'PPS': 100000, + 'VM_UNDERLAY_DIP': PA_VALIDATION_DIP, + 'VM_VNI': VM_VNI, + 'VNET_ID': '$vnet_1'} + } + ], + + 'DASH_ENI_ETHER_ADDRESS_MAP': [ + {'address_map_1': { + 'MAC': ENI_MAC, + 'VNI': INBOUND_ROUTING_VNI} + } + ], + + 'DASH_ROUTE_RULE_TABLE': [ + {'inbound_routing_1': { + 'VNI': INBOUND_ROUTING_VNI, + 'action_type': 'DECAP_PA_VALIDATE'} + } + ], + + 'DASH_PA_VALIDATION': [ + {'pa_validation_1': {'action': 'permit', + 'eni': '$eni_1', + 'sip': PA_VALIDATION_SIP, + 'switch_id': SWITCH_ID, + 'vni': INBOUND_ROUTING_VNI} + } + ] + +} + + +class TestSaiVnetInbound: + + def test_vnet_inbound_create(self, confgen, dpu): + """Test configuration create""" + + # confgen.mergeParams(TEST_VNET_INBOUND_CONFIG) + # confgen.generate() + # for item in confgen.items(): + # pprint(item) + + with (current_file_dir / 'vnet_inbound_setup_commands.json').open(mode='r') as config_file: + vnet_inbound_setup_commands = json.load(config_file) + result = [*dpu.process_commands(vnet_inbound_setup_commands)] + print("\n======= SAI commands RETURN values =======") + pprint(result) + + @pytest.mark.ptf + @pytest.mark.xfail(reason="https://github.com/Azure/DASH/issues/233") + def test_vnet_inbound_traffic_check(self, dpu, dataplane): + """Verify traffic forwarding in PTF style""" + + outer_smac = "00:0a:05:06:06:06" + outer_dmac = "00:0b:05:06:06:06" + inner_smac = "00:0a:04:06:06:06" + inner_dmac = "00:0b:04:06:06:06" + + # PAcket to send + inner_pkt = simple_udp_packet(eth_dst=ENI_MAC, + eth_src=inner_smac, + ip_dst=INNER_VM_IP, + ip_src=INNER_REMOTE_IP) + vxlan_pkt = simple_vxlan_packet(eth_dst=outer_dmac, + eth_src=outer_smac, + ip_dst=PA_VALIDATION_DIP, + ip_src=PA_VALIDATION_SIP, + udp_sport=11638, + with_udp_chksum=False, + vxlan_vni=DIR_LOOKUP_VNI, + inner_frame=inner_pkt) + + # Expected Packet to check + inner_exp_pkt = simple_udp_packet(eth_dst=inner_dmac, + eth_src=ENI_MAC, + ip_dst=INNER_VM_IP, + ip_src=INNER_REMOTE_IP) + vxlan_exp_pkt = simple_vxlan_packet(eth_dst="00:00:00:00:00:00", + eth_src="00:00:00:00:00:00", + ip_dst=PA_VALIDATION_DIP, + ip_src=PA_VALIDATION_SIP, + udp_sport=11638, + with_udp_chksum=False, + vxlan_vni=INBOUND_ROUTING_VNI, + inner_frame=inner_exp_pkt) + + # dataplane.start_capture() + print("\nSending outbound packet...\n\n", vxlan_pkt.__repr__()) + send_packet(dataplane, 0, vxlan_pkt) + + print("\nVerifying packet...\n", vxlan_exp_pkt.__repr__()) + verify_packet(dataplane, vxlan_exp_pkt, 1) + + def test_vnet_inbound_remove(self, confgen, dpu): + """Test configuration remove""" + + # confgen.mergeParams(TEST_VNET_INBOUND_CONFIG) + # confgen.generate() + # for item in confgen.items(): + # item['OP'] = 'remove' + # pprint(item) + + with (current_file_dir / 'vnet_inbound_setup_commands.json').open(mode='r') as config_file: + setup_commands = json.load(config_file) + cleanup_commands = [] + for cmd in reversed(setup_commands): + cleanup_commands.append({'name': cmd['name'], 'op': 'remove'}) + + result = [*dpu.process_commands(cleanup_commands)] + print("\n======= SAI commands RETURN values =======") + pprint(result) diff --git a/test/test-cases/scale/saic/test_sai_vnet_outbound_scale.py b/test/test-cases/scale/saic/test_sai_vnet_outbound_scale.py new file mode 100644 index 000000000..2661221dd --- /dev/null +++ b/test/test-cases/scale/saic/test_sai_vnet_outbound_scale.py @@ -0,0 +1,275 @@ +import json +from pathlib import Path +from pprint import pprint + +import pytest +import saichallenger.dataplane.snappi.snappi_traffic_utils as stu +from saichallenger.dataplane.ptf_testutils import (send_packet, + simple_udp_packet, + simple_vxlan_packet, + verify_no_other_packets, + verify_packet) + +import dash_helper.vnet2vnet_helper as dh + +current_file_dir = Path(__file__).parent + +# Constants for scale VNET outbound routing configuration +NUMBER_OF_VIP = 1 +NUMBER_OF_DLE = 2 +NUMBER_OF_ENI = 2 +NUMBER_OF_EAM = NUMBER_OF_ENI +NUMBER_OF_ORE = 2 # Per ENI +NUMBER_OF_OCPE = 2 # Per ORE +NUMBER_OF_VNET = NUMBER_OF_ENI + (NUMBER_OF_ORE * NUMBER_OF_ENI) # So far per ORE, but may be different +NUMBER_OF_IN_ACL_GROUP = 10 +NUMBER_OF_OUT_ACL_GROUP = 10 + + +# Scaled configuration +# Pay attention to the 'count', 'start', 'step' keywords. +# See README.md for details. +TEST_VNET_OUTBOUND_CONFIG_SCALE = { + + 'DASH_VIP': { + 'vpe': { + 'count': NUMBER_OF_VIP, + 'SWITCH_ID': '$SWITCH_ID', + 'IPV4': { + 'count': NUMBER_OF_VIP, + 'start': '172.16.1.100', + 'step': '0.1.0.0' + } + } + }, + + 'DASH_DIRECTION_LOOKUP': { + 'dle': { + 'count': NUMBER_OF_DLE, + 'SWITCH_ID': '$SWITCH_ID', + 'VNI': { + 'count': NUMBER_OF_DLE, + 'start': 5000, + 'step': 1000 + }, + 'ACTION': 'SET_OUTBOUND_DIRECTION' + } + }, + + 'DASH_ACL_GROUP': { + 'in_acl_group_id': { + 'count': NUMBER_OF_IN_ACL_GROUP, + 'ADDR_FAMILY': 'IPv4' + }, + 'out_acl_group_id': { + 'count': NUMBER_OF_OUT_ACL_GROUP, + 'ADDR_FAMILY': 'IPv4' + } + }, + + 'DASH_VNET': { + 'vnet': { + 'VNI': { + 'count': NUMBER_OF_VNET, + 'start': 1000, + 'step': 1000 + } + } + }, + + 'DASH_ENI': { + 'eni': { + 'count': NUMBER_OF_ENI, + 'ACL_GROUP': { + 'INBOUND': { + 'STAGE1': { + 'list': 'DASH_ACL_GROUP#in_acl_group_id#0' + }, + 'STAGE2': { + 'list': 'DASH_ACL_GROUP#in_acl_group_id#0' + }, + 'STAGE3': { + 'list': 'DASH_ACL_GROUP#in_acl_group_id#0' + }, + 'STAGE4': { + 'list': 'DASH_ACL_GROUP#in_acl_group_id#0' + }, + 'STAGE5': { + 'list': 'DASH_ACL_GROUP#in_acl_group_id#0' + } + }, + 'OUTBOUND': { + 'STAGE1': 0, + 'STAGE2': 0, + 'STAGE3': 0, + 'STAGE4': 0, + 'STAGE5': 0 + } + }, + 'ADMIN_STATE': True, + 'CPS': 10000, + 'FLOWS': 10000, + 'PPS': 100000, + 'VM_UNDERLAY_DIP': { + 'count': NUMBER_OF_ENI, + 'start': '172.16.1.1', + 'step': '0.0.1.0' + }, + 'VM_VNI': { + 'count': NUMBER_OF_ENI, + 'start': 9 + }, + 'VNET_ID': { + 'count': NUMBER_OF_ENI, + 'start': '$vnet_#{4}' + } + } + }, + + 'DASH_ENI_ETHER_ADDRESS_MAP': { + 'eam': { + 'count': NUMBER_OF_EAM, + 'SWITCH_ID': '$SWITCH_ID', + 'MAC': { + 'count': NUMBER_OF_EAM, + 'start': '00:CC:CC:CC:00:00', + 'step': "00:00:00:00:00:01" + }, + 'ENI_ID': { + 'count': NUMBER_OF_ENI, + 'start': '$eni_#{0}' + } + } + }, + + 'DASH_OUTBOUND_ROUTING': { + 'ore': { + 'count': NUMBER_OF_ENI * NUMBER_OF_ORE, # Full count: OREs per ENI and VNET + 'SWITCH_ID': '$SWITCH_ID', + 'ACTION': 'ROUTE_VNET', + 'DESTINATION': { + 'count': NUMBER_OF_ORE, + 'start': '10.1.1.0/31', + 'step': '0.0.0.2' + }, + 'ENI_ID': { + 'count': NUMBER_OF_ENI, + 'start': '$eni_#{0}', + 'delay': NUMBER_OF_ORE + }, + 'DST_VNET_ID': { + 'count': NUMBER_OF_VNET, + 'start': '$vnet_#{0}', + 'delay': NUMBER_OF_ORE + } + } + }, + + 'DASH_OUTBOUND_CA_TO_PA': { + 'ocpe': { + 'count': (NUMBER_OF_ENI * NUMBER_OF_ORE) * NUMBER_OF_OCPE, # 2 Per ORE + 'SWITCH_ID': '$SWITCH_ID', + 'DIP': { + 'count': NUMBER_OF_ORE * NUMBER_OF_OCPE, + 'start': '10.1.1.0', + 'step': '0.0.0.1' + }, + 'DST_VNET_ID': { + 'count': NUMBER_OF_VNET, + 'start': '$vnet_#{0}', + 'delay': NUMBER_OF_ORE + }, + 'UNDERLAY_DIP': { + 'count': NUMBER_OF_ENI * NUMBER_OF_ORE, + 'start': '172.16.1.20', + 'step': '0.0.1.0' + }, + 'OVERLAY_DMAC': { + 'count': NUMBER_OF_ENI * NUMBER_OF_ORE, + 'start': '00:DD:DD:DD:00:00' + }, + 'USE_DST_VNET_VNI': True + } + } +} + + +class TestSaiVnetOutbound: + + def test_create_vnet_config(self, confgen, dpu): + """Generate and apply configuration""" + + # confgen.mergeParams(TEST_VNET_OUTBOUND_CONFIG_SCALE) + # confgen.generate() + # results = [] + # for item in confgen.items(): + # pprint(item) + # results.append(dpu.command_processor.process_command(item)) + + with (current_file_dir / 'vnet_outbound_setup_commands_scale.json').open(mode='r') as config_file: + setup_commands = json.load(config_file) + result = [*dpu.process_commands(setup_commands)] + # print("\n======= SAI commands RETURN values =======") + # for cmd, res in zip(setup_commands, result): + # print(cmd['name'], cmd['type'], res) + + @pytest.mark.snappi + def test_run_traffic_check_fixed_packets(self, dpu, dataplane): + """ + Test with the fixed number of packets to send. + packets_per_flow=1 means that each possible packet path will be verified using a single packet. + NOTE: This test does not verify the correctness of the packets transformation. + """ + + #Generate traffic configuration, apply it and run. + dh.scale_vnet_outbound_flows(dataplane, TEST_VNET_OUTBOUND_CONFIG_SCALE, + packets_per_flow=1, flow_duration=0, pps_per_flow=10) + dataplane.set_config() + dataplane.start_traffic() + + # The following function waits for expected counters and fail if no success during time out. + stu.wait_for(lambda: dh.check_flows_all_packets_metrics(dataplane, dataplane.flows, + name="Custom flow group", show=True)[0], + "Test", timeout_seconds=5) + + @pytest.mark.snappi + def test_run_traffic_check_fixed_duration(self, dpu, dataplane): + """ + Test with the fixed traffic duration to send. + flow_duration sets the total duration of traffic. Number of packets is limited by PPS. + For the HW PPS may be omitted and then it will send traffic on a line rate. + NOTE: This test does not verify the correctness of the packets transformation. + """ + test_duration = 5 + dh.scale_vnet_outbound_flows(dataplane, TEST_VNET_OUTBOUND_CONFIG_SCALE, + packets_per_flow=0, flow_duration=test_duration, pps_per_flow=5) + dataplane.set_config() + dataplane.start_traffic() + stu.wait_for(lambda: dh.check_flows_all_seconds_metrics(dataplane, dataplane.flows, + name="Custom flow group", show=True)[0], + "Test", timeout_seconds=test_duration + 1) + + def test_remove_vnet_config(self, confgen, dpu, dataplane): + """ + Generate and remove configuration + We generate configuration on remove stage as well to avoid storing giant objects in memory. + """ + + # confgen.mergeParams(TEST_VNET_OUTBOUND_CONFIG_SCALE) + # confgen.generate() + # results = [] + # for item in confgen.items(): + # item['op'] = 'remove' + # pprint(item) + # results.append(dpu.command_processor.process_command(item)) + + with (current_file_dir / 'vnet_outbound_setup_commands_scale.json').open(mode='r') as config_file: + setup_commands = json.load(config_file) + cleanup_commands = [] + for cmd in reversed(setup_commands): + cleanup_commands.append({'name': cmd['name'], 'op': 'remove'}) + + result = [*dpu.process_commands(cleanup_commands)] + # print("\n======= SAI commands RETURN values =======") + # for cmd, res in zip(cleanup_commands, result): + # print(cmd['name'], res) diff --git a/test/test-cases/scale/saic/test_sai_vnet_outbound_simple.py b/test/test-cases/scale/saic/test_sai_vnet_outbound_simple.py new file mode 100644 index 000000000..6cc1a1e74 --- /dev/null +++ b/test/test-cases/scale/saic/test_sai_vnet_outbound_simple.py @@ -0,0 +1,297 @@ +import json +import time +from pathlib import Path +from pprint import pprint + +import pytest +import saichallenger.dataplane.snappi.snappi_traffic_utils as stu +from saichallenger.dataplane.ptf_testutils import (send_packet, + simple_udp_packet, + simple_vxlan_packet, + verify_no_other_packets, + verify_packet) + +import dash_helper.vnet2vnet_helper as dh + +current_file_dir = Path(__file__).parent + +# Constants +SWITCH_ID = 5 + +# Simple, non-scaled configuration. +# See README.md for details. +TEST_VNET_OUTBOUND_CONFIG = { + + "ENI_COUNT": 1, + "ACL_RULES_NSG": 1, + "ACL_TABLE_COUNT": 1, + "IP_PER_ACL_RULE": 1, + "IP_MAPPED_PER_ACL_RULE": 1, + "IP_ROUTE_DIVIDER_PER_ACL_RULE": 1, + + 'DASH_VIP': { + 'vpe': { + 'SWITCH_ID': '$SWITCH_ID', + 'IPV4': "172.16.1.100" + } + }, + + 'DASH_DIRECTION_LOOKUP': { + 'dle': { + 'SWITCH_ID': '$SWITCH_ID', + 'VNI': 100, + 'ACTION': 'SET_OUTBOUND_DIRECTION' + } + }, + + 'DASH_ACL_GROUP': { + 'in_acl_group_id': { + 'ADDR_FAMILY': 'IPv4' + }, + 'out_acl_group_id': { + 'ADDR_FAMILY': 'IPv4' + } + }, + + 'DASH_VNET': { + 'vnet': { + 'VNI': 1000 + } + }, + + 'DASH_ENI': { + 'eni': { + 'ACL_GROUP': { + 'INBOUND': { + 'STAGE1': '$in_acl_group_id_#{0}', + 'STAGE2': '$in_acl_group_id_#{0}', + 'STAGE3': '$in_acl_group_id_#{0}}', + 'STAGE4': '$in_acl_group_id_#{0}}', + 'STAGE5': '$in_acl_group_id_#{0}}' + }, + 'OUTBOUND': { + 'STAGE1': 0, + 'STAGE2': 0, + 'STAGE3': 0, + 'STAGE4': 0, + 'STAGE5': 0 + } + }, + 'ADMIN_STATE': True, + 'CPS': 10000, + 'FLOWS': 10000, + 'PPS': 100000, + 'VM_UNDERLAY_DIP': "172.16.1.1", + 'VM_VNI': 9, + 'VNET_ID': '$vnet_#{0}' + } + }, + + 'DASH_ENI_ETHER_ADDRESS_MAP': { + 'eam': { + 'SWITCH_ID': '$SWITCH_ID', + 'MAC': "00:cc:cc:cc:00:00", + 'ENI_ID': '$eni_#{0}' + } + }, + + 'DASH_OUTBOUND_ROUTING': { + 'ore': { + 'SWITCH_ID': '$SWITCH_ID', + 'ENI_ID': '$eni_#{0}', + 'DESTINATION': "10.1.0.0/16", + 'ACTION': 'ROUTE_VNET', + 'DST_VNET_ID': '$vnet_#{0}' + } + }, + + 'DASH_OUTBOUND_CA_TO_PA': { + 'ocpe': { + 'SWITCH_ID': '$SWITCH_ID', + 'DST_VNET_ID': '$vnet_#{0}', + 'DIP': "10.1.2.50", + 'UNDERLAY_DIP': "172.16.1.20", + 'OVERLAY_DMAC': "00:DD:DD:DD:00:00", + 'USE_DST_VNET_VNI': True + } + } +} + + +class TestSaiVnetOutbound: + + def test_vnet_inbound_simple_create(self, confgen, dpu): + """Generate and apply configuration""" + + # confgen.mergeParams(TEST_VNET_OUTBOUND_CONFIG) + # confgen.generate() + # results = [] + # for item in confgen.items(): + # pprint(item) + # results.append(dpu.command_processor.process_command(item)) + + with (current_file_dir / 'vnet_outbound_setup_commands_simple.json').open(mode='r') as config_file: + setup_commands = json.load(config_file) + result = [*dpu.process_commands(setup_commands)] + print("\n======= SAI commands RETURN values =======") + pprint(result) + + @pytest.mark.ptf + def test_vnet_inbound_simple_packet_modification(self, dpu, dataplane): + """Verify proper packet transformation.""" + + SRC_VM_IP = "10.1.1.10" + OUTER_SMAC = "00:00:05:06:06:06" + OUR_MAC = "00:00:02:03:04:05" + + VIP = TEST_VNET_OUTBOUND_CONFIG['DASH_VIP']['vpe']['IPV4'] + VNET_VNI = TEST_VNET_OUTBOUND_CONFIG['DASH_VNET']['vnet']['VNI'] + DIR_LOOKUP_VNI = TEST_VNET_OUTBOUND_CONFIG['DASH_DIRECTION_LOOKUP']['dle']['VNI'] + SRC_VM_PA_IP = TEST_VNET_OUTBOUND_CONFIG['DASH_ENI']['eni']['VM_UNDERLAY_DIP'] + ENI_MAC = TEST_VNET_OUTBOUND_CONFIG['DASH_ENI_ETHER_ADDRESS_MAP']['eam']['MAC'] + DST_CA_IP = TEST_VNET_OUTBOUND_CONFIG['DASH_OUTBOUND_CA_TO_PA']['ocpe']['DIP'] + DST_PA_IP = TEST_VNET_OUTBOUND_CONFIG['DASH_OUTBOUND_CA_TO_PA']['ocpe']["UNDERLAY_DIP"] + DST_CA_MAC = TEST_VNET_OUTBOUND_CONFIG['DASH_OUTBOUND_CA_TO_PA']['ocpe']["OVERLAY_DMAC"] + + # # check VIP drop + WRONG_VIP = "172.16.100.100" + inner_pkt = simple_udp_packet(eth_dst="02:02:02:02:02:02", + eth_src=ENI_MAC, + ip_dst=DST_CA_IP, + ip_src=SRC_VM_IP) + vxlan_pkt = simple_vxlan_packet(eth_dst=OUR_MAC, + eth_src=OUTER_SMAC, + ip_dst=WRONG_VIP, + ip_src=SRC_VM_PA_IP, + udp_sport=11638, + with_udp_chksum=False, + vxlan_vni=VNET_VNI, + inner_frame=inner_pkt) + print("\n\nSending packet with wrong vip...\n\n", vxlan_pkt.__repr__()) + send_packet(dataplane, 0, vxlan_pkt) + print("\nVerifying drop...") + verify_no_other_packets(dataplane) + + # check routing drop + WRONG_DST_CA = "10.200.2.50" + inner_pkt = simple_udp_packet(eth_dst="02:02:02:02:02:02", + eth_src=ENI_MAC, + ip_dst=WRONG_DST_CA, + ip_src=SRC_VM_IP) + vxlan_pkt = simple_vxlan_packet(eth_dst=OUR_MAC, + eth_src=OUTER_SMAC, + ip_dst=VIP, + ip_src=SRC_VM_PA_IP, + udp_sport=11638, + with_udp_chksum=False, + vxlan_vni=VNET_VNI, + inner_frame=inner_pkt) + print("\nSending packet with wrong dst CA IP to verify routing drop...\n\n", vxlan_pkt.__repr__()) + send_packet(dataplane, 0, vxlan_pkt) + print("\nVerifying drop...") + verify_no_other_packets(dataplane) + + # check mapping drop + WRONG_DST_CA = "10.1.211.211" + inner_pkt = simple_udp_packet(eth_dst="02:02:02:02:02:02", + eth_src=ENI_MAC, + ip_dst=WRONG_DST_CA, + ip_src=SRC_VM_IP) + vxlan_pkt = simple_vxlan_packet(eth_dst=OUR_MAC, + eth_src=OUTER_SMAC, + ip_dst=VIP, + ip_src=SRC_VM_PA_IP, + udp_sport=11638, + with_udp_chksum=False, + vxlan_vni=VNET_VNI, + inner_frame=inner_pkt) + print("\nSending packet with wrong dst CA IP to verify mapping drop...\n\n", vxlan_pkt.__repr__()) + send_packet(dataplane, 0, vxlan_pkt) + print("\nVerifying drop...") + verify_no_other_packets(dataplane) + + # check forwarding + inner_pkt = simple_udp_packet(eth_dst = "02:02:02:02:02:02", + eth_src = ENI_MAC, + ip_dst = DST_CA_IP, + ip_src = SRC_VM_IP) + vxlan_pkt = simple_vxlan_packet(eth_dst = OUR_MAC, + eth_src = OUTER_SMAC, + ip_dst = VIP, + ip_src = SRC_VM_PA_IP, + udp_sport = 11638, + with_udp_chksum = False, + vxlan_vni = DIR_LOOKUP_VNI, + inner_frame = inner_pkt) + + inner_exp_pkt = simple_udp_packet(eth_dst = DST_CA_MAC, + eth_src = ENI_MAC, + ip_dst = DST_CA_IP, + ip_src = SRC_VM_IP) + vxlan_exp_pkt = simple_vxlan_packet(eth_dst = "00:00:00:00:00:00", + eth_src = "00:00:00:00:00:00", + ip_dst = DST_PA_IP, + ip_src = VIP, + udp_sport = 0, # TODO: Fix sport in pipeline + with_udp_chksum = False, + vxlan_vni = VNET_VNI, + vxlan_flags = 0, + inner_frame = inner_exp_pkt) + vxlan_exp_pkt['IP'].chksum = 0 + + print("\nSending outbound packet...\n\n", vxlan_pkt.__repr__()) + send_packet(dataplane, 0, vxlan_pkt) + time.sleep(0.5) + print("\nVerifying packet...\n", vxlan_exp_pkt.__repr__()) + verify_packet(dataplane, vxlan_exp_pkt, 0) + + @pytest.mark.snappi + def test_vnet_inbound_simple_traffic_fixed_packets(self, dpu, dataplane): + """ + Verify same config with high-rate traffic. + packets_per_flow=10 means that each possible packet path will be verified using 10 packet. + NOTE: For BMv2 we keep here PPS limitation + """ + dh.scale_vnet_outbound_flows(dataplane, TEST_VNET_OUTBOUND_CONFIG, packets_per_flow=10, pps_per_flow=10) + dataplane.set_config() + dataplane.start_traffic() + stu.wait_for(lambda: dh.check_flow_packets_metrics(dataplane, dataplane.flows[0], show=True)[0], + "Test", timeout_seconds=10) + + @pytest.mark.snappi + def test_vnet_inbound_simple_traffic_fixed_duration(self, dpu, dataplane): + """ + Test with the fixed traffic duration to send. + flow_duration sets the total duration of traffic. Number of packets is limited by PPS. + For the HW PPS may be omitted and then it will send traffic on a line rate. + NOTE: This test does not verify the correctness of the packets transformation. + """ + test_duration = 5 + dh.scale_vnet_outbound_flows(dataplane, TEST_VNET_OUTBOUND_CONFIG, + packets_per_flow=0, flow_duration=test_duration, pps_per_flow=5) + dataplane.set_config() + dataplane.start_traffic() + stu.wait_for(lambda: dh.check_flows_all_seconds_metrics(dataplane, dataplane.flows, + name="Custom flow group", show=True)[0], + "Test", timeout_seconds=test_duration + 1) + + def test_vnet_inbound_simple_remove(self, confgen, dpu): + """Verify configuration removal""" + + # confgen.mergeParams(TEST_VNET_OUTBOUND_CONFIG) + # confgen.generate() + # results = [] + # for item in confgen.items(): + # item['op'] = 'remove' + # pprint(item) + # results.append(dpu.command_processor.process_command(item)) + + with (current_file_dir / 'vnet_outbound_setup_commands_simple.json').open(mode='r') as config_file: + setup_commands = json.load(config_file) + cleanup_commands = [] + for cmd in reversed(setup_commands): + cleanup_commands.append({'name': cmd['name'], 'op': 'remove'}) + + result = [*dpu.process_commands(cleanup_commands)] + print("\n======= SAI commands RETURN values =======") + pprint(result) diff --git a/test/test-cases/scale/saic/vnet_inbound_setup_commands.json b/test/test-cases/scale/saic/vnet_inbound_setup_commands.json new file mode 100644 index 000000000..e43e09c09 --- /dev/null +++ b/test/test-cases/scale/saic/vnet_inbound_setup_commands.json @@ -0,0 +1,162 @@ +[ + { + "name": "vip_entry", + "op": "create", + "type": "SAI_OBJECT_TYPE_VIP_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "vip": "192.168.0.1" + }, + "attributes": [ + "SAI_VIP_ENTRY_ATTR_ACTION", + "SAI_VIP_ENTRY_ACTION_ACCEPT" + ] + }, + { + "name": "direction_lookup_entry", + "op": "create", + "type": "SAI_OBJECT_TYPE_DIRECTION_LOOKUP_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "vni": "2000" + }, + "attributes": [ + "SAI_DIRECTION_LOOKUP_ENTRY_ATTR_ACTION", + "SAI_DIRECTION_LOOKUP_ENTRY_ACTION_SET_OUTBOUND_DIRECTION" + ] + }, + { + "name": "acl_in_1", + "op": "create", + "type": "SAI_OBJECT_TYPE_DASH_ACL_GROUP", + "attributes": [ + "SAI_DASH_ACL_GROUP_ATTR_IP_ADDR_FAMILY", + "SAI_IP_ADDR_FAMILY_IPV4" + ] + }, + { + "name": "acl_out_1", + "op": "create", + "type": "SAI_OBJECT_TYPE_DASH_ACL_GROUP", + "attributes": [ + "SAI_DASH_ACL_GROUP_ATTR_IP_ADDR_FAMILY", + "SAI_IP_ADDR_FAMILY_IPV4" + ] + }, + { + "name": "vnet_1", + "op": "create", + "type": "SAI_OBJECT_TYPE_VNET", + "attributes": [ + "SAI_VNET_ATTR_VNI", + "2000" + ] + }, + { + "name": "eni_id", + "op": "create", + "type": "SAI_OBJECT_TYPE_ENI", + "attributes": [ + "SAI_ENI_ATTR_CPS", + "10000", + "SAI_ENI_ATTR_PPS", + "100000", + "SAI_ENI_ATTR_FLOWS", + "100000", + "SAI_ENI_ATTR_ADMIN_STATE", + "True", + "SAI_ENI_ATTR_VM_UNDERLAY_DIP", + "10.10.2.10", + "SAI_ENI_ATTR_VM_VNI", + "9", + "SAI_ENI_ATTR_VNET_ID", + "$vnet_1", + "SAI_ENI_ATTR_INBOUND_V4_STAGE1_DASH_ACL_GROUP_ID", + "$acl_in_1", + "SAI_ENI_ATTR_INBOUND_V4_STAGE2_DASH_ACL_GROUP_ID", + "$acl_in_1", + "SAI_ENI_ATTR_INBOUND_V4_STAGE3_DASH_ACL_GROUP_ID", + "$acl_in_1", + "SAI_ENI_ATTR_INBOUND_V4_STAGE4_DASH_ACL_GROUP_ID", + "$acl_in_1", + "SAI_ENI_ATTR_INBOUND_V4_STAGE5_DASH_ACL_GROUP_ID", + "$acl_in_1", + "SAI_ENI_ATTR_INBOUND_V6_STAGE1_DASH_ACL_GROUP_ID", + "$acl_out_1", + "SAI_ENI_ATTR_INBOUND_V6_STAGE2_DASH_ACL_GROUP_ID", + "$acl_out_1", + "SAI_ENI_ATTR_INBOUND_V6_STAGE3_DASH_ACL_GROUP_ID", + "$acl_out_1", + "SAI_ENI_ATTR_INBOUND_V6_STAGE4_DASH_ACL_GROUP_ID", + "$acl_out_1", + "SAI_ENI_ATTR_INBOUND_V6_STAGE5_DASH_ACL_GROUP_ID", + "$acl_out_1", + "SAI_ENI_ATTR_OUTBOUND_V4_STAGE1_DASH_ACL_GROUP_ID", + "0", + "SAI_ENI_ATTR_OUTBOUND_V4_STAGE2_DASH_ACL_GROUP_ID", + "0", + "SAI_ENI_ATTR_OUTBOUND_V4_STAGE3_DASH_ACL_GROUP_ID", + "0", + "SAI_ENI_ATTR_OUTBOUND_V4_STAGE4_DASH_ACL_GROUP_ID", + "0", + "SAI_ENI_ATTR_OUTBOUND_V4_STAGE5_DASH_ACL_GROUP_ID", + "0", + "SAI_ENI_ATTR_OUTBOUND_V6_STAGE1_DASH_ACL_GROUP_ID", + "0", + "SAI_ENI_ATTR_OUTBOUND_V6_STAGE2_DASH_ACL_GROUP_ID", + "0", + "SAI_ENI_ATTR_OUTBOUND_V6_STAGE3_DASH_ACL_GROUP_ID", + "0", + "SAI_ENI_ATTR_OUTBOUND_V6_STAGE4_DASH_ACL_GROUP_ID", + "0", + "SAI_ENI_ATTR_OUTBOUND_V6_STAGE5_DASH_ACL_GROUP_ID", + "0" + ] + }, + { + "name": "eni_ether_address_map_entry", + "op": "create", + "type": "SAI_OBJECT_TYPE_ENI_ETHER_ADDRESS_MAP_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "address": "00:AA:AA:AA:AA:00" + }, + "attributes": [ + "SAI_ENI_ETHER_ADDRESS_MAP_ENTRY_ATTR_ENI_ID", + "$eni_id" + ] + }, + { + "name": "inbound_routing_entry", + "op": "create", + "type": "SAI_OBJECT_TYPE_INBOUND_ROUTING_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "eni_id": "$eni_id", + "vni": "1000", + "sip": "10.10.2.0", + "sip_mask": "255.255.255.0", + "priority": 0 + }, + "attributes": [ + "SAI_INBOUND_ROUTING_ENTRY_ATTR_ACTION", + "SAI_INBOUND_ROUTING_ENTRY_ACTION_VXLAN_DECAP_PA_VALIDATE", + "SAI_INBOUND_ROUTING_ENTRY_ATTR_SRC_VNET_ID", + "$vnet_1" + ] + }, + { + "name": "pa_validation_entry", + "op": "create", + "type": "SAI_OBJECT_TYPE_PA_VALIDATION_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "sip": "10.10.2.10", + "vnet_id": "$vnet_1" + }, + "attributes": [ + "SAI_PA_VALIDATION_ENTRY_ATTR_ACTION", + "SAI_PA_VALIDATION_ENTRY_ACTION_PERMIT" + ] + } +] diff --git a/test/test-cases/scale/saic/vnet_outbound_setup_commands_scale.json b/test/test-cases/scale/saic/vnet_outbound_setup_commands_scale.json new file mode 100644 index 000000000..564e8798a --- /dev/null +++ b/test/test-cases/scale/saic/vnet_outbound_setup_commands_scale.json @@ -0,0 +1,366 @@ +[ + { + "name": "vpe_#0", + "op": "create", + "type": "SAI_OBJECT_TYPE_VIP_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "vip": "172.16.1.100" + }, + "attributes": [ + "SAI_VIP_ENTRY_ATTR_ACTION", "SAI_VIP_ENTRY_ACTION_ACCEPT" + ] + }, + { + "name": "vpe_#1", + "op": "create", + "type": "SAI_OBJECT_TYPE_VIP_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "vip": "172.17.1.100" + }, + "attributes": [ + "SAI_VIP_ENTRY_ATTR_ACTION", "SAI_VIP_ENTRY_ACTION_ACCEPT" + ] + }, + { + "name": "dle_#0", + "op": "create", + "type": "SAI_OBJECT_TYPE_DIRECTION_LOOKUP_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "vni": "5000" + }, + "attributes": [ + "SAI_DIRECTION_LOOKUP_ENTRY_ATTR_ACTION", "SAI_DIRECTION_LOOKUP_ENTRY_ACTION_SET_OUTBOUND_DIRECTION" + ] + }, + { + "name": "dle_#1", + "op": "create", + "type": "SAI_OBJECT_TYPE_DIRECTION_LOOKUP_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "vni": "6000" + }, + "attributes": [ + "SAI_DIRECTION_LOOKUP_ENTRY_ATTR_ACTION", "SAI_DIRECTION_LOOKUP_ENTRY_ACTION_SET_OUTBOUND_DIRECTION" + ] + }, + { + "name": "vnet_#0", + "op": "create", + "type": "SAI_OBJECT_TYPE_VNET", + "attributes": [ + "SAI_VNET_ATTR_VNI", "1000" + ] + }, + { + "name": "vnet_#1", + "op": "create", + "type": "SAI_OBJECT_TYPE_VNET", + "attributes": [ + "SAI_VNET_ATTR_VNI", "2000" + ] + }, + { + "name": "vnet_#2", + "op": "create", + "type": "SAI_OBJECT_TYPE_VNET", + "attributes": [ + "SAI_VNET_ATTR_VNI", "3000" + ] + }, + { + "name": "vnet_#3", + "op": "create", + "type": "SAI_OBJECT_TYPE_VNET", + "attributes": [ + "SAI_VNET_ATTR_VNI", "4000" + ] + }, + { + "name": "vnet_#4", + "op": "create", + "type": "SAI_OBJECT_TYPE_VNET", + "attributes": [ + "SAI_VNET_ATTR_VNI", "5000" + ] + }, + { + "name": "vnet_#5", + "op": "create", + "type": "SAI_OBJECT_TYPE_VNET", + "attributes": [ + "SAI_VNET_ATTR_VNI", "6000" + ] + }, + { + "name": "eni_#0", + "op": "create", + "type": "SAI_OBJECT_TYPE_ENI", + "attributes": [ + "SAI_ENI_ATTR_CPS", "10000", + "SAI_ENI_ATTR_PPS", "100000", + "SAI_ENI_ATTR_FLOWS", "100000", + "SAI_ENI_ATTR_ADMIN_STATE", "True", + "SAI_ENI_ATTR_VM_UNDERLAY_DIP", "172.16.1.1", + "SAI_ENI_ATTR_VM_VNI", "9", + "SAI_ENI_ATTR_VNET_ID", "$vnet_#4", + "SAI_ENI_ATTR_INBOUND_V4_STAGE1_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V4_STAGE2_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V4_STAGE3_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V4_STAGE4_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V4_STAGE5_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V6_STAGE1_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V6_STAGE2_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V6_STAGE3_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V6_STAGE4_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V6_STAGE5_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V4_STAGE1_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V4_STAGE2_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V4_STAGE3_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V4_STAGE4_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V4_STAGE5_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V6_STAGE1_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V6_STAGE2_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V6_STAGE3_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V6_STAGE4_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V6_STAGE5_DASH_ACL_GROUP_ID", "0" + ] + }, + { + "name": "eni_#1", + "op": "create", + "type": "SAI_OBJECT_TYPE_ENI", + "attributes": [ + "SAI_ENI_ATTR_CPS", "10000", + "SAI_ENI_ATTR_PPS", "100000", + "SAI_ENI_ATTR_FLOWS", "100000", + "SAI_ENI_ATTR_ADMIN_STATE", "True", + "SAI_ENI_ATTR_VM_UNDERLAY_DIP", "172.16.2.1", + "SAI_ENI_ATTR_VM_VNI", "10", + "SAI_ENI_ATTR_VNET_ID", "$vnet_#5", + "SAI_ENI_ATTR_INBOUND_V4_STAGE1_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V4_STAGE2_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V4_STAGE3_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V4_STAGE4_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V4_STAGE5_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V6_STAGE1_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V6_STAGE2_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V6_STAGE3_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V6_STAGE4_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V6_STAGE5_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V4_STAGE1_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V4_STAGE2_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V4_STAGE3_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V4_STAGE4_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V4_STAGE5_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V6_STAGE1_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V6_STAGE2_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V6_STAGE3_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V6_STAGE4_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V6_STAGE5_DASH_ACL_GROUP_ID", "0" + ] + }, + { + "name": "eam_#0", + "op": "create", + "type": "SAI_OBJECT_TYPE_ENI_ETHER_ADDRESS_MAP_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "address": "00:CC:CC:CC:00:00" + }, + "attributes": [ + "SAI_ENI_ETHER_ADDRESS_MAP_ENTRY_ATTR_ENI_ID", "$eni_#0" + ] + }, + { + "name": "eam_#1", + "op": "create", + "type": "SAI_OBJECT_TYPE_ENI_ETHER_ADDRESS_MAP_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "address": "00:CC:CC:CC:00:01" + }, + "attributes": [ + "SAI_ENI_ETHER_ADDRESS_MAP_ENTRY_ATTR_ENI_ID", "$eni_#1" + ] + }, + { + "name": "ore_#0", + "op": "create", + "type": "SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "eni_id": "$eni_#0", + "destination": "10.1.1.0/31" + }, + "attributes": [ + "SAI_OUTBOUND_ROUTING_ENTRY_ATTR_ACTION", "SAI_OUTBOUND_ROUTING_ENTRY_ACTION_ROUTE_VNET", + "SAI_OUTBOUND_ROUTING_ENTRY_ATTR_DST_VNET_ID", "$vnet_#0" + ] + }, + { + "name": "ore_#1", + "op": "create", + "type": "SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "eni_id": "$eni_#0", + "destination": "10.1.1.2/31" + }, + "attributes": [ + "SAI_OUTBOUND_ROUTING_ENTRY_ATTR_ACTION", "SAI_OUTBOUND_ROUTING_ENTRY_ACTION_ROUTE_VNET", + "SAI_OUTBOUND_ROUTING_ENTRY_ATTR_DST_VNET_ID", "$vnet_#1" + ] + }, + { + "name": "ore_#2", + "op": "create", + "type": "SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "eni_id": "$eni_#1", + "destination": "10.1.1.0/31" + }, + "attributes": [ + "SAI_OUTBOUND_ROUTING_ENTRY_ATTR_ACTION", "SAI_OUTBOUND_ROUTING_ENTRY_ACTION_ROUTE_VNET", + "SAI_OUTBOUND_ROUTING_ENTRY_ATTR_DST_VNET_ID", "$vnet_#2" + ] + }, + { + "name": "ore_#3", + "op": "create", + "type": "SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "eni_id": "$eni_#1", + "destination": "10.1.1.2/31" + }, + "attributes": [ + "SAI_OUTBOUND_ROUTING_ENTRY_ATTR_ACTION", "SAI_OUTBOUND_ROUTING_ENTRY_ACTION_ROUTE_VNET", + "SAI_OUTBOUND_ROUTING_ENTRY_ATTR_DST_VNET_ID", "$vnet_#3" + ] + }, + { + "name": "ocpe_#0", + "op": "create", + "type": "SAI_OBJECT_TYPE_OUTBOUND_CA_TO_PA_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "dst_vnet_id": "$vnet_#0", + "dip": "10.1.1.0" + }, + "attributes": [ + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_UNDERLAY_DIP", "172.16.1.20", + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_OVERLAY_DMAC", "00:DD:DD:DD:DD:00", + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_USE_DST_VNET_VNI", "True" + ] + }, + { + "name": "ocpe_#1", + "op": "create", + "type": "SAI_OBJECT_TYPE_OUTBOUND_CA_TO_PA_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "dst_vnet_id": "$vnet_#0", + "dip": "10.1.1.1" + }, + "attributes": [ + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_UNDERLAY_DIP", "172.16.2.20", + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_OVERLAY_DMAC", "00:DD:DD:DD:DD:01", + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_USE_DST_VNET_VNI", "True" + ] + }, + { + "name": "ocpe_#2", + "op": "create", + "type": "SAI_OBJECT_TYPE_OUTBOUND_CA_TO_PA_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "dst_vnet_id": "$vnet_#1", + "dip": "10.1.1.2" + }, + "attributes": [ + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_UNDERLAY_DIP", "172.16.3.20", + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_OVERLAY_DMAC", "00:DD:DD:DD:DD:02", + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_USE_DST_VNET_VNI", "True" + ] + }, + { + "name": "ocpe_#3", + "op": "create", + "type": "SAI_OBJECT_TYPE_OUTBOUND_CA_TO_PA_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "dst_vnet_id": "$vnet_#1", + "dip": "10.1.1.3" + }, + "attributes": [ + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_UNDERLAY_DIP", "172.16.4.20", + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_OVERLAY_DMAC", "00:DD:DD:DD:DD:03", + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_USE_DST_VNET_VNI", "True" + ] + }, + { + "name": "ocpe_#4", + "op": "create", + "type": "SAI_OBJECT_TYPE_OUTBOUND_CA_TO_PA_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "dst_vnet_id": "$vnet_#2", + "dip": "10.1.1.0" + }, + "attributes": [ + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_UNDERLAY_DIP", "172.16.5.20", + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_OVERLAY_DMAC", "00:DD:DD:DD:DD:04", + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_USE_DST_VNET_VNI", "True" + ] + }, + { + "name": "ocpe_#5", + "op": "create", + "type": "SAI_OBJECT_TYPE_OUTBOUND_CA_TO_PA_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "dst_vnet_id": "$vnet_#2", + "dip": "10.1.1.1" + }, + "attributes": [ + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_UNDERLAY_DIP", "172.16.6.20", + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_OVERLAY_DMAC", "00:DD:DD:DD:DD:05", + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_USE_DST_VNET_VNI", "True" + ] + }, + { + "name": "ocpe_#6", + "op": "create", + "type": "SAI_OBJECT_TYPE_OUTBOUND_CA_TO_PA_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "dst_vnet_id": "$vnet_#3", + "dip": "10.1.1.2" + }, + "attributes": [ + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_UNDERLAY_DIP", "172.16.7.20", + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_OVERLAY_DMAC", "00:DD:DD:DD:DD:06", + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_USE_DST_VNET_VNI", "True" + ] + }, + { + "name": "ocpe_#7", + "op": "create", + "type": "SAI_OBJECT_TYPE_OUTBOUND_CA_TO_PA_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "dst_vnet_id": "$vnet_#3", + "dip": "10.1.1.3" + }, + "attributes": [ + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_UNDERLAY_DIP", "172.16.8.20", + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_OVERLAY_DMAC", "00:DD:DD:DD:DD:07", + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_USE_DST_VNET_VNI", "True" + ] + } +] diff --git a/test/test-cases/scale/saic/vnet_outbound_setup_commands_simple.json b/test/test-cases/scale/saic/vnet_outbound_setup_commands_simple.json new file mode 100644 index 000000000..087132275 --- /dev/null +++ b/test/test-cases/scale/saic/vnet_outbound_setup_commands_simple.json @@ -0,0 +1,125 @@ +[ + { + "name": "vpe", + "op": "create", + "type": "SAI_OBJECT_TYPE_VIP_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "vip": "172.16.1.100" + }, + "attributes": [ + "SAI_VIP_ENTRY_ATTR_ACTION", "SAI_VIP_ENTRY_ACTION_ACCEPT" + ] + }, + { + "name": "dle", + "op": "create", + "type": "SAI_OBJECT_TYPE_DIRECTION_LOOKUP_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "vni": "100" + }, + "attributes": [ + "SAI_DIRECTION_LOOKUP_ENTRY_ATTR_ACTION", "SAI_DIRECTION_LOOKUP_ENTRY_ACTION_SET_OUTBOUND_DIRECTION" + ] + }, + { + "name": "in_acl_group_id", + "op": "create", + "type": "SAI_OBJECT_TYPE_DASH_ACL_GROUP", + "attributes": [ + "SAI_DASH_ACL_GROUP_ATTR_IP_ADDR_FAMILY", "SAI_IP_ADDR_FAMILY_IPV4" + ] + }, + { + "name": "out_acl_group_id", + "op": "create", + "type": "SAI_OBJECT_TYPE_DASH_ACL_GROUP", + "attributes": [ + "SAI_DASH_ACL_GROUP_ATTR_IP_ADDR_FAMILY", "SAI_IP_ADDR_FAMILY_IPV4" + ] + }, + { + "name": "vnet", + "op": "create", + "type": "SAI_OBJECT_TYPE_VNET", + "attributes": [ + "SAI_VNET_ATTR_VNI", "1000" + ] + }, + { + "name": "eni", + "op": "create", + "type": "SAI_OBJECT_TYPE_ENI", + "attributes": [ + "SAI_ENI_ATTR_CPS", "10000", + "SAI_ENI_ATTR_PPS", "100000", + "SAI_ENI_ATTR_FLOWS", "100000", + "SAI_ENI_ATTR_ADMIN_STATE", "True", + "SAI_ENI_ATTR_VM_UNDERLAY_DIP", "172.16.1.1", + "SAI_ENI_ATTR_VM_VNI", "9", + "SAI_ENI_ATTR_VNET_ID", "$vnet", + "SAI_ENI_ATTR_INBOUND_V4_STAGE1_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V4_STAGE2_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V4_STAGE3_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V4_STAGE4_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V4_STAGE5_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V6_STAGE1_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V6_STAGE2_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V6_STAGE3_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V6_STAGE4_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_INBOUND_V6_STAGE5_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V4_STAGE1_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V4_STAGE2_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V4_STAGE3_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V4_STAGE4_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V4_STAGE5_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V6_STAGE1_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V6_STAGE2_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V6_STAGE3_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V6_STAGE4_DASH_ACL_GROUP_ID", "0", + "SAI_ENI_ATTR_OUTBOUND_V6_STAGE5_DASH_ACL_GROUP_ID", "0" + ] + }, + { + "name": "eam", + "op": "create", + "type": "SAI_OBJECT_TYPE_ENI_ETHER_ADDRESS_MAP_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "address": "00:CC:CC:CC:00:00" + }, + "attributes": [ + "SAI_ENI_ETHER_ADDRESS_MAP_ENTRY_ATTR_ENI_ID", "$eni" + ] + }, + { + "name": "ore", + "op": "create", + "type": "SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "eni_id": "$eni", + "destination": "10.1.0.0/16" + }, + "attributes": [ + "SAI_OUTBOUND_ROUTING_ENTRY_ATTR_ACTION", "SAI_OUTBOUND_ROUTING_ENTRY_ACTION_ROUTE_VNET", + "SAI_OUTBOUND_ROUTING_ENTRY_ATTR_DST_VNET_ID", "$vnet" + ] + }, + { + "name": "ocpe", + "op": "create", + "type": "SAI_OBJECT_TYPE_OUTBOUND_CA_TO_PA_ENTRY", + "key": { + "switch_id": "$SWITCH_ID", + "dst_vnet_id": "$vnet", + "dip": "10.1.2.50" + }, + "attributes": [ + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_UNDERLAY_DIP", "172.16.1.20", + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_OVERLAY_DMAC", "00:DD:DD:DD:00:00", + "SAI_OUTBOUND_CA_TO_PA_ENTRY_ATTR_USE_DST_VNET_VNI", "True" + ] + } +] diff --git a/test/third-party/traffic_gen/deployment/.env b/test/third-party/traffic_gen/deployment/.env index 72ec4ae3b..a15c34c10 100644 --- a/test/third-party/traffic_gen/deployment/.env +++ b/test/third-party/traffic_gen/deployment/.env @@ -1,6 +1,6 @@ DOCKER_REGISTRY=ghcr.io/open-traffic-generator -CONTROLLER_VERSION=0.0.1-3383 -TRAFFIC_ENGINE_VERSION=1.6.0.17 +CONTROLLER_VERSION=0.0.1-3587 +TRAFFIC_ENGINE_VERSION=1.6.0.19 IFC1=veth1 IFC2=veth3 TCP_PORT_IFC1=5555