From f80f55b6db84cf822826e71ad0fa77d799977d8c Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Fri, 7 Aug 2026 19:02:42 +0000 Subject: [PATCH 1/2] [ci]: complete sonic-swss CI-unification cutover Land all sonic-swss-owned migration surfaces in one change. Build and local development: - replace the local container-setup.py artifact installer and duplicated Build-template setup with sonic-swss-common's shared buildenv_setup - add declarative Build/Test packages and immediate upstream artifacts; inherit sairedis -> swss-common -> common-libs/VPP transitively - add the canonical build.sh, preserve the existing non-root/custom local development workflow, and publish build-env/ additively with the existing top-level DEBs/coverage artifacts - expose repo + staged_upstreams so upstream pipelines can reuse SWSS's canonical Build template with required same-run artifacts Shared docker-sonic-vs stack: - replace the repo-specific BuildDocker template with a swss-owned reusable build-docker-template - parameterize debug package, VPP, gcov, nexthopgroup, and current-run swss-common/sairedis artifact sources while keeping SWSS behavior default - parameterize the Dockerfile without changing its current package set, coverage source handling, or ASAN image behavior Shared VS test stack: - replace the imperative local test template with run-vs-tests-template - use buildenv_setup --scope test and packages/test.yaml for the shared host environment, while retaining module setup, coverage, ASAN, logging, JUnit, retry/parallel execution, and the SWSS single_asic_voq test pass - provide neutral defaults plus staged_upstreams/repo parameters for later sonic-sairedis and sonic-swss-common adoption The deprecated azure-pipelines-dash.yml is intentionally unchanged. Consumer repo references and the final required shared-infra gate remain follow-up PRs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 19e0b615-271d-48f0-98cc-7ad6905c7f9e Signed-off-by: Lawrence Lee --- .artifactignore | 4 + .../build-docker-sonic-vs-template.yml | 201 ------------- .azure-pipelines/build-docker-template.yml | 248 +++++++++++++++ .azure-pipelines/build-template.yml | 284 +++++------------- .azure-pipelines/docker-sonic-vs/Dockerfile | 76 +++-- .azure-pipelines/run-vs-tests-template.yml | 277 +++++++++++++++++ .../test-docker-sonic-vs-template.yml | 261 ---------------- .gitignore | 1 + azure-pipelines.yml | 71 ++--- build-env/Dockerfile | 49 ++- build-env/README.md | 146 +++++---- build-env/build.sh | 24 ++ build-env/compose.yaml | 9 +- build-env/container-setup.py | 144 --------- build-env/packages/base.yaml | 12 + build-env/packages/test.yaml | 34 +++ build-env/packages/tooling.yaml | 4 + build-env/upstream-artifacts.yaml | 78 +++++ 18 files changed, 958 insertions(+), 965 deletions(-) delete mode 100644 .azure-pipelines/build-docker-sonic-vs-template.yml create mode 100644 .azure-pipelines/build-docker-template.yml create mode 100644 .azure-pipelines/run-vs-tests-template.yml delete mode 100644 .azure-pipelines/test-docker-sonic-vs-template.yml create mode 100755 build-env/build.sh delete mode 100644 build-env/container-setup.py create mode 100644 build-env/packages/base.yaml create mode 100644 build-env/packages/test.yaml create mode 100644 build-env/packages/tooling.yaml create mode 100644 build-env/upstream-artifacts.yaml diff --git a/.artifactignore b/.artifactignore index cbaad306e2e..e1133c59b22 100644 --- a/.artifactignore +++ b/.artifactignore @@ -3,3 +3,7 @@ !coverage.info !coverage.xml !build.info +# Publish the declarative build environment beside the existing top-level +# artifacts so downstream consumers can walk the dependency cascade. +!build-env +!build-env/** diff --git a/.azure-pipelines/build-docker-sonic-vs-template.yml b/.azure-pipelines/build-docker-sonic-vs-template.yml deleted file mode 100644 index 1f7733c898f..00000000000 --- a/.azure-pipelines/build-docker-sonic-vs-template.yml +++ /dev/null @@ -1,201 +0,0 @@ -parameters: -- name: arch - type: string - values: - - amd64 - - armhf - - arm64 - default: amd64 - -- name: timeout - type: number - default: 60 - -- name: swss_artifact_name - type: string - -- name: swss_common_artifact_name - type: string - -- name: swss_common_branch - type: string - default: '$(BUILD_BRANCH)' - -- name: debian_version - type: string - -- name: vpp_artifact_name - type: string - default: 'vpp-bookworm' - -- name: artifact_name - type: string - -- name: buildimage_artifact_name - type: string - default: 'sonic-buildimage.vs' - -- name: buildimage_artifact_project - type: string - default: 'build' - -- name: buildimage_artifact_pipeline - type: string - default: 'Azure.sonic-buildimage.official.vs' - -- name: buildimage_artifact_branch - type: string - default: '$(BUILD_BRANCH)' - -- name: sairedis_artifact_name - type: string - -- name: sairedis_artifact_project - type: string - default: 'build' - -- name: sairedis_artifact_pipeline - type: string - default: 'Azure.sonic-sairedis' - -- name: sairedis_artifact_branch - type: string - default: '$(BUILD_BRANCH)' - -- name: sairedis_artifact_pattern - type: string - default: '**' - -- name: asan - type: boolean - default: false - -jobs: -- job: - displayName: ${{ parameters.arch }} - timeoutInMinutes: ${{ parameters.timeout }} - - pool: - vmImage: 'ubuntu-22.04' - - steps: - - checkout: self - - template: merge-target-branch.yml - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: build - pipeline: Azure.sonic-swss-common - artifact: ${{ parameters.swss_common_artifact_name }} - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/${{ parameters.swss_common_branch }}' - path: $(Build.ArtifactStagingDirectory)/download - allowPartiallySucceededBuilds: true - displayName: "Download sonic swss common deb packages" - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: ${{ parameters.sairedis_artifact_project }} - pipeline: ${{ parameters.sairedis_artifact_pipeline }} - artifact: ${{ parameters.sairedis_artifact_name }} - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/${{ parameters.sairedis_artifact_branch }}' - allowPartiallySucceededBuilds: true - path: $(Build.ArtifactStagingDirectory)/download/sairedis - patterns: | - ${{ parameters.sairedis_artifact_pattern }}/libsaivs_*.deb - ${{ parameters.sairedis_artifact_pattern }}/libsaivs-dev_*.deb - ${{ parameters.sairedis_artifact_pattern }}/libsairedis_*.deb - ${{ parameters.sairedis_artifact_pattern }}/libsairedis-dev_*.deb - ${{ parameters.sairedis_artifact_pattern }}/libsaimetadata_*.deb - ${{ parameters.sairedis_artifact_pattern }}/libsaimetadata-dev_*.deb - ${{ parameters.sairedis_artifact_pattern }}/syncd-vs_*.deb - displayName: "Download sonic sairedis deb packages" - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: build - pipeline: sonic-net.sonic-dash-api - ${{ if eq(parameters.arch, 'amd64') }}: - artifact: sonic-dash-api - ${{ else }}: - artifact: sonic-dash-api.${{ parameters.arch }} - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/$(BUILD_BRANCH)' - path: $(Build.ArtifactStagingDirectory)/download - patterns: | - libdashapi*.deb - displayName: "Download dash api" - - task: DownloadPipelineArtifact@2 - inputs: - artifact: ${{ parameters.swss_artifact_name }} - path: $(Build.ArtifactStagingDirectory)/download - displayName: "Download pre-stage built ${{ parameters.swss_artifact_name }}" - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: ${{ parameters.buildimage_artifact_project }} - pipeline: ${{ parameters.buildimage_artifact_pipeline }} - artifact: ${{ parameters.buildimage_artifact_name }} - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/${{ parameters.buildimage_artifact_branch }}' - path: $(Build.ArtifactStagingDirectory)/download - patterns: '**/target/${{ parameters.artifact_name }}.gz' - displayName: "Download sonic-buildimage ${{ parameters.artifact_name }}" - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: ${{ parameters.buildimage_artifact_project }} - pipeline: ${{ parameters.buildimage_artifact_pipeline }} - artifact: ${{ parameters.buildimage_artifact_name }} - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/${{ parameters.buildimage_artifact_branch }}' - path: $(Build.ArtifactStagingDirectory)/download - patterns: '**/target/debs/${{ parameters.debian_version }}/framework_*.deb' - displayName: "Download sonic-buildimage sonic-framework package" - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: build - pipeline: sonic-net.sonic-platform-vpp - artifact: ${{ parameters.vpp_artifact_name }} - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/master' - allowPartiallySucceededBuilds: true - path: $(Build.ArtifactStagingDirectory)/download - displayName: "Download sonic platform-vpp deb packages" - condition: eq('${{ parameters.arch }}', 'amd64') - - script: | - set -ex - echo $(Build.DefinitionName).$(Build.BuildNumber) - - docker load < $(Build.ArtifactStagingDirectory)/download/target/${{ parameters.artifact_name }}.gz - - mkdir -p .azure-pipelines/docker-sonic-vs/debs - - find $(Build.ArtifactStagingDirectory)/download/sairedis -name '*.deb' -exec cp "{}" .azure-pipelines/docker-sonic-vs/debs \; - cp -v $(Build.ArtifactStagingDirectory)/download/*.deb .azure-pipelines/docker-sonic-vs/debs - if [ -f $(Build.ArtifactStagingDirectory)/download/coverage.info ]; then - cp -v $(Build.ArtifactStagingDirectory)/download/coverage.info $(Build.ArtifactStagingDirectory)/ - cp -v $(Build.ArtifactStagingDirectory)/download/coverage.xml $(Build.ArtifactStagingDirectory)/ - fi - - pushd .azure-pipelines - ls -l docker-sonic-vs/debs - - build_dir=$(grep BUILD_DIR $(Build.ArtifactStagingDirectory)/download/build.info | cut -d= -f2) - build_args="--build-arg build_dir=$build_dir" - if [ '${{ parameters.asan }}' == True ]; then - build_args="$build_args --build-arg need_dbg=y" - fi - - docker build $build_args --no-cache -t docker-sonic-vs:$(Build.DefinitionName).$(Build.BuildNumber).asan-${{ parameters.asan }} docker-sonic-vs - - popd - - docker save docker-sonic-vs:$(Build.DefinitionName).$(Build.BuildNumber).asan-${{ parameters.asan }} | gzip -c > $(Build.ArtifactStagingDirectory)/docker-sonic-vs.gz - rm -rf $(Build.ArtifactStagingDirectory)/download - displayName: "Build ${{ parameters.artifact_name }}" - - publish: $(Build.ArtifactStagingDirectory)/ - artifact: ${{ parameters.artifact_name }} - displayName: "Archive sonic docker vs image" diff --git a/.azure-pipelines/build-docker-template.yml b/.azure-pipelines/build-docker-template.yml new file mode 100644 index 00000000000..dbf02e70a49 --- /dev/null +++ b/.azure-pipelines/build-docker-template.yml @@ -0,0 +1,248 @@ +parameters: +- name: repo + type: string + default: self + +- name: arch + type: string + values: [amd64] + default: amd64 + +- name: timeout + type: number + default: 60 + +- name: swss_artifact_name + type: string + +- name: sairedis_artifact_name + type: string + +- name: sairedis_current_run + type: boolean + default: false + +- name: swss_common_artifact_name + type: string + +- name: swss_common_current_run + type: boolean + default: false + +- name: artifact_name + type: string + +- name: debian_version + type: string + default: bookworm + +- name: vpp_artifact_name + type: string + default: vpp-bookworm + +- name: buildimage_artifact_name + type: string + default: sonic-buildimage.vs + +- name: buildimage_artifact_project + type: string + default: build + +- name: buildimage_artifact_pipeline + type: string + default: Azure.sonic-buildimage.official.vs + +- name: buildimage_artifact_branch + type: string + default: '$(BUILD_BRANCH)' + +- name: debug_pkg + type: string + default: swss-dbg + +- name: include_gcov + type: boolean + default: true + +- name: include_vpp + type: boolean + default: true + +- name: include_nexthopgroup + type: boolean + default: false + +- name: asan + type: boolean + default: false + +jobs: +- job: + displayName: ${{ parameters.arch }} + timeoutInMinutes: ${{ parameters.timeout }} + + pool: + vmImage: 'ubuntu-22.04' + + steps: + - checkout: ${{ parameters.repo }} + clean: true + + - ${{ if eq(parameters.repo, 'self') }}: + - template: merge-target-branch.yml + + - ${{ if eq(parameters.swss_common_current_run, true) }}: + - task: DownloadPipelineArtifact@2 + inputs: + artifact: ${{ parameters.swss_common_artifact_name }} + path: $(Build.ArtifactStagingDirectory)/download + displayName: "Download current-run ${{ parameters.swss_common_artifact_name }}" + - ${{ else }}: + - task: DownloadPipelineArtifact@2 + inputs: + source: specific + project: build + pipeline: Azure.sonic-swss-common + artifact: ${{ parameters.swss_common_artifact_name }} + runVersion: latestFromBranch + runBranch: refs/heads/$(BUILD_BRANCH) + allowPartiallySucceededBuilds: true + path: $(Build.ArtifactStagingDirectory)/download + displayName: "Download sonic-swss-common artifacts" + + - ${{ if eq(parameters.sairedis_current_run, true) }}: + - task: DownloadPipelineArtifact@2 + inputs: + artifact: ${{ parameters.sairedis_artifact_name }} + path: $(Build.ArtifactStagingDirectory)/download/sairedis + displayName: "Download current-run ${{ parameters.sairedis_artifact_name }}" + - ${{ else }}: + - task: DownloadPipelineArtifact@2 + inputs: + source: specific + project: build + pipeline: Azure.sonic-sairedis + artifact: ${{ parameters.sairedis_artifact_name }} + runVersion: latestFromBranch + runBranch: refs/heads/$(BUILD_BRANCH) + allowPartiallySucceededBuilds: true + path: $(Build.ArtifactStagingDirectory)/download/sairedis + patterns: | + **/libsaivs_*.deb + **/libsaivs-dev_*.deb + **/libsairedis_*.deb + **/libsairedis-dev_*.deb + **/libsaimetadata_*.deb + **/libsaimetadata-dev_*.deb + **/syncd-vs_*.deb + displayName: "Download sonic-sairedis artifacts" + + # The swss artifact is always produced earlier in the current consumer run. + - task: DownloadPipelineArtifact@2 + inputs: + artifact: ${{ parameters.swss_artifact_name }} + path: $(Build.ArtifactStagingDirectory)/download + displayName: "Download current-run ${{ parameters.swss_artifact_name }}" + + - task: DownloadPipelineArtifact@2 + inputs: + source: specific + project: build + pipeline: sonic-net.sonic-dash-api + ${{ if eq(parameters.arch, 'amd64') }}: + artifact: sonic-dash-api + ${{ else }}: + artifact: sonic-dash-api.${{ parameters.arch }} + runVersion: latestFromBranch + runBranch: refs/heads/$(BUILD_BRANCH) + path: $(Build.ArtifactStagingDirectory)/download + patterns: | + libdashapi*.deb + displayName: "Download dash-api" + + - task: DownloadPipelineArtifact@2 + inputs: + source: specific + project: ${{ parameters.buildimage_artifact_project }} + pipeline: ${{ parameters.buildimage_artifact_pipeline }} + artifact: ${{ parameters.buildimage_artifact_name }} + runVersion: latestFromBranch + runBranch: refs/heads/${{ parameters.buildimage_artifact_branch }} + path: $(Build.ArtifactStagingDirectory)/download + patterns: | + **/target/${{ parameters.artifact_name }}.gz + **/target/debs/${{ parameters.debian_version }}/framework_*.deb + displayName: "Download base ${{ parameters.artifact_name }} image" + + - ${{ if eq(parameters.include_vpp, true) }}: + - task: DownloadPipelineArtifact@2 + inputs: + source: specific + project: build + pipeline: sonic-net.sonic-platform-vpp + artifact: ${{ parameters.vpp_artifact_name }} + runVersion: latestFromBranch + runBranch: refs/heads/master + allowPartiallySucceededBuilds: true + path: $(Build.ArtifactStagingDirectory)/download + displayName: "Download VPP artifacts" + + - ${{ if eq(parameters.include_nexthopgroup, true) }}: + - task: DownloadPipelineArtifact@2 + inputs: + source: specific + project: build + pipeline: Azure.sonic-buildimage.common_libs + artifact: common-lib + runVersion: latestFromBranch + runBranch: refs/heads/$(BUILD_BRANCH) + path: $(Build.ArtifactStagingDirectory)/download + patterns: '**/target/debs/*/libnexthopgroup_*.deb' + displayName: "Download libnexthopgroup" + + - script: | + set -ex + echo $(Build.DefinitionName).$(Build.BuildNumber) + + docker load < $(Build.ArtifactStagingDirectory)/download/target/${{ parameters.artifact_name }}.gz + + rm -rf .azure-pipelines/docker-sonic-vs/debs + mkdir -p .azure-pipelines/docker-sonic-vs/debs + find $(Build.ArtifactStagingDirectory)/download/sairedis -name '*.deb' -exec cp -v "{}" .azure-pipelines/docker-sonic-vs/debs \; + find $(Build.ArtifactStagingDirectory)/download -maxdepth 1 -name '*.deb' -exec cp -v "{}" .azure-pipelines/docker-sonic-vs/debs \; + find $(Build.ArtifactStagingDirectory)/download -name 'libnexthopgroup_*.deb' -exec cp -v "{}" .azure-pipelines/docker-sonic-vs/debs \; + + if [ '${{ parameters.include_gcov }}' == True ] && [ -f $(Build.ArtifactStagingDirectory)/download/coverage.info ]; then + cp -v $(Build.ArtifactStagingDirectory)/download/coverage.info $(Build.ArtifactStagingDirectory)/ + cp -v $(Build.ArtifactStagingDirectory)/download/coverage.xml $(Build.ArtifactStagingDirectory)/ + fi + + build_dir="" + if [ '${{ parameters.include_gcov }}' == True ] && [ -f $(Build.ArtifactStagingDirectory)/download/build.info ]; then + build_dir=$(grep BUILD_DIR $(Build.ArtifactStagingDirectory)/download/build.info | cut -d= -f2) + fi + + build_args="--build-arg build_dir=$build_dir" + build_args="$build_args --build-arg debug_pkg=${{ parameters.debug_pkg }}" + build_args="$build_args --build-arg include_gcov=$([ '${{ parameters.include_gcov }}' == True ] && echo y || echo n)" + build_args="$build_args --build-arg include_vpp=$([ '${{ parameters.include_vpp }}' == True ] && echo y || echo n)" + build_args="$build_args --build-arg include_nexthopgroup=$([ '${{ parameters.include_nexthopgroup }}' == True ] && echo y || echo n)" + if [ '${{ parameters.asan }}' == True ]; then + build_args="$build_args --build-arg need_dbg=y" + fi + + pushd .azure-pipelines + docker build $build_args --no-cache \ + -t docker-sonic-vs:$(Build.DefinitionName).$(Build.BuildNumber).asan-${{ parameters.asan }} \ + docker-sonic-vs + popd + + docker save docker-sonic-vs:$(Build.DefinitionName).$(Build.BuildNumber).asan-${{ parameters.asan }} \ + | gzip -c > $(Build.ArtifactStagingDirectory)/docker-sonic-vs.gz + rm -rf $(Build.ArtifactStagingDirectory)/download + rm -rf .azure-pipelines/docker-sonic-vs/debs + displayName: "Build ${{ parameters.artifact_name }}" + + - publish: $(Build.ArtifactStagingDirectory)/ + artifact: ${{ parameters.artifact_name }} + displayName: "Archive sonic docker VS image" diff --git a/.azure-pipelines/build-template.yml b/.azure-pipelines/build-template.yml index 834bd0956f6..e83a8ce895d 100644 --- a/.azure-pipelines/build-template.yml +++ b/.azure-pipelines/build-template.yml @@ -1,4 +1,8 @@ parameters: +- name: repo + type: string + default: self + - name: arch type: string values: @@ -20,38 +24,6 @@ parameters: type: number default: 90 -- name: sonic_slave - type: string - -- name: sairedis_artifact_name - type: string - -- name: sairedis_artifact_project - type: string - default: 'build' - -- name: sairedis_artifact_pipeline - type: string - default: 'Azure.sonic-sairedis' - -- name: sairedis_artifact_branch - type: string - default: '$(BUILD_BRANCH)' - -- name: sairedis_artifact_pattern - type: string - default: '**' - -- name: swss_common_artifact_name - type: string - -- name: swss_common_branch - type: string - default: '$(BUILD_BRANCH)' - -- name: dash_artifact_name - type: string - - name: artifact_name type: string @@ -63,31 +35,19 @@ parameters: type: boolean default: false -- name: common_lib_artifact_name - type: string - -- name: common_lib_artifact_project - type: string - default: 'build' - -- name: common_lib_artifact_pipeline - type: string - default: 'Azure.sonic-buildimage.common_libs' - -- name: common_lib_artifact_branch - type: string - default: '$(BUILD_BRANCH)' - - name: asan type: boolean default: false -- name: debian_version - type: string +- name: staged_upstreams + # List of {artifact: , name: }. + # Reusers stage same-run sairedis/swss-common bundles for the cascade. + type: object + default: [] -- name: vpp_artifact_name +- name: debian_version type: string - default: 'vpp-bookworm' + default: bookworm jobs: - job: @@ -104,173 +64,87 @@ jobs: image: sonicdev-microsoft.azurecr.io:443/sonic-slave-${{ parameters.debian_version }}:$(BUILD_BRANCH)-${{ parameters.arch }} steps: - - checkout: self + - checkout: ${{ parameters.repo }} clean: true submodules: true - - template: merge-target-branch.yml - - script: | - set -xe - sudo apt-get update - sudo apt-get install -y \ - libhiredis-dev \ - libzmq3-dev \ - swig \ - libdbus-1-dev \ - libteam-dev - sudo pip3 install lcov_cobertura - sudo apt-get install -y redis-server - sudo sed -i 's/notify-keyspace-events ""/notify-keyspace-events AKE/' /etc/redis/redis.conf - sudo sed -ri 's/^# unixsocket/unixsocket/' /etc/redis/redis.conf - sudo sed -ri 's/^unixsocketperm .../unixsocketperm 777/' /etc/redis/redis.conf - sudo sed -ri 's/redis-server.sock/redis.sock/' /etc/redis/redis.conf - sudo service redis-server restart - displayName: "Install dependencies" - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: build - pipeline: Azure.sonic-swss-common - artifact: ${{ parameters.swss_common_artifact_name }} - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/${{ parameters.swss_common_branch }}' - allowPartiallySucceededBuilds: true - path: $(Build.ArtifactStagingDirectory)/download/swsscommon - patterns: | - libswsscommon_1.0.0_${{ parameters.arch }}.deb - libswsscommon-dev_1.0.0_${{ parameters.arch }}.deb - displayName: "Download sonic swss common deb packages" - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: ${{ parameters.sairedis_artifact_project }} - pipeline: ${{ parameters.sairedis_artifact_pipeline }} - artifact: ${{ parameters.sairedis_artifact_name }} - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/${{ parameters.sairedis_artifact_branch }}' - allowPartiallySucceededBuilds: true - path: $(Build.ArtifactStagingDirectory)/download/sairedis - patterns: | - ${{ parameters.sairedis_artifact_pattern }}/libsaivs_*.deb - ${{ parameters.sairedis_artifact_pattern }}/libsaivs-dev_*.deb - ${{ parameters.sairedis_artifact_pattern }}/libsairedis_*.deb - ${{ parameters.sairedis_artifact_pattern }}/libsairedis-dev_*.deb - ${{ parameters.sairedis_artifact_pattern }}/libsaimetadata_*.deb - ${{ parameters.sairedis_artifact_pattern }}/libsaimetadata-dev_*.deb - ${{ parameters.sairedis_artifact_pattern }}/syncd-vs_*.deb - displayName: "Download sonic sairedis deb packages" - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: ${{ parameters.common_lib_artifact_project }} - pipeline: ${{ parameters.common_lib_artifact_pipeline }} - artifact: ${{ parameters.common_lib_artifact_name }} - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/${{ parameters.common_lib_artifact_branch }}' - path: $(Build.ArtifactStagingDirectory)/download/common - patterns: | - target/debs/${{ parameters.debian_version }}/libnl-3-200_*.deb - target/debs/${{ parameters.debian_version }}/libnl-3-dev_*.deb - target/debs/${{ parameters.debian_version }}/libnl-genl-3-200_*.deb - target/debs/${{ parameters.debian_version }}/libnl-genl-3-dev_*.deb - target/debs/${{ parameters.debian_version }}/libnl-route-3-200_*.deb - target/debs/${{ parameters.debian_version }}/libnl-route-3-dev_*.deb - target/debs/${{ parameters.debian_version }}/libnl-nf-3-200_*.deb - target/debs/${{ parameters.debian_version }}/libnl-nf-3-dev_*.deb - target/debs/${{ parameters.debian_version }}/libyang3_*.deb - displayName: "Download common libs" - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: ${{ parameters.common_lib_artifact_project }} - pipeline: ${{ parameters.common_lib_artifact_pipeline }} - artifact: ${{ parameters.common_lib_artifact_name }} - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/${{ parameters.common_lib_artifact_branch }}' - path: $(Build.ArtifactStagingDirectory)/download/common - patterns: | - target/debs/${{ parameters.debian_version }}/libpcre3_*.deb - displayName: "Download Trixie-specific common libs" - condition: eq('${{ parameters.debian_version }}', 'trixie') - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: build - pipeline: sonic-net.sonic-dash-api - artifact: ${{ parameters.dash_artifact_name }} - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/$(BUILD_BRANCH)' - path: $(Build.ArtifactStagingDirectory)/download/common - patterns: | - libdashapi*.deb - displayName: "Download dash api" - - script: | - set -ex - cd download - sudo dpkg -i $(find common -type f -name '*.deb') - cd .. - workingDirectory: $(Build.ArtifactStagingDirectory) - displayName: "Install SONiC-built dependencies" - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: build - pipeline: sonic-net.sonic-platform-vpp - artifact: ${{ parameters.vpp_artifact_name }} - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/master' - allowPartiallySucceededBuilds: true - path: $(Build.ArtifactStagingDirectory)/download - displayName: "Download sonic platform-vpp deb packages" - condition: eq('${{ parameters.arch }}', 'amd64') - - script: | - set -ex - sudo env VPP_INSTALL_SKIP_SYSCTL=1 dpkg -i download/libvppinfra-dev_*_${{ parameters.arch }}.deb - sudo env VPP_INSTALL_SKIP_SYSCTL=1 dpkg -i download/libvppinfra_*_${{ parameters.arch }}.deb - sudo env VPP_INSTALL_SKIP_SYSCTL=1 dpkg -i download/vpp_*_${{ parameters.arch }}.deb - sudo env VPP_INSTALL_SKIP_SYSCTL=1 dpkg -i download/vpp-crypto-engines_*_${{ parameters.arch }}.deb - sudo env VPP_INSTALL_SKIP_SYSCTL=1 dpkg -i download/vpp-dbg_*_${{ parameters.arch }}.deb - sudo env VPP_INSTALL_SKIP_SYSCTL=1 dpkg -i download/vpp-dev_*_${{ parameters.arch }}.deb - sudo env VPP_INSTALL_SKIP_SYSCTL=1 dpkg -i download/vpp-plugin-core_*_${{ parameters.arch }}.deb - sudo env VPP_INSTALL_SKIP_SYSCTL=1 dpkg -i download/vpp-plugin-devtools_*_${{ parameters.arch }}.deb - sudo env VPP_INSTALL_SKIP_SYSCTL=1 dpkg -i download/vpp-plugin-dpdk_*_${{ parameters.arch }}.deb - sudo env VPP_INSTALL_SKIP_SYSCTL=1 dpkg -i download/python3-vpp-api_*_${{ parameters.arch }}.deb - workingDirectory: $(Build.ArtifactStagingDirectory) - displayName: "Install sonic platform-vpp packages" - condition: eq('${{ parameters.arch }}', 'amd64') - - script: | - set -ex - cd download - sudo dpkg -i $(find swsscommon -type f -name '*.deb') - sudo dpkg -i $(find sairedis -type f -name '*.deb') - cd .. - rm -rf download - workingDirectory: $(Build.ArtifactStagingDirectory) - displayName: "Install sonic swss common and sairedis" + + - ${{ if eq(parameters.repo, 'self') }}: + - template: merge-target-branch.yml + + - ${{ each up in parameters.staged_upstreams }}: + - task: DownloadPipelineArtifact@2 + inputs: + artifact: ${{ up.artifact }} + path: $(Pipeline.Workspace)/staged/${{ up.name }} + displayName: "Stage current-run ${{ up.name }} for the cascade" + - script: | set -ex - tar czf pytest.tgz tests - cp -r pytest.tgz $(Build.ArtifactStagingDirectory)/ - if [ '${{ parameters.archive_gcov }}' == True ]; then - export ENABLE_GCOV=y - echo BUILD_DIR=$(pwd) > build.info - fi - if [ '${{ parameters.asan }}' == True ]; then - export ENABLE_ASAN=y + sudo apt-get update + sudo apt-get install -qq -y python3-yaml python3-requests git jq + + clone_swss_common() { + ref="$1" + for delay in 0 5 15; do + [ "$delay" -eq 0 ] || sleep "$delay" + rm -rf /tmp/sw-common + git clone --depth 1 --branch "$ref" \ + https://github.com/sonic-net/sonic-swss-common /tmp/sw-common && return 0 + done + return 1 + } + + SETUP_BRANCH="$(BUILD_BRANCH)" + if ! clone_swss_common "$SETUP_BRANCH"; then + case "$(BUILD_BRANCH)" in + master|20[0-9][0-9][0-9][0-9]) + echo "Failed to clone required sonic-swss-common branch $(BUILD_BRANCH)" >&2 + exit 1 + ;; + *) + echo "No matching swss-common feature branch; falling back to master" + clone_swss_common master + SETUP_BRANCH=master + ;; + esac fi - ./autogen.sh - RUSTFLAGS=-Dwarnings dpkg-buildpackage -us -uc -b -j$(nproc) && cp ../*.deb . - displayName: "Compile sonic swss" - - script: | - RUSTFLAGS=-Dwarnings cargo test - displayName: "Test countersyncd" + + REQ_ARGS=$(echo "$STAGED_UPSTREAMS_JSON" \ + | jq -r '.[] | .name' \ + | xargs -I{} echo --required-staged-upstream {} \ + | tr '\n' ' ') + PYTHONPATH=/tmp/sw-common/ci python3 -m buildenv_setup \ + --repo-dir $(Build.SourcesDirectory) \ + --scope build \ + --arch ${{ parameters.arch }} \ + --debian-version ${{ parameters.debian_version }} \ + --branch "$SETUP_BRANCH" \ + --upstream-staged-dir $(Pipeline.Workspace)/staged \ + $REQ_ARGS + displayName: "Set up build environment (buildenv_setup)" + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + STAGED_UPSTREAMS_JSON: ${{ convertToJson(parameters.staged_upstreams) }} + + - ${{ if eq(parameters.archive_pytests, true) }}: + - script: | + set -ex + tar czf $(Build.ArtifactStagingDirectory)/pytest.tgz tests + displayName: "Archive SWSS pytest sources" + + - script: GCOV=${{ parameters.archive_gcov }} ASAN=${{ parameters.asan }} ./build-env/build.sh + displayName: "Compile and test sonic-swss" + - task: PublishTestResults@2 inputs: testResultsFiles: '**/*_tr.xml' testRunTitle: gtest condition: and(succeededOrFailed(), eq('${{ parameters.asan }}', false)) + - publish: $(System.DefaultWorkingDirectory)/ artifact: ${{ parameters.artifact_name }} displayName: "Archive swss debian packages" + - publish: $(Build.ArtifactStagingDirectory)/ artifact: sonic-swss-pytests displayName: "Archive swss pytests" diff --git a/.azure-pipelines/docker-sonic-vs/Dockerfile b/.azure-pipelines/docker-sonic-vs/Dockerfile index 41733ffda13..f741fd5b250 100644 --- a/.azure-pipelines/docker-sonic-vs/Dockerfile +++ b/.azure-pipelines/docker-sonic-vs/Dockerfile @@ -1,39 +1,36 @@ FROM docker-sonic-vs ARG docker_container_name -ARG need_dbg +ARG need_dbg=n +ARG debug_pkg=swss-dbg ARG build_dir +ARG include_gcov=y +ARG include_vpp=y +ARG include_nexthopgroup=n ENV BUILD_DIR=$build_dir COPY ["debs", "/debs"] -# Remove the libswsscommon package first with force all option. -# Remove the other existing packages before installing the new/current packages. This is to overcome limitations with -# Docker's diff detection mechanism, where only the file size and the modification timestamp (which will remain the -# same, even though contents have changed) are checked between the previous and current layer. +# Remove the existing packages before installing current artifacts. This avoids +# Docker layer diff issues when a rebuilt DEB retains its size and timestamp. RUN dpkg --remove --force-all libswsscommon RUN apt --fix-broken install -y -RUN dpkg --purge python3-swsscommon sonic-db-cli libsaimetadata libsairedis libsaivs syncd-vs swss sonic-eventd libdashapi framework +RUN dpkg --purge python3-swsscommon sonic-db-cli libsaimetadata libsairedis \ + libsaivs syncd-vs swss sonic-eventd libdashapi framework +RUN if [ "$include_nexthopgroup" = "y" ]; then \ + dpkg --purge libnexthopgroup || true; \ + fi RUN apt-get update -# vpp package configure requires to set: -# permission denied on key "vm.nr_hugepages" -# permission denied on key "vm.hugetlb_shm_group" -# permission denied on key "fs.protected_fifos" -# permission denied on key "fs.protected_hardlinks" -# permission denied on key "fs.protected_regular" -# permission denied on key "fs.protected_symlinks" - -# which can't be done during "docker build" command -# so let's put "true" command as sysctl, and after install let's bring it back - -RUN cp /usr/sbin/sysctl /usr/sbin/sysctl.org -RUN cp /usr/bin/true /usr/sbin/sysctl - -RUN apt install -y $(ls /debs/*.deb | grep vpp) - -RUN mv /usr/sbin/sysctl.org /usr/sbin/sysctl +# VPP maintainer scripts invoke sysctl operations that docker build cannot +# perform. Consumers that do not include VPP skip this block entirely. +RUN if [ "$include_vpp" = "y" ]; then \ + cp /usr/sbin/sysctl /usr/sbin/sysctl.org; \ + cp /usr/bin/true /usr/sbin/sysctl; \ + apt install -y $(ls /debs/*.deb | grep vpp); \ + mv /usr/sbin/sysctl.org /usr/sbin/sysctl; \ + fi RUN apt install -y /debs/libdashapi_1.0.0_amd64.deb \ /debs/libswsscommon_1.0.0_amd64.deb \ @@ -45,18 +42,31 @@ RUN apt install -y /debs/libdashapi_1.0.0_amd64.deb \ /debs/syncd-vs_1.0.0_amd64.deb \ /debs/swss_1.0.0_amd64.deb -RUN if [ "$need_dbg" = "y" ] ; then dpkg -i /debs/swss-dbg_1.0.0_amd64.deb ; fi +RUN if [ "$include_nexthopgroup" = "y" ]; then \ + apt install -y /debs/libnexthopgroup_1.0.0_amd64.deb; \ + fi + +RUN if [ "$need_dbg" = "y" ]; then \ + dpkg -i "/debs/${debug_pkg}_1.0.0_amd64.deb"; \ + fi COPY ["start.sh", "/usr/bin/"] RUN pip3 install scapy==2.5.0 -RUN apt-get -y install software-properties-common libdatetime-perl libcapture-tiny-perl build-essential libcpanel-json-xs-perl git python3-protobuf - -RUN git clone -b v2.0 --single-branch --depth 1 https://github.com/linux-test-project/lcov && cd lcov && make install - -RUN lcov --version - -RUN pip3 install lcov_cobertura - -RUN if [ -n "$BUILD_DIR" ]; then mkdir -p $BUILD_DIR && tar -xf /tmp/gcov/gcov-source.tar -C $BUILD_DIR; fi +RUN apt-get -y install software-properties-common libdatetime-perl \ + libcapture-tiny-perl build-essential libcpanel-json-xs-perl git \ + python3-protobuf + +RUN if [ "$include_gcov" = "y" ]; then \ + git clone -b v2.0 --single-branch --depth 1 https://github.com/linux-test-project/lcov; \ + cd lcov; \ + make install; \ + lcov --version; \ + pip3 install lcov_cobertura; \ + fi + +RUN if [ "$include_gcov" = "y" ] && [ -n "$BUILD_DIR" ]; then \ + mkdir -p "$BUILD_DIR"; \ + tar -xf /tmp/gcov/gcov-source.tar -C "$BUILD_DIR"; \ + fi diff --git a/.azure-pipelines/run-vs-tests-template.yml b/.azure-pipelines/run-vs-tests-template.yml new file mode 100644 index 00000000000..55080a10f46 --- /dev/null +++ b/.azure-pipelines/run-vs-tests-template.yml @@ -0,0 +1,277 @@ +parameters: +- name: repo + type: string + default: self + +- name: timeout + type: number + default: 480 + +- name: log_artifact_name + type: string + +- name: docker_sonic_vs_name + type: string + default: docker-sonic-vs + +- name: archive_gcov + type: boolean + default: false + +- name: asan + type: boolean + default: false + +- name: num_ports + type: number + default: 0 + +- name: run_tests_pattern + type: string + default: "" + +- name: pytest_params + type: string + default: "--force-flaky" + +- name: parallelism + type: number + default: 8 + +- name: retry + type: number + default: 3 + +- name: continue_on_error + type: boolean + default: false + +- name: extra_modes + # List of {name, pattern, pytest_params, parallelism}; empty for consumers + # without an additional VS mode. + type: object + default: [] + +- name: staged_upstreams + # Same-run host bundles, e.g. swss-common's Ubuntu artifact in its own Test. + type: object + default: [] + +- name: debian_version + type: string + default: bookworm + +jobs: +- job: + displayName: vstest + timeoutInMinutes: ${{ parameters.timeout }} + variables: + isAsan: ${{ parameters.asan }} + ${{ if parameters.archive_gcov }}: + DIFF_COVER_CHECK_THRESHOLD: 80 + DIFF_COVER_ENABLE: 'true' + DIFF_COVER_COVERAGE_FILES: Cobertura.xml + + pool: sonictest + + steps: + - script: | + set -ex + ip a show dev eth0 || true + ls -A1 | xargs -r -I{} sudo rm -rf {} + sudo apt-get purge -y libswsscommon python3-swsscommon || true + displayName: "Clean workspace" + + - checkout: ${{ parameters.repo }} + clean: true + + - ${{ if eq(parameters.repo, 'self') }}: + - template: merge-target-branch.yml + + - ${{ each up in parameters.staged_upstreams }}: + - task: DownloadPipelineArtifact@2 + inputs: + artifact: ${{ up.artifact }} + path: $(Pipeline.Workspace)/staged/${{ up.name }} + displayName: "Stage current-run ${{ up.name }} for test setup" + + - task: DownloadPipelineArtifact@2 + inputs: + artifact: ${{ parameters.docker_sonic_vs_name }} + path: $(Build.ArtifactStagingDirectory)/download + displayName: "Download current-run ${{ parameters.docker_sonic_vs_name }}" + + - script: | + set -ex + sudo apt-get update + sudo apt-get install -y python3-yaml python3-requests git jq + + clone_swss_common() { + ref="$1" + for delay in 0 5 15; do + [ "$delay" -eq 0 ] || sleep "$delay" + rm -rf /tmp/sw-common + git clone --depth 1 --branch "$ref" \ + https://github.com/sonic-net/sonic-swss-common /tmp/sw-common && return 0 + done + return 1 + } + + SETUP_BRANCH="$(BUILD_BRANCH)" + if ! clone_swss_common "$SETUP_BRANCH"; then + case "$(BUILD_BRANCH)" in + master|20[0-9][0-9][0-9][0-9]) + echo "Failed to clone required sonic-swss-common branch $(BUILD_BRANCH)" >&2 + exit 1 + ;; + *) + echo "No matching swss-common feature branch; falling back to master" + clone_swss_common master + SETUP_BRANCH=master + ;; + esac + fi + + REQ_ARGS=$(echo "$STAGED_UPSTREAMS_JSON" \ + | jq -r '.[] | .name' \ + | xargs -I{} echo --required-staged-upstream {} \ + | tr '\n' ' ') + PYTHONPATH=/tmp/sw-common/ci python3 -m buildenv_setup \ + --repo-dir $(Build.SourcesDirectory) \ + --scope test \ + --arch amd64 \ + --debian-version ${{ parameters.debian_version }} \ + --host-os ubuntu-22.04 \ + --branch "$SETUP_BRANCH" \ + --upstream-staged-dir $(Pipeline.Workspace)/staged \ + $REQ_ARGS + displayName: "Set up VS test host (buildenv_setup)" + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + STAGED_UPSTREAMS_JSON: ${{ convertToJson(parameters.staged_upstreams) }} + + - ${{ if eq(parameters.archive_gcov, true) }}: + - script: | + set -ex + curl -sSL https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add - + sudo apt-add-repository https://packages.microsoft.com/ubuntu/22.04/prod + sudo apt-get update + sudo apt-get install -y dotnet-sdk-8.0 + sudo dotnet tool install dotnet-reportgenerator-globaltool \ + --tool-path /usr/bin 2>&1 | tee log.log || grep 'already installed' log.log + rm -f log.log + displayName: "Install coverage report tooling" + + - script: | + set -ex + sudo .azure-pipelines/build_and_install_module.sh + displayName: "Build and install host kernel modules" + + - script: | + set -ex + sudo docker load -i $(Build.ArtifactStagingDirectory)/download/docker-sonic-vs.gz + docker ps + ip netns list + uname -a + sudo /sbin/ip link add Vrf1 type vrf table 1001 || { echo 'vrf command failed'; exit 1; } + sudo /sbin/ip link del Vrf1 type vrf table 1001 + pushd tests + + params='${{ parameters.pytest_params }}' + if [ '${{ parameters.archive_gcov }}' == True ]; then + cp $(Build.ArtifactStagingDirectory)/download/coverage.info ./ + cp $(Build.ArtifactStagingDirectory)/download/coverage.xml ./ + params="$params --enable-coverage --force-recreate-dvs" + fi + if [ '${{ parameters.asan }}' == True ]; then + params="$params --graceful-stop" + fi + if [ ${{ parameters.num_ports }} -gt 0 ]; then + params="$params --num-ports=${{ parameters.num_ports }}" + fi + + all_tests=$(ls test_*.py | xargs) + all_tests="$all_tests p4rt dash" + if [ -n '${{ parameters.run_tests_pattern }}' ]; then + all_tests=$(ls ${{ parameters.run_tests_pattern }} | xargs) + fi + + IMAGE_NAME=docker-sonic-vs:$(Build.DefinitionName).$(Build.BuildNumber).asan-${{ parameters.asan }} + echo "$all_tests" | xargs -n 1 | parallel -j${{ parameters.parallelism }} \ + sudo DEFAULT_CONTAINER_REGISTRY=publicmirror.azurecr.io/ ./run-tests.sh \ + \'"$IMAGE_NAME"\' \'"$params"\' "{}" ${{ parameters.retry }} + displayName: "Run VS tests" + continueOnError: ${{ parameters.continue_on_error }} + + - ${{ each mode in parameters.extra_modes }}: + - script: | + set -ex + pushd tests + IMAGE_NAME=docker-sonic-vs:$(Build.DefinitionName).$(Build.BuildNumber).asan-${{ parameters.asan }} + echo '${{ mode.pattern }}' | xargs -n 1 | parallel -j${{ mode.parallelism }} \ + sudo DEFAULT_CONTAINER_REGISTRY=publicmirror.azurecr.io/ ./run-tests.sh \ + \'"$IMAGE_NAME"\' \'"${{ mode.pytest_params }}"\' "{}" ${{ parameters.retry }} + displayName: "Run VS tests (${{ mode.name }})" + continueOnError: ${{ parameters.continue_on_error }} + + - script: | + rm -rf $(Build.ArtifactStagingDirectory)/download + echo "##vso[task.setvariable variable=TestsRun]Yes" + condition: succeededOrFailed() + displayName: "Record test status" + + - ${{ if eq(parameters.archive_gcov, true) }}: + - script: | + set -ex + reportgenerator -reporttypes:Cobertura -reports:tests/*coverage.xml -targetdir:. + mkdir -p $(Build.ArtifactStagingDirectory)/gcov + cp Cobertura.xml tests/*coverage.xml $(Build.ArtifactStagingDirectory)/gcov/ + cp tests/*coverage.info $(Build.ArtifactStagingDirectory)/gcov/ + displayName: "Generate coverage report" + + - task: PublishCodeCoverageResults@1 + inputs: + codeCoverageTool: Cobertura + summaryFileLocation: '$(System.DefaultWorkingDirectory)/Cobertura.xml' + displayName: "Publish test coverage" + + - task: PublishTestResults@2 + inputs: + testResultsFiles: '**/*_tr.xml' + testRunTitle: vstest + condition: and(eq(variables['TestsRun'], 'Yes'), ne(variables['isAsan'], 'true')) + + - script: | + if [ -d tests/log ]; then + cp -r tests/log $(Build.ArtifactStagingDirectory)/ + if [ '${{ parameters.asan }}' == True ]; then + cp -vr tests/log/*/log/asan $(Build.ArtifactStagingDirectory)/ || true + fi + fi + displayName: "Collect logs" + condition: always() + + - publish: $(Build.ArtifactStagingDirectory)/ + artifact: ${{ parameters.log_artifact_name }}@$(System.JobAttempt) + displayName: "Publish logs" + condition: always() + + - publish: $(Build.ArtifactStagingDirectory)/asan + artifact: asan-reports + displayName: "Publish ASAN reports" + condition: eq('${{ parameters.asan }}', true) + + - script: | + if [ -d $(Build.ArtifactStagingDirectory)/asan ] \ + && [ "$(ls -A $(Build.ArtifactStagingDirectory)/asan)" ]; then + echo "There are issues reported by ASAN" + exit 1 + fi + echo "No issues reported by ASAN" + displayName: "Check ASAN reports" + condition: eq('${{ parameters.asan }}', true) + continueOnError: true + + - script: | + sudo apt-get -o DPkg::Lock::Timeout=600 install -y python-is-python3 + displayName: "Install temporary Python 3 compatibility symlink" diff --git a/.azure-pipelines/test-docker-sonic-vs-template.yml b/.azure-pipelines/test-docker-sonic-vs-template.yml deleted file mode 100644 index 82f4cf09e97..00000000000 --- a/.azure-pipelines/test-docker-sonic-vs-template.yml +++ /dev/null @@ -1,261 +0,0 @@ -parameters: -- name: timeout - type: number - default: 480 - -- name: log_artifact_name - type: string - -- name: gcov_artifact_name - type: string - -- name: sonic_slave - type: string - -- name: archive_gcov - type: boolean - default: false - -- name: docker_sonic_vs_name - type: string - default: docker-sonic-vs - -- name: swss_common_branch - type: string - default: '$(BUILD_BRANCH)' - -- name: sonic_buildimage_ubuntu22_04 - type: string - default: '$(BUILD_BRANCH)' - -- name: asan - type: boolean - default: false - -- name: num_ports - type: number - default: 0 - -- name: run_tests_pattern - type: string - default: "" - -- name: debian_version - type: string - default: bookworm - -jobs: -- job: - displayName: vstest - timeoutInMinutes: ${{ parameters.timeout }} - variables: - isAsan: ${{ parameters.asan }} - ${{ if parameters.archive_gcov }}: - DIFF_COVER_CHECK_THRESHOLD: 80 - DIFF_COVER_ENABLE: 'true' - DIFF_COVER_COVERAGE_FILES: Cobertura.xml - - pool: sonictest - - steps: - - script: | - ip a show dev eth0 || true - ls -A1 | xargs -I{} sudo rm -rf {} - displayName: "Clean workspace" - - checkout: self - - template: merge-target-branch.yml - - task: DownloadPipelineArtifact@2 - inputs: - artifact: ${{ parameters.docker_sonic_vs_name }} - path: $(Build.ArtifactStagingDirectory)/download - displayName: "Download pre-stage built ${{ parameters.docker_sonic_vs_name }}" - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: build - pipeline: Azure.sonic-swss-common - artifact: sonic-swss-common.amd64.ubuntu22_04 - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/${{ parameters.swss_common_branch }}' - path: $(Build.ArtifactStagingDirectory)/download - allowPartiallySucceededBuilds: true - displayName: "Download sonic swss common deb packages" - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: build - pipeline: Azure.sonic-buildimage.common_libs - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/$(BUILD_BRANCH)' - path: $(Build.ArtifactStagingDirectory)/download - artifact: common-lib - patterns: | - target/debs/${{ parameters.debian_version }}/libyang3_*.deb - target/debs/${{ parameters.debian_version }}/libyang-dev_3*.deb - displayName: "Download libyang from common lib" - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: build - pipeline: sonic-net.sonic-buildimage-ubuntu22.04 - artifact: sonic-buildimage.amd64.ubuntu22_04 - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/${{ parameters.sonic_buildimage_ubuntu22_04 }}' - path: $(Build.ArtifactStagingDirectory)/download - displayName: "Download sonic buildimage ubuntu22.04 deb packages" - - - script: | - set -ex - # Install .NET CORE - curl -sSL https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add - - sudo apt-add-repository https://packages.microsoft.com/ubuntu/22.04/prod - sudo apt-get update - sudo apt-get install -y dotnet-sdk-8.0 - sudo dotnet tool install dotnet-reportgenerator-globaltool --tool-path /usr/bin 2>&1 | tee log.log || grep 'already installed' log.log - rm log.log - displayName: "Install .NET CORE" - - - script: | - set -ex - # install packages for vs test - sudo uv pip install --system pytest flaky exabgp docker lcov_cobertura - - # install other dependencies - sudo apt-get -o DPkg::Lock::Timeout=600 install -y net-tools \ - bridge-utils \ - vlan \ - libzmq3-dev \ - libzmq5 \ - libhiredis0.14 \ - libpcre3-dev \ - python3-redis \ - parallel - - sudo .azure-pipelines/build_and_install_module.sh - - # Install libyang packages from downloaded artifacts - sudo apt-get install -y \ - $(Build.ArtifactStagingDirectory)/download/target/debs/${{ parameters.debian_version }}/libyang3_*.deb \ - $(Build.ArtifactStagingDirectory)/download/target/debs/${{ parameters.debian_version }}/libyang-dev_3*.deb - - # python3-libyang's bookworm .deb pins python3 (>= 3.11~, << 3.12) - # and the sonictest pool host runs Ubuntu 22.04 (python 3.10), so the - # .deb won't install regardless of resolver. Build the Python bindings - # from PyPI instead — same fallback as the inline amd64/ubuntu-22.04 - # job. --no-build-isolation + apt's python3-cffi sidesteps a cffi - # version-mismatch Exception that jammy's pip 22.0.2 build-isolation - # env otherwise triggers. --no-build-isolation needs wheel present - # in the environment to provide bdist_wheel, so install it first. - sudo apt-get install -y python3-cffi python3-dev - sudo uv pip install --system wheel - sudo uv pip install --system --no-build-isolation 'libyang==3.3.0' - - sudo dpkg -i $(Build.ArtifactStagingDirectory)/download/libprotobuf*_amd64.deb $(Build.ArtifactStagingDirectory)/download/libprotobuf-lite*_amd64.deb $(Build.ArtifactStagingDirectory)/download/python3-protobuf*_amd64.deb - sudo dpkg -i $(Build.ArtifactStagingDirectory)/download/libdashapi*.deb - sudo dpkg -i --force-confask,confnew $(Build.ArtifactStagingDirectory)/download/libswsscommon_1.0.0_amd64.deb || sudo apt-get install -y -f - sudo dpkg -i $(Build.ArtifactStagingDirectory)/download/python3-swsscommon_1.0.0_amd64.deb - displayName: "Install dependencies" - - - script: | - set -ex - sudo docker load -i $(Build.ArtifactStagingDirectory)/download/docker-sonic-vs.gz - docker ps - ip netns list - uname -a - sudo /sbin/ip link add Vrf1 type vrf table 1001 || { echo 'vrf command failed' ; exit 1; } - sudo /sbin/ip link del Vrf1 type vrf table 1001 - pushd tests - - params="--force-flaky" - if [ '${{ parameters.archive_gcov }}' == True ]; then - cp $(Build.ArtifactStagingDirectory)/download/coverage.info ./ - cp $(Build.ArtifactStagingDirectory)/download/coverage.xml ./ - params=" ${params} --enable-coverage --force-recreate-dvs " - fi - if [ '${{ parameters.asan }}' == True ]; then - params=" ${params} --graceful-stop " - fi - if [ ${{ parameters.num_ports }} -gt 0 ]; then - params=" ${params} --num-ports=${{ parameters.num_ports }} " - fi - - all_tests=$(ls test_*.py | xargs) - all_tests="${all_tests} p4rt dash" - - if [ -n '${{ parameters.run_tests_pattern }}' ]; then - all_tests=" $(ls ${{ parameters.run_tests_pattern }} | xargs) " - fi - # Run the tests in parallel and retry - retry=3 - parallel=8 - IMAGE_NAME=docker-sonic-vs:$(Build.DefinitionName).$(Build.BuildNumber).asan-${{ parameters.asan }} - echo $all_tests | xargs -n 1 | parallel -j${parallel} sudo DEFAULT_CONTAINER_REGISTRY=publicmirror.azurecr.io/ ./run-tests.sh \'"$IMAGE_NAME"\' \'"$params"\' "{}" $retry - single_asic_voq_tests="test_portchannel.py test_neighbor.py test_route.py" - parallel=3 - echo $single_asic_voq_tests | xargs -n 1 | parallel -j${parallel} sudo ./run-tests.sh \'"$IMAGE_NAME"\' \'"--force-flaky --force-recreate-dvs --switch-mode=single_asic_voq_fs"\' "{}" $retry - - rm -rf $(Build.ArtifactStagingDirectory)/download - displayName: "Run vs tests" - continueOnError: ${{ parameters.asan }} - - - script: | - echo "##vso[task.setvariable variable=TestsRun]Yes" - condition: succeededOrFailed() - displayName: 'Record Test Status' - - - script: | - set -ex - reportgenerator -reporttypes:Cobertura -reports:tests/*coverage.xml -targetdir:. - mkdir $(Build.ArtifactStagingDirectory)/gcov - cp Cobertura.xml tests/*coverage.xml $(Build.ArtifactStagingDirectory)/gcov/ - cp tests/*coverage.info $(Build.ArtifactStagingDirectory)/gcov/ - condition: ${{ parameters.archive_gcov }} - displayName: "Generate coverage.xml" - - - task: PublishCodeCoverageResults@1 - condition: ${{ parameters.archive_gcov }} - inputs: - codeCoverageTool: Cobertura - summaryFileLocation: '$(System.DefaultWorkingDirectory)/Cobertura.xml' - displayName: 'Publish test coverage' - - - task: PublishTestResults@2 - inputs: - testResultsFiles: '**/*_tr.xml' - testRunTitle: vstest - condition: and(eq(variables['TestsRun'], 'Yes'), ne(variables['isAsan'], 'true')) - - - script: | - cp -r tests/log $(Build.ArtifactStagingDirectory)/ - - if [ '${{ parameters.asan }}' == True ]; then - cp -vr tests/log/*/log/asan $(Build.ArtifactStagingDirectory)/ - fi - displayName: "Collect logs" - condition: always() - - - publish: $(Build.ArtifactStagingDirectory)/ - artifact: ${{ parameters.log_artifact_name }}@$(System.JobAttempt) - displayName: "Publish logs" - condition: always() - - - publish: $(Build.ArtifactStagingDirectory)/asan - artifact: asan-reports - displayName: "Publish ASAN reports" - condition: eq('${{ parameters.asan }}', true) - - - script: | - if [ "$(ls -A $(Build.ArtifactStagingDirectory)/asan)" ]; then - echo "There are issues reported by ASAN" - exit 1 - else - echo "No issues reported by ASAN" - fi - displayName: "Check ASAN reports" - condition: eq('${{ parameters.asan }}', true) - continueOnError: true - - - script: | - sudo apt-get -o DPkg::Lock::Timeout=600 install -y python-is-python3 - - displayName: "Install temporary workaround to add a symlink to python 3" diff --git a/.gitignore b/.gitignore index b26d6a52d8d..5d075870f79 100644 --- a/.gitignore +++ b/.gitignore @@ -107,3 +107,4 @@ orchagent/p4orch/tests/*_tr.xml build-env/.env build-env/custom-setup.sh +.azure-pipelines/docker-sonic-vs/debs/ diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6275c62545a..1e562dacb90 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -47,14 +47,8 @@ stages: parameters: arch: amd64 pool: sonicso1ES-amd64 - sonic_slave: sonic-slave-${{ parameters.debian_version }} - common_lib_artifact_name: common-lib - swss_common_artifact_name: sonic-swss-common-${{ parameters.debian_version }} - sairedis_artifact_name: sonic-sairedis-${{ parameters.debian_version }} - dash_artifact_name: sonic-dash-api artifact_name: sonic-swss-${{ parameters.debian_version }} debian_version: ${{ parameters.debian_version }} - vpp_artifact_name: vpp-${{ parameters.debian_version }} archive_pytests: true archive_gcov: true @@ -65,14 +59,8 @@ stages: parameters: arch: amd64 pool: sonicso1ES-amd64 - sonic_slave: sonic-slave-${{ parameters.debian_version }} - common_lib_artifact_name: common-lib - swss_common_artifact_name: sonic-swss-common-${{ parameters.debian_version }} - sairedis_artifact_name: sonic-sairedis-${{ parameters.debian_version }} - dash_artifact_name: sonic-dash-api artifact_name: sonic-swss-asan-${{ parameters.debian_version }} debian_version: ${{ parameters.debian_version }} - vpp_artifact_name: vpp-${{ parameters.debian_version }} asan: true - stage: BuildArm @@ -84,14 +72,8 @@ stages: arch: armhf timeout: 240 pool: sonicso1ES-armhf - sonic_slave: sonic-slave-${{ parameters.debian_version }} - common_lib_artifact_name: common-lib.armhf - swss_common_artifact_name: sonic-swss-common-${{ parameters.debian_version }}.armhf - sairedis_artifact_name: sonic-sairedis-${{ parameters.debian_version }}.armhf - dash_artifact_name: sonic-dash-api.armhf artifact_name: sonic-swss-${{ parameters.debian_version }}.armhf debian_version: ${{ parameters.debian_version }} - vpp_artifact_name: vpp-${{ parameters.debian_version }} archive_gcov: false - template: .azure-pipelines/build-template.yml @@ -99,14 +81,8 @@ stages: arch: arm64 timeout: 240 pool: sonicso1ES-arm64 - sonic_slave: sonic-slave-${{ parameters.debian_version }} - common_lib_artifact_name: common-lib.arm64 - swss_common_artifact_name: sonic-swss-common-${{ parameters.debian_version }}.arm64 - sairedis_artifact_name: sonic-sairedis-${{ parameters.debian_version }}.arm64 - dash_artifact_name: sonic-dash-api.arm64 artifact_name: sonic-swss-${{ parameters.debian_version }}.arm64 debian_version: ${{ parameters.debian_version }} - vpp_artifact_name: vpp-${{ parameters.debian_version }} archive_gcov: false - stage: BuildTrixie @@ -117,14 +93,8 @@ stages: parameters: arch: amd64 pool: sonicso1ES-amd64 - sonic_slave: sonic-slave-trixie - common_lib_artifact_name: common-lib - swss_common_artifact_name: sonic-swss-common-trixie - sairedis_artifact_name: sonic-sairedis-trixie - dash_artifact_name: sonic-dash-api-trixie artifact_name: sonic-swss-trixie debian_version: trixie - vpp_artifact_name: vpp-trixie archive_gcov: false - template: .azure-pipelines/build-template.yml @@ -132,14 +102,8 @@ stages: arch: armhf timeout: 240 pool: sonicso1ES-armhf - sonic_slave: sonic-slave-trixie - common_lib_artifact_name: common-lib.armhf - swss_common_artifact_name: sonic-swss-common-trixie.armhf - sairedis_artifact_name: sonic-sairedis-trixie.armhf - dash_artifact_name: sonic-dash-api-trixie.armhf artifact_name: sonic-swss-trixie.armhf debian_version: trixie - vpp_artifact_name: vpp-trixie archive_gcov: false - template: .azure-pipelines/build-template.yml @@ -147,21 +111,15 @@ stages: arch: arm64 timeout: 240 pool: sonicso1ES-arm64 - sonic_slave: sonic-slave-trixie - common_lib_artifact_name: common-lib.arm64 - swss_common_artifact_name: sonic-swss-common-trixie.arm64 - sairedis_artifact_name: sonic-sairedis-trixie.arm64 - dash_artifact_name: sonic-dash-api-trixie.arm64 artifact_name: sonic-swss-trixie.arm64 debian_version: trixie - vpp_artifact_name: vpp-trixie archive_gcov: false - stage: BuildDocker dependsOn: Build condition: succeeded('Build') jobs: - - template: .azure-pipelines/build-docker-sonic-vs-template.yml + - template: .azure-pipelines/build-docker-template.yml parameters: swss_common_artifact_name: sonic-swss-common-${{ parameters.debian_version }} sairedis_artifact_name: sonic-sairedis-${{ parameters.debian_version }} @@ -169,12 +127,15 @@ stages: debian_version: ${{ parameters.debian_version }} vpp_artifact_name: vpp-${{ parameters.debian_version }} artifact_name: docker-sonic-vs + debug_pkg: swss-dbg + include_gcov: true + include_vpp: true - stage: BuildDockerAsan dependsOn: BuildAsan condition: succeeded('BuildAsan') jobs: - - template: .azure-pipelines/build-docker-sonic-vs-template.yml + - template: .azure-pipelines/build-docker-template.yml parameters: swss_common_artifact_name: sonic-swss-common-${{ parameters.debian_version }} sairedis_artifact_name: sonic-sairedis-${{ parameters.debian_version }} @@ -183,31 +144,41 @@ stages: debian_version: ${{ parameters.debian_version }} vpp_artifact_name: vpp-${{ parameters.debian_version }} asan: true + debug_pkg: swss-dbg + include_gcov: true + include_vpp: true - stage: Test dependsOn: BuildDocker condition: succeeded('BuildDocker') jobs: - - template: .azure-pipelines/test-docker-sonic-vs-template.yml + - template: .azure-pipelines/run-vs-tests-template.yml parameters: log_artifact_name: log - gcov_artifact_name: sonic-gcov - sonic_slave: sonic-slave-${{ parameters.debian_version }} archive_gcov: true debian_version: ${{ parameters.debian_version }} + extra_modes: + - name: single_asic_voq + pattern: 'test_portchannel.py test_neighbor.py test_route.py' + pytest_params: '--force-flaky --force-recreate-dvs --switch-mode=single_asic_voq_fs' + parallelism: 3 - stage: TestAsan dependsOn: BuildDockerAsan condition: succeeded('BuildDockerAsan') jobs: - - template: .azure-pipelines/test-docker-sonic-vs-template.yml + - template: .azure-pipelines/run-vs-tests-template.yml parameters: log_artifact_name: log-asan - gcov_artifact_name: sonic-gcov - sonic_slave: sonic-slave-${{ parameters.debian_version }} docker_sonic_vs_name: docker-sonic-vs-asan asan: true + continue_on_error: true debian_version: ${{ parameters.debian_version }} + extra_modes: + - name: single_asic_voq + pattern: 'test_portchannel.py test_neighbor.py test_route.py' + pytest_params: '--force-flaky --force-recreate-dvs --switch-mode=single_asic_voq_fs' + parallelism: 3 - stage: Gcov condition: false diff --git a/build-env/Dockerfile b/build-env/Dockerfile index 5b891c511bb..4d4ca802213 100644 --- a/build-env/Dockerfile +++ b/build-env/Dockerfile @@ -1,22 +1,53 @@ -ARG DEBIAN="bookworm" -ARG BRANCH="master" +# Local development image for sonic-swss. +# +# CI uses the same sonic-slave base image and buildenv_setup configuration, but +# does not use this Dockerfile. Source stays mounted from the host so ordinary +# edits do not invalidate the dependency layer. + +ARG DEBIAN=bookworm +ARG BRANCH=master FROM sonicdev-microsoft.azurecr.io:443/sonic-slave-${DEBIAN}:${BRANCH} + ARG USER ARG UID ARG GID ARG HOME ARG DEBIAN ARG BRANCH +ARG SWSS_COMMON_REF=master +RUN groupadd --gid "${GID}" "${USER}" \ + && useradd --gid "${GID}" --uid "${UID}" --create-home --home-dir "${HOME}" --shell /bin/bash "${USER}" \ + && usermod --append --groups sudo "${USER}" +RUN echo "${USER} ALL=(ALL) NOPASSWD:ALL" > "/etc/sudoers.d/${USER}" \ + && chmod 0440 "/etc/sudoers.d/${USER}" \ + && sed --in-place -E 's/^#?PermitRootLogin.*$/PermitRootLogin prohibit-password/g' /etc/ssh/sshd_config +RUN echo "* soft nofile 2048" >> /etc/security/limits.conf \ + && mkdir --parents /__w \ + && chown --recursive "${USER}:${USER}" /__w -COPY container-setup.py custom-setup.sh /tmp/ - -RUN groupadd --gid ${GID} ${USER}; useradd --gid ${GID} --uid ${UID} --create-home --home-dir ${HOME} --shell /bin/bash ${USER}; usermod --append --groups sudo ${USER} -RUN echo "${USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${USER}; chmod 0440 /etc/sudoers.d/${USER}; sed --in-place -E 's/^#?PermitRootLogin.*$/PermitRootLogin prohibit-password/g' /etc/ssh/sshd_config -RUN echo "* soft nofile 2048" >> /etc/security/limits.conf -RUN mkdir --parents /__w; chown --recursive ${USER}:${USER} /__w +# Obtain the shared tool, then copy only dependency declarations so the heavy +# setup layer is cached independently of source edits. +RUN git clone --depth 1 --branch "${SWSS_COMMON_REF}" \ + https://github.com/sonic-net/sonic-swss-common /tmp/sw-common +COPY packages/ /workspace/build-env/packages/ +COPY upstream-artifacts.yaml /workspace/build-env/upstream-artifacts.yaml +COPY custom-setup.sh /tmp/custom-setup.sh -RUN apt-get update; python3 /tmp/container-setup.py --branch ${BRANCH} --debian-version ${DEBIAN}; bash /tmp/custom-setup.sh +# Inline scripts inherited through the cascade may invoke sudo even though this +# layer runs as root, so keep sudo available while using --no-sudo for the +# installer's own apt/pip/dpkg commands. +RUN apt-get update \ + && apt-get install -y python3-yaml python3-requests sudo \ + && PYTHONPATH=/tmp/sw-common/ci python3 -m buildenv_setup \ + --repo-dir /workspace \ + --scope build \ + --arch amd64 \ + --debian-version "${DEBIAN}" \ + --branch "${BRANCH}" \ + --org-url https://dev.azure.com/mssonic \ + --no-sudo \ + && bash /tmp/custom-setup.sh USER ${USER} CMD ["sudo", "service", "ssh", "start", "-D"] diff --git a/build-env/README.md b/build-env/README.md index 7891ea4bb94..efeb79754f9 100644 --- a/build-env/README.md +++ b/build-env/README.md @@ -1,60 +1,90 @@ # SWSS Docker-Based Build Environment -This directory contains scripts to create a docker image which serves as a build environment for SWSS. The image is designed to closely mimic the build environment used for SWSS in Azure Pipelines. The primary motivation behind this container is to provide an easy way to run the C++ unit tests (`tests/mock_tests`) locally. - -## Getting Started - -Note: Your home directory on the host machine will be mounted to the home directory inside the container. This means that any settings in your home directory (e.g. authorized SSH keys or `.bashrc` settings) will also apply in the container and files inside your host home directory will be accessible inside the container. - -From this directory (`sonic-swss/build-env`): -1. Before building the image for the first time, run the `env_init.sh` script. This script also creates the `custom-setup.sh` script (see **Custom Setup** below). -2. Build the image and start the container with `docker compose up -d swss-bookworm`. -3. Enter the container either with `docker exec -it -u ${USER} swss-bookworm-master bash` or via SSH `ssh ${USER}@172.19.0.10`. -4. Once inside the container, navigate to the repository root directory `sonic-swss`. -5. Run `./autogen.sh` and then `./configure` to generate Makefiles for the repository. -6. Navigate to the unit test directory `sonic-swss/tests/mock_tests` -7. From here, you can build and run all unit tests using `make check`. You can also build a run a specific test binary, e.g. `make tests` and then `./tests`. - ``` - lawlee@e77fd85f06b6:~/repos/sonic-swss/tests/mock_tests$ make check - make check-TESTS - make[1]: Entering directory '/home/lawlee/repos/sonic-swss/tests/mock_tests' - make[2]: Entering directory '/home/lawlee/repos/sonic-swss/tests/mock_tests' - PASS: tests - PASS: tests_intfmgrd - PASS: tests_teammgrd - PASS: tests_portsyncd - PASS: tests_fpmsyncd - PASS: tests_response_publisher - ============================================================================ - Testsuite summary for sonic-swss 1.0 - ============================================================================ - # TOTAL: 6 - # PASS: 6 - # SKIP: 0 - # XFAIL: 0 - # FAIL: 0 - # XPASS: 0 - # ERROR: 0 - ============================================================================ - make[2]: Leaving directory '/home/lawlee/repos/sonic-swss/tests/mock_tests' - make[1]: Leaving directory '/home/lawlee/repos/sonic-swss/tests/mock_tests' - lawlee@e77fd85f06b6:~/repos/sonic-swss/tests/mock_tests$ - ``` -8. To shut down and remove the container, run `docker compose down swss-bookworm` on the host. -9. To rebuild the container image (e.g. in case of dependency changes), run `docker compose build --pull swss-bookworm` - -## Custom Setup -The container image can be customized to suit personal development preferences, e.g. installing a different shell. To do this, modify the `custom-setup.sh` script generated by `env_init.sh`. By default, `custom-setup.sh` is empty and has no effect. Put any desired shell commands in this script, which will run *after* all SWSS dependencies are installed. - -Note: `custom-setup.sh` is `.gitignore`d and should not be committed to the repository. - -## Debugging Failing Tests - -If tests are failing unexpectedly, build artifacts may be out-of-date but not being rebuilt due to switching branches. From `tests/mock_tests`, run `make clean` and then rebuild and re-run the tests. - -To run individual test classes/cases, use the `--gtest_filter` flag with the test binary. E.g. to run only tests in the `DashOrchTest` class from the main set of orchagent unit tests: -1. `cd tests/mock_tests` and `make tests` to build the test binary. -2. `./tests --gtest_filter="DashOrchTest*"` to run this test class only. `./tests --gtest_filter="DashOrchTest.SetEniMode"` to run this test case only. -3. `./tests --gtest_list_tests` will print all available test cases. - -The tests are built with debug symbols. A simple way to run a test in GDB: `gdb --args ./tests --gtest_filter="DashOrchTest*"`. +This directory is the single source of truth for sonic-swss build and VS-test +environment setup. CI and local development both consume the declarative YAML +through the shared +[`buildenv_setup`](https://github.com/sonic-net/sonic-swss-common/tree/master/ci) +tool hosted in sonic-swss-common. + +## Contents + +| Path | Purpose | Cascades downstream? | +|------|---------|----------------------| +| `packages/base.yaml` | Packages needed to build/link sonic-swss | **Yes** | +| `packages/tooling.yaml` | Build-only tools | No | +| `packages/test.yaml` | Shared VS test-host dependencies | N/A | +| `upstream-artifacts.yaml` | Build and test upstream bundles | **Yes** | +| `build.sh` | Canonical package build + Rust tests | - | +| `Dockerfile`, `compose.yaml` | Local development image/workflow | - | + +For Build scope, sonic-swss declares only immediate artifact dependencies: +sonic-sairedis and sonic-dash-api. The sairedis artifact carries its own +`build-env/`, so buildenv_setup recursively inherits sonic-swss-common, +common-libs, VPP, packages, and the canonical Redis test configuration. + +For Test scope, `packages/test.yaml` and the test-scoped artifact entries set up +the Ubuntu VS-test host. The swss-owned `run-vs-tests-template.yml` consumes the +same configuration for all three dataplane repositories. + +## CI + +The Build template clones sonic-swss-common to obtain the shared tool, then runs: + +```bash +PYTHONPATH=/tmp/sw-common/ci python3 -m buildenv_setup \ + --repo-dir "$(Build.SourcesDirectory)" \ + --scope build \ + --arch \ + --debian-version \ + --branch "$(BUILD_BRANCH)" + +GCOV= ASAN= ./build-env/build.sh +``` + +The published artifact retains the existing top-level DEBs and coverage files +and adds `build-env/` alongside them for future downstream cascade consumers. + +## Local development + +The existing non-root, home-mounted workflow is preserved: + +1. From `build-env/`, run `./env_init.sh`. This records your UID/GID and creates + ignored `custom-setup.sh` if needed. +2. Build and start the container: + + ```bash + docker compose up -d swss-bookworm + ``` + +3. Enter it with either: + + ```bash + docker exec -it -u "${USER}" swss-bookworm-master bash + # or + ssh "${USER}"@172.19.0.10 + ``` + +4. Build the repository from its mounted host path: + + ```bash + ./build-env/build.sh + ``` + + For alternate modes: + + ```bash + GCOV=true ./build-env/build.sh + ASAN=true ./build-env/build.sh + ``` + +5. Stop the environment with `docker compose down`. + +`SWSS_COMMON_REF` selects the sonic-swss-common branch containing +buildenv_setup; `BRANCH` selects the sonic-slave image and upstream artifact +branch. Both default to `master`. + +## Custom setup + +`env_init.sh` creates ignored `custom-setup.sh`. Commands placed there run as +root after the canonical dependency setup, allowing personal shell/tooling +customization without changing the shared environment definition. diff --git a/build-env/build.sh b/build-env/build.sh new file mode 100755 index 00000000000..e797b3c611c --- /dev/null +++ b/build-env/build.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# +# Canonical sonic-swss package build, used by CI and local development. +set -ex + +cd "$(dirname "$0")/.." + +case "${GCOV:-${ENABLE_GCOV:-false}}" in + [Tt]rue|1|yes|y) + export ENABLE_GCOV=y + echo "BUILD_DIR=$(pwd)" > build.info + ;; +esac + +case "${ASAN:-${ENABLE_ASAN:-false}}" in + [Tt]rue|1|yes|y) export ENABLE_ASAN=y ;; +esac + +rm -f ../swss_*.deb ../swss-dbg_*.deb +./autogen.sh +RUSTFLAGS=-Dwarnings dpkg-buildpackage -us -uc -b -j"$(nproc)" +cp ../*.deb . + +RUSTFLAGS=-Dwarnings cargo test diff --git a/build-env/compose.yaml b/build-env/compose.yaml index 72f5485892c..e8d5353b44e 100644 --- a/build-env/compose.yaml +++ b/build-env/compose.yaml @@ -1,7 +1,7 @@ services: swss-bookworm: - container_name: swss-bookworm-master - image: "swss-bookworm-master:latest" + container_name: swss-bookworm-${BRANCH:-master} + image: "swss-bookworm-${BRANCH:-master}:latest" tty: true volumes: - ${HOME}:${HOME} @@ -12,9 +12,10 @@ services: UID: ${UID:?Did you run the env_init.sh script?} GID: ${GID:?Did you run the env_init.sh script?} DEBIAN: bookworm - BRANCH: master + BRANCH: ${BRANCH:-master} + SWSS_COMMON_REF: ${SWSS_COMMON_REF:-master} tags: - - "swss-bookworm-master:latest" + - "swss-bookworm-${BRANCH:-master}:latest" networks: docker_net: ipv4_address: 172.19.0.10 diff --git a/build-env/container-setup.py b/build-env/container-setup.py deleted file mode 100644 index dff65669f84..00000000000 --- a/build-env/container-setup.py +++ /dev/null @@ -1,144 +0,0 @@ -import argparse -import glob -import requests -import json -import os -import subprocess -from pathlib import Path - - -SAIREDIS = 'sonic-sairedis' -COMMON = 'sonic-common-libs' -SWSSCOMMON = 'sonic-swsscommon' -BUILDIMAGE = 'sonic-buildimage' -VPP = 'sonic-platform-vpp' -DASH_API = 'sonic-dash-api' - -# These values are obtained from the pipeline URLs, e.g. the pipeline for swsscommon is https://dev.azure.com/mssonic/build/_build?definitionId=9 -pipeline_id_map = { - SAIREDIS: 12, - COMMON: 465, - SWSSCOMMON: 9, - BUILDIMAGE: 142, - VPP: 1016, - DASH_API: 1318 -} - -pipeline_artifact_map = { - SAIREDIS: 'sonic-sairedis-{}', - COMMON: 'common-lib', - SWSSCOMMON: 'sonic-swss-common-{}', - BUILDIMAGE: 'sonic-buildimage.vs', - VPP: 'VPP', - DASH_API: 'sonic-dash-api' -} - -deb_files_regex = ['libswsscommon*.deb', 'libnl*.deb', 'libsai*.deb', 'syncd-vs*.deb', 'libyang3_*.deb', 'libyang-dev_3*.deb', 'python3-swsscommon*.deb', '*vpp*.deb', 'libdash*.deb'] - -pipeline_out_file_map = { - SAIREDIS: 'sairedis.zip', - COMMON: 'common-lib.zip', - SWSSCOMMON: 'swsscommon.zip', - VPP: 'vpp.zip', - DASH_API: 'dash-api.zip' -} - -force_main_branch = [VPP] - -build_url = 'https://dev.azure.com/mssonic/build/_apis/build/builds?definitions={}&branchName=refs/heads/{}&resultFilter=succeeded,partiallySucceeded&statusFilter=completed&maxBuildsPerDefinition=1&queryOrder=finishTimeDescending' -artifact_url = 'https://dev.azure.com/mssonic/build/_apis/build/builds/{}/artifacts?artifactName={}&api-version=5.1' - - -def get_latest_build(pipeline, branch): - if pipeline in force_main_branch: - target_branch = "main" - else: - target_branch = branch - - url = build_url.format(pipeline_id_map[pipeline], target_branch) - print(url) - res = requests.get(url) - if res.status_code != 200: - raise Exception(f"Failed to fetch build info for {pipeline} on branch {target_branch}: HTTP {res.status_code}") - build_info = json.loads(res.content) - if not build_info.get('value') and target_branch == "master": - url = build_url.format(pipeline_id_map[pipeline], "main") - print(url) - res = requests.get(url) - if res.status_code != 200: - raise Exception(f"Failed to fetch build info for {pipeline} on branch main: HTTP {res.status_code}") - build_info = json.loads(res.content) - if not build_info.get('value'): - raise Exception(f"No successful builds found for {pipeline} on branch {target_branch}") - return build_info['value'][0]['id'] - - -def get_artifact_url(pipeline, build_id, debian_version): - if pipeline in [SAIREDIS, SWSSCOMMON]: - artifact_name = pipeline_artifact_map[pipeline].format(debian_version) - else: - artifact_name = pipeline_artifact_map[pipeline] - url = artifact_url.format(build_id, artifact_name) - print(url) - res = requests.get(url) - if res.status_code != 200: - raise Exception( - f"Failed to fetch artifact info for {pipeline} (build {build_id}, artifact {artifact_name}): HTTP {res.status_code}" - ) - artifact_info = json.loads(res.content) - return artifact_info['resource']['downloadUrl'] - - -def download_artifact(pipeline, filename, branch, debian_version): - build_id = get_latest_build(pipeline, branch) - download_url = get_artifact_url(pipeline, build_id, debian_version) - print("URL: {}".format(download_url)) - - with open(filename, 'wb') as out_file: - res = requests.get(download_url, stream=True, timeout=60) - res.raise_for_status() - content = res.content - out_file.write(content) - - -def get_all_artifacts(dest_dir, branch, debian_version): - for pipeline, filename in pipeline_out_file_map.items(): - print("Getting artifact {}".format(pipeline)) - dest_file = os.path.join(dest_dir, filename) - download_artifact(pipeline, dest_file, branch, debian_version) - print("Finished getting artifact {}".format(pipeline)) - - -def main(branch, debian_version): - try: - work_dir = Path("/tmp/sonic/") - work_dir.mkdir(parents=True, exist_ok=True) - - get_all_artifacts(str(work_dir), branch, debian_version) - - for filename in pipeline_out_file_map.values(): - print("Extracting {}".format(filename)) - if "common-lib" in filename: - cmd = ['bash', '-c', f"unzip -l {filename} | grep -oE 'common-lib/target/debs/{debian_version}.*deb$' | xargs unzip -o -j {filename}"] - else: - cmd = ['unzip', '-o', '-j', filename] - - subprocess.run(cmd, cwd=work_dir, stdout=subprocess.DEVNULL) - - debs_to_install = [] - for pattern in deb_files_regex: - debs_to_install += [os.path.join(".", x) for x in glob.glob(pattern, root_dir=work_dir)] - - cmd = ["sudo", "env", "VPP_INSTALL_SKIP_SYSCTL=1", "/usr/bin/apt-get", "install", "-y"] + debs_to_install - subprocess.run(cmd, cwd=work_dir, stdout=subprocess.DEVNULL) - except Exception: - raise - - -if __name__ == '__main__': - parser = argparse.ArgumentParser('SWSS Build Setup') - parser.add_argument('-b', '--branch', default="master") - parser.add_argument('-d', '--debian-version', default="bookworm") - - args = parser.parse_args() - main(args.branch, args.debian_version) diff --git a/build-env/packages/base.yaml b/build-env/packages/base.yaml new file mode 100644 index 00000000000..205da24bbfb --- /dev/null +++ b/build-env/packages/base.yaml @@ -0,0 +1,12 @@ +# sonic-swss build and link dependencies. +# +# base.yaml cascades to any future downstream consumers of sonic-swss. Packages +# already present in the sairedis -> swss-common cascade are repeated when SWSS +# directly depends on them; buildenv_setup deduplicates apt installs. + +packages: + - libhiredis-dev + - libzmq3-dev + - swig + - libdbus-1-dev + - libteam-dev diff --git a/build-env/packages/test.yaml b/build-env/packages/test.yaml new file mode 100644 index 00000000000..41008bb4fe2 --- /dev/null +++ b/build-env/packages/test.yaml @@ -0,0 +1,34 @@ +# VS test-host dependencies. sonic-swss owns the shared VS test stack, so this +# is the single package list used by its run-vs-tests template. + +packages: + # Python test runner and helpers. + - { name: pytest, type: pip } + - { name: flaky, type: pip } + - { name: exabgp, type: pip } + - { name: docker, type: pip } + - { name: lcov_cobertura, type: pip } + - { name: wheel, type: pip } + - name: libyang==3.3.0 + type: pip + pip_args: [--no-build-isolation] + requires: [libyang3, python3-cffi, python3-dev, wheel] + + # Common VS/DVS and KVM host packages (union of the three existing templates). + - python3-pip + - libvirt-clients + - qemu + - openvswitch-switch + - net-tools + - bridge-utils + - vlan + - python3-libvirt + - libzmq3-dev + - libzmq5 + - libhiredis0.14 + - libpcre3-dev + - python3-redis + - python3-cffi + - python3-dev + - uuid-dev + - parallel diff --git a/build-env/packages/tooling.yaml b/build-env/packages/tooling.yaml new file mode 100644 index 00000000000..6635e673750 --- /dev/null +++ b/build-env/packages/tooling.yaml @@ -0,0 +1,4 @@ +# Build-only tooling for sonic-swss. This file does not cascade downstream. + +packages: + - { name: lcov_cobertura, type: pip } diff --git a/build-env/upstream-artifacts.yaml b/build-env/upstream-artifacts.yaml new file mode 100644 index 00000000000..7ea14d4e740 --- /dev/null +++ b/build-env/upstream-artifacts.yaml @@ -0,0 +1,78 @@ +# Upstream artifacts used by sonic-swss Build and by the shared VS test host. +# +# Build scope declares only immediate dependencies: sairedis (which cascades to +# swss-common/common-libs/VPP) and dash-api. Test scope uses Ubuntu-host-specific +# artifacts because VS tests execute outside the Debian build container. + +upstream: + # ------------------------------ Build ---------------------------------- # + - name: sonic-sairedis + project: build + pipeline: Azure.sonic-sairedis + scopes: [build] + cascade_optional: false + result_filter: [succeeded, partiallySucceeded] + artifact_name: + - { when: { arch: amd64 }, value: 'sonic-sairedis-{debian_version}' } + - { when: { arch: { not: amd64 } }, value: 'sonic-sairedis-{debian_version}.{arch}' } + debs: + - 'libsaivs_1.0.0_{arch}.deb' + - 'libsaivs-dev_1.0.0_{arch}.deb' + - 'libsairedis_1.0.0_{arch}.deb' + - 'libsairedis-dev_1.0.0_{arch}.deb' + - 'libsaimetadata_1.0.0_{arch}.deb' + - 'libsaimetadata-dev_1.0.0_{arch}.deb' + - 'syncd-vs_1.0.0_{arch}.deb' + + - name: sonic-dash-api + project: build + pipeline: sonic-net.sonic-dash-api + scopes: [build] + cascade_optional: true + result_filter: [succeeded] + artifact_name: + - { when: { arch: amd64, debian_version: { not: trixie } }, value: 'sonic-dash-api' } + - { when: { arch: { not: amd64 }, debian_version: { not: trixie } }, value: 'sonic-dash-api.{arch}' } + - { when: { arch: amd64, debian_version: trixie }, value: 'sonic-dash-api-trixie' } + - { when: { arch: { not: amd64 }, debian_version: trixie }, value: 'sonic-dash-api-trixie.{arch}' } + debs: + - 'libdashapi*.deb' + + # ------------------------------- Test ---------------------------------- # + - name: common-libs + project: build + pipeline: Azure.sonic-buildimage.common_libs + scopes: [test] + cascade_optional: true + result_filter: [succeeded] + apt_fix_broken: true + artifact_name: common-lib + debs: + - 'target/debs/{debian_version}/libyang3_*.deb' + - 'target/debs/{debian_version}/libyang-dev_3*.deb' + + - name: sonic-swss-common-test-host + project: build + pipeline: Azure.sonic-swss-common + scopes: [test] + cascade_optional: false + result_filter: [succeeded, partiallySucceeded] + apt_fix_broken: true + dpkg_args: [--force-confask,confnew] + artifact_name: sonic-swss-common.amd64.ubuntu22_04 + debs: + - 'libswsscommon_1.0.0_amd64.deb' + - 'python3-swsscommon_1.0.0_amd64.deb' + + - name: sonic-buildimage-ubuntu22-04 + project: build + pipeline: sonic-net.sonic-buildimage-ubuntu22.04 + scopes: [test] + cascade_optional: true + result_filter: [succeeded] + apt_fix_broken: true + artifact_name: sonic-buildimage.amd64.ubuntu22_04 + debs: + - 'libprotobuf*_amd64.deb' + - 'python3-protobuf*_amd64.deb' + - 'libdashapi*.deb' From d66a81294c7f03e8870e3514ead70d78c7ec17ca Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Fri, 7 Aug 2026 19:08:44 +0000 Subject: [PATCH 2/2] [ci]: keep dpkg force options as one test-host argument Quote --force-confask,confnew in YAML so it is passed to dpkg as a single option rather than being split into an option plus a nonexistent `confnew` archive path. Validated the corrected test scope end-to-end in Ubuntu 22.04: common-libs, swss-common test-host and sonic-buildimage artifacts install successfully, libyang Python bindings build, and the shared Redis hook completes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 19e0b615-271d-48f0-98cc-7ad6905c7f9e Signed-off-by: Lawrence Lee --- build-env/upstream-artifacts.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-env/upstream-artifacts.yaml b/build-env/upstream-artifacts.yaml index 7ea14d4e740..47dbba8af6c 100644 --- a/build-env/upstream-artifacts.yaml +++ b/build-env/upstream-artifacts.yaml @@ -58,7 +58,7 @@ upstream: cascade_optional: false result_filter: [succeeded, partiallySucceeded] apt_fix_broken: true - dpkg_args: [--force-confask,confnew] + dpkg_args: ["--force-confask,confnew"] artifact_name: sonic-swss-common.amd64.ubuntu22_04 debs: - 'libswsscommon_1.0.0_amd64.deb'