├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.md │ ├── enhancement.md │ └── other.md ├── dependabot.yaml └── workflows │ ├── build-test-lint.yml │ ├── codeql.yml │ ├── image-push-master.yml │ └── image-push-release.yml ├── .gitignore ├── .golangci.yml ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── cmd └── sriovdp │ ├── main.go │ ├── manager.go │ └── manager_test.go ├── deployments ├── README.md ├── cdi │ ├── README.md │ └── sriovdp-daemonset.yaml ├── configMap.yaml ├── flannel-network.yaml ├── pod-tc1.yaml ├── pod-tc2.yaml ├── sriov-crd.yaml └── sriovdp-daemonset.yaml ├── docs ├── README.md ├── bond │ ├── README.md │ ├── bond-crd.yaml │ ├── sriov-net.yaml │ └── test-pod.yaml ├── config-file │ └── README.md ├── ddp │ ├── README.md │ ├── e800 │ │ ├── configMap.yaml │ │ ├── crd-sriov-comms.yaml │ │ ├── crd-sriov-default.yaml │ │ ├── pod-comms.yaml │ │ └── pod-default.yaml │ └── x700 │ │ ├── configMap.yaml │ │ ├── crd-sriov-gtp.yaml │ │ ├── crd-sriov-pppoe.yaml │ │ ├── pod-gtp.yaml │ │ └── pod-pppoe.yaml ├── dpdk │ ├── README-virt.md │ ├── README.md │ ├── configMap-all-nics.yaml │ ├── configMap-virt.yaml │ ├── configMap.yaml │ ├── crd-dpdk-net2000.yaml │ ├── mlnx-dpdk │ │ ├── configMap.yaml │ │ ├── crd-dpdk-connectx4.yaml │ │ ├── crd-dpdk-connectx5.yaml │ │ ├── mlnx-pod_testpmd_connectx4.yaml │ │ └── mlnx-pod_testpmd_connectx5.yaml │ ├── pod_testpmd.yaml │ └── pod_testpmd_virt.yaml ├── rdma │ ├── README.md │ ├── configMap.yaml │ ├── crd-rdma-intel.yaml │ ├── crd-rdma-mlnx.yaml │ ├── pod_intel_rdma_test.yaml │ └── pod_mlx_rdma_test.yaml ├── subfunctions │ └── README.md ├── udev │ └── README.md ├── vdpa │ ├── README.md │ └── configMap.yaml └── vf-setup.md ├── go.mod ├── go.sum ├── images ├── Dockerfile ├── Dockerfile.arm64 ├── Dockerfile.ppc64le ├── README.md ├── ddptool-1.0.1.12.tar.gz └── entrypoint.sh └── pkg ├── accelerator ├── accelDevice.go ├── accelDeviceProvider.go ├── accelDeviceProvider_test.go ├── accelDevice_test.go ├── accelResourcePool.go ├── accelResourcePool_test.go └── accelerator_suite_test.go ├── auxnetdevice ├── auxNetDevice.go ├── auxNetDeviceProvider.go ├── auxNetDeviceProvider_test.go ├── auxNetDevice_test.go ├── auxNetResourcePool.go └── auxNetResourcePool_test.go ├── cdi ├── cdi.go ├── cdi_test.go └── mocks │ └── CDI.go ├── devices ├── api.go ├── api_test.go ├── devices_suite_test.go ├── devices_test.go ├── gen_net.go ├── gen_pci.go ├── host.go ├── host_test.go ├── rdma.go ├── rdma_test.go ├── vdpa.go └── vdpa_test.go ├── factory ├── factory.go └── factory_test.go ├── infoprovider ├── extraInfoProvider.go ├── extraInfoProvider_test.go ├── genericInfoProvider.go ├── genericInfoProvider_test.go ├── infoprovider_suite_test.go ├── rdmaInfoProvider.go ├── rdmaInfoProvider_test.go ├── uioInfoProvider.go ├── uioInfoProvider_test.go ├── vdpaInfoProvider.go ├── vdpaInfoProvider_test.go ├── vfioInfoProvider.go ├── vfioInfoProvider_test.go ├── vhostNetInfoProvider.go └── vhostNetInfoProvider_test.go ├── netdevice ├── nadutils.go ├── netDeviceProvider.go ├── netDeviceProvider_test.go ├── netResourcePool.go ├── netResourcePool_test.go ├── pciNetDevice.go └── pciNetDevice_test.go ├── resources ├── ddpSelector.go ├── ddpSelector_test.go ├── deviceSelectors.go ├── deviceSelectors_test.go ├── pKeySelector.go ├── pKeySelector_test.go ├── pool_stub.go ├── pool_stub_test.go ├── resources_suite_test.go ├── server.go ├── server_test.go └── testing.go ├── types ├── mocks │ ├── APIDevice.go │ ├── AccelDevice.go │ ├── AuxNetDevice.go │ ├── DeviceInfoProvider.go │ ├── DeviceProvider.go │ ├── DeviceSelector.go │ ├── HostDevice.go │ ├── LinkWatcher.go │ ├── NadUtils.go │ ├── NetDevice.go │ ├── PciDevice.go │ ├── PciNetDevice.go │ ├── RdmaSpec.go │ ├── ResourceFactory.go │ ├── ResourcePool.go │ ├── ResourceServer.go │ └── VdpaDevice.go └── types.go └── utils ├── ddp.go ├── ddp_test.go ├── mocks ├── NetlinkProvider.go ├── RdmaProvider.go ├── SriovnetProvider.go └── VdpaProvider.go ├── netlink_provider.go ├── netlink_provider_test.go ├── rdma_provider.go ├── sriovnet_provider.go ├── testdata └── pci.ids ├── testing.go ├── utils.go ├── utils_suite_test.go ├── utils_test.go └── vdpa_provider.go /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Report a bug with SR-IOV Network Device Plugin 4 | 5 | --- 6 | 7 | 8 | ### What happened? 9 | 10 | ### What did you expect to happen? 11 | 12 | ### What are the minimal steps needed to reproduce the bug? 13 | 14 | ### Anything else we need to know? 15 | 16 | ### Component Versions 17 | Please fill in the below table with the version numbers of components used. 18 | 19 | Component | Version| 20 | ------------------------------|--------------------| 21 | |SR-IOV Network Device Plugin || 22 | |SR-IOV CNI Plugin || 23 | |Multus || 24 | |Kubernetes || 25 | |OS || 26 | 27 | ### Config Files 28 | Config file locations may be config dependent. 29 | 30 | ##### Device pool config file location (Try '/etc/pcidp/config.json') 31 | 32 | ##### Multus config (Try '/etc/cni/multus/net.d') 33 | 34 | ##### CNI config (Try '/etc/cni/net.d/') 35 | 36 | ##### Kubernetes deployment type ( Bare Metal, Kubeadm etc.) 37 | 38 | ##### Kubeconfig file 39 | 40 | ##### SR-IOV Network Custom Resource Definition 41 | 42 | ### Logs 43 | ##### SR-IOV Network Device Plugin Logs (use `kubectl logs $PODNAME`) 44 | 45 | ##### Multus logs (If enabled. Try '/var/log/multus.log' ) 46 | 47 | ##### Kubelet logs (journalctl -u kubelet) 48 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Enhancement / Feature Request 3 | about: Suggest an enhancement or new feature for SR-IOV Network Device Plugin 4 | 5 | --- 6 | 7 | 8 | 9 | ## What would you like to be added? 10 | 11 | ## What is the use case for this feature / enhancement? 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/other.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Other Issues 3 | about: For everything that isn't a bug report or a feature request 4 | 5 | --- 6 | 7 | 8 | 9 | ## What issue would you like to bring attention to? 10 | 11 | ## What is the impact of this issue? 12 | 13 | ## Do you have a proposed response or remediation for the issue? -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Docker images 4 | - package-ecosystem: "docker" 5 | directory: "/images" 6 | schedule: 7 | interval: "weekly" 8 | day: "monday" 9 | commit-message: 10 | prefix: "chore:" 11 | 12 | # GitHub Actions 13 | - package-ecosystem: "github-actions" 14 | directory: "/" 15 | schedule: 16 | interval: "weekly" 17 | day: "monday" 18 | commit-message: 19 | prefix: "chore:" 20 | 21 | # Go modules 22 | - package-ecosystem: "gomod" 23 | directory: "/" 24 | schedule: 25 | interval: "weekly" 26 | day: "monday" 27 | commit-message: 28 | prefix: "chore:" 29 | groups: 30 | kubernetes-dependencies: 31 | patterns: 32 | - "k8s.io/*" 33 | gomod-dependencies: 34 | patterns: 35 | - "*" 36 | exclude-patterns: 37 | - "k8s.io/*" 38 | -------------------------------------------------------------------------------- /.github/workflows/build-test-lint.yml: -------------------------------------------------------------------------------- 1 | name: build-test-lint 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | name: build 6 | runs-on: ubuntu-24.04 7 | steps: 8 | - name: Set up Go 9 | uses: actions/setup-go@v6 10 | with: 11 | go-version: 1.23.x 12 | 13 | - name: Check out code into the Go module directory 14 | uses: actions/checkout@v5 15 | 16 | - name: Build 17 | run: make build 18 | 19 | test: 20 | runs-on: ubuntu-24.04 21 | needs: build 22 | name: test 23 | steps: 24 | - name: Set up Go 25 | uses: actions/setup-go@v6 26 | with: 27 | go-version: 1.23.x 28 | 29 | - name: Check out code into the Go module directory 30 | uses: actions/checkout@v5 31 | 32 | - name: Install hwdata 33 | run: sudo apt-get install hwdata -y 34 | 35 | - name: Go test 36 | run: make test-race 37 | 38 | test-coverage: 39 | runs-on: ubuntu-24.04 40 | needs: build 41 | name: test-coverage 42 | steps: 43 | - name: Set up Go 44 | uses: actions/setup-go@v6 45 | with: 46 | go-version: 1.23.x 47 | 48 | - uses: actions/checkout@v5 49 | 50 | - name: Install hwdata 51 | run: sudo apt-get install hwdata -y 52 | 53 | - name: Go test with coverage 54 | run: make test-coverage 55 | 56 | - name: Coveralls 57 | uses: coverallsapp/github-action@v2 58 | with: 59 | github-token: ${{ secrets.GITHUB_TOKEN }} 60 | file: test/coverage/cover.out 61 | 62 | golangci: 63 | name: Golangci-lint 64 | runs-on: ubuntu-24.04 65 | steps: 66 | - name: Set up Go 67 | uses: actions/setup-go@v6 68 | with: 69 | go-version: 1.23.x 70 | 71 | - name: checkout 72 | uses: actions/checkout@v5 73 | 74 | - name: lint test 75 | run: make lint 76 | 77 | shellcheck: 78 | name: Shellcheck 79 | runs-on: ubuntu-24.04 80 | env: 81 | SHELLCHECK_OPTS: -e SC3037 # disabled because of false issue in entrypoint.sh ln 14-21. Not using any complicated flags. Works with alpines almquist shell. 82 | steps: 83 | - uses: actions/checkout@v5 84 | - name: Run ShellCheck 85 | uses: ludeeus/action-shellcheck@master 86 | with: 87 | ignore: vendor 88 | 89 | hadolint: 90 | runs-on: ubuntu-24.04 91 | name: Hadolint 92 | steps: 93 | - uses: actions/checkout@v5 94 | - uses: hadolint/hadolint-action@v3.1.0 95 | name: Run Hadolint 96 | with: 97 | dockerfile: ./images/Dockerfile 98 | ignore: DL3018 # DL3018: GH issue 368 99 | 100 | go-check: 101 | runs-on: ubuntu-24.04 102 | steps: 103 | - uses: actions/checkout@v5 104 | 105 | - name: Set up Go 106 | uses: actions/setup-go@v6 107 | with: 108 | go-version: 1.23.x 109 | 110 | # if this fails, run go mod tidy 111 | - name: Check if module files are consistent with code 112 | run: go mod tidy && git diff --exit-code 113 | 114 | # if this fails, run go mod vendor 115 | - name: Check if vendor directory is consistent with go modules 116 | run: go mod vendor && git diff --exit-code 117 | 118 | sriov-operator-e2e-test: 119 | name: SR-IOV operator e2e tests 120 | needs: [ build,test ] 121 | runs-on: [ sriov ] 122 | env: 123 | TEST_REPORT_PATH: k8s-artifacts 124 | steps: 125 | - name: Check out the repo 126 | uses: actions/checkout@v5 127 | 128 | - name: build sriov-network-device-plugin image 129 | run: podman build -f images/Dockerfile -t ghaction-sriov-network-device-plugin:pr-${{github.event.pull_request.number}} . 130 | 131 | - name: Check out sriov operator's code 132 | uses: actions/checkout@v5 133 | with: 134 | repository: k8snetworkplumbingwg/sriov-network-operator 135 | path: sriov-network-operator-wc 136 | 137 | - name: run test 138 | run: make test-e2e-conformance-virtual-k8s-cluster-ci 139 | working-directory: sriov-network-operator-wc 140 | env: 141 | LOCAL_SRIOV_DEVICE_PLUGIN_IMAGE: ghaction-sriov-network-device-plugin:pr-${{github.event.pull_request.number}} 142 | 143 | - uses: actions/upload-artifact@v5 144 | if: always() 145 | with: 146 | name: ${{ env.TEST_REPORT_PATH }} 147 | path: ./sriov-network-operator-wc/${{ env.TEST_REPORT_PATH }} 148 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | schedule: 9 | - cron: "37 4 * * 0" 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze 14 | runs-on: ubuntu-24.04 15 | permissions: 16 | actions: read 17 | contents: read 18 | security-events: write 19 | 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | language: [ go ] 24 | 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v5 28 | 29 | - name: Initialize CodeQL 30 | uses: github/codeql-action/init@v4 31 | with: 32 | languages: ${{ matrix.language }} 33 | queries: +security-and-quality 34 | 35 | - name: Autobuild 36 | uses: github/codeql-action/autobuild@v4 37 | 38 | - name: Perform CodeQL Analysis 39 | uses: github/codeql-action/analyze@v4 40 | with: 41 | category: "/language:${{ matrix.language }}" 42 | -------------------------------------------------------------------------------- /.github/workflows/image-push-master.yml: -------------------------------------------------------------------------------- 1 | name: "Push images on merge to master" 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | build-and-push-amd64-device-plugin: 8 | name: Image push AMD64 9 | runs-on: ubuntu-24.04 10 | env: 11 | IMAGE_NAME: ghcr.io/${{ github.repository }} 12 | steps: 13 | - name: Check out the repo 14 | uses: actions/checkout@v5 15 | 16 | - name: Set up Docker Buildx 17 | uses: docker/setup-buildx-action@v3 18 | 19 | - name: Login to Docker 20 | uses: docker/login-action@v3 21 | with: 22 | registry: ghcr.io 23 | username: ${{ github.repository_owner }} 24 | password: ${{ secrets.GITHUB_TOKEN }} 25 | 26 | - name: Build and push sriov-network-device-plugin 27 | uses: docker/build-push-action@v6 28 | with: 29 | context: . 30 | platforms: linux/amd64 31 | push: true 32 | tags: | 33 | ${{ env.IMAGE_NAME }}:latest-amd64 34 | ${{ env.IMAGE_NAME }}:${{ github.sha }} 35 | file: images/Dockerfile 36 | 37 | build-and-push-arm64-device-plugin: 38 | name: Image push ARM64 39 | runs-on: ubuntu-24.04 40 | env: 41 | IMAGE_NAME: ghcr.io/${{ github.repository }} 42 | steps: 43 | - name: Check out the repo 44 | uses: actions/checkout@v5 45 | 46 | - name: Set up QEMU 47 | uses: docker/setup-qemu-action@v3 48 | 49 | - name: Set up Docker Buildx 50 | uses: docker/setup-buildx-action@v3 51 | 52 | - name: Login to Docker 53 | uses: docker/login-action@v3 54 | with: 55 | registry: ghcr.io 56 | username: ${{ github.repository_owner }} 57 | password: ${{ secrets.GITHUB_TOKEN }} 58 | 59 | - name: Build and push sriov-network-device-plugin 60 | uses: docker/build-push-action@v6 61 | with: 62 | context: . 63 | platforms: linux/arm64 64 | push: true 65 | tags: | 66 | ${{ env.IMAGE_NAME }}:latest-arm64 67 | file: images/Dockerfile.arm64 68 | 69 | build-and-push-ppc64le-device-plugin: 70 | name: Image push ppc64le 71 | runs-on: ubuntu-24.04 72 | env: 73 | IMAGE_NAME: ghcr.io/${{ github.repository }} 74 | steps: 75 | - name: Check out the repo 76 | uses: actions/checkout@v5 77 | 78 | - name: Set up QEMU 79 | uses: docker/setup-qemu-action@v3 80 | 81 | - name: Set up Docker Buildx 82 | uses: docker/setup-buildx-action@v3 83 | 84 | - name: Login to Docker 85 | uses: docker/login-action@v3 86 | with: 87 | registry: ghcr.io 88 | username: ${{ github.repository_owner }} 89 | password: ${{ secrets.GITHUB_TOKEN }} 90 | 91 | - name: Build and push sriov-network-device-plugin 92 | uses: docker/build-push-action@v6 93 | with: 94 | context: . 95 | platforms: linux/ppc64le 96 | push: true 97 | tags: | 98 | ${{ env.IMAGE_NAME }}:latest-ppc64le 99 | file: images/Dockerfile.ppc64le 100 | 101 | push-manifest: 102 | runs-on: ubuntu-24.04 103 | env: 104 | IMAGE_NAME: ghcr.io/${{ github.repository }} 105 | needs: [build-and-push-amd64-device-plugin,build-and-push-arm64-device-plugin,build-and-push-ppc64le-device-plugin] 106 | steps: 107 | - name: Set up Docker Buildx 108 | uses: docker/setup-buildx-action@v3 109 | 110 | - name: Login to GitHub Container Registry 111 | uses: docker/login-action@v3 112 | with: 113 | registry: ghcr.io 114 | username: ${{ github.repository_owner }} 115 | password: ${{ secrets.GITHUB_TOKEN }} 116 | 117 | - name: Create manifest for multi-arch images 118 | run: | 119 | docker buildx imagetools create -t ${{ env.IMAGE_NAME }}:latest \ 120 | ${{ env.IMAGE_NAME }}:latest-amd64 \ 121 | ${{ env.IMAGE_NAME }}:latest-arm64 \ 122 | ${{ env.IMAGE_NAME }}:latest-ppc64le 123 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.gopath/* 2 | /build/* 3 | /test/coverage* 4 | 5 | # Created by https://www.gitignore.io/api/go,macos,linux,windows,visualstudiocode 6 | 7 | ### Go ### 8 | # Binaries for programs and plugins 9 | bin 10 | *.exe 11 | *.exe~ 12 | *.dll 13 | *.so 14 | *.dylib 15 | 16 | # Test binary, build with `go test -c` 17 | *.test 18 | 19 | # Output of the go coverage tool, specifically when used with LiteIDE 20 | *.out 21 | 22 | ### Linux ### 23 | *~ 24 | 25 | # temporary files which can be created if a process still has a handle open of a deleted file 26 | .fuse_hidden* 27 | 28 | # KDE directory preferences 29 | .directory 30 | 31 | # Linux trash folder which might appear on any partition or disk 32 | .Trash-* 33 | 34 | # .nfs files are created when an open file is removed but is still being accessed 35 | .nfs* 36 | 37 | ### macOS ### 38 | *.DS_Store 39 | .AppleDouble 40 | .LSOverride 41 | 42 | # Icon must end with two \r 43 | Icon 44 | 45 | # Thumbnails 46 | ._* 47 | 48 | # Files that might appear in the root of a volume 49 | .DocumentRevisions-V100 50 | .fseventsd 51 | .Spotlight-V100 52 | .TemporaryItems 53 | .Trashes 54 | .VolumeIcon.icns 55 | .com.apple.timemachine.donotpresent 56 | 57 | # Directories potentially created on remote AFP share 58 | .AppleDB 59 | .AppleDesktop 60 | Network Trash Folder 61 | Temporary Items 62 | .apdisk 63 | 64 | ### VisualStudioCode ### 65 | .vscode/* 66 | !.vscode/settings.json 67 | !.vscode/tasks.json 68 | !.vscode/launch.json 69 | !.vscode/extensions.json 70 | .history 71 | 72 | ### Windows ### 73 | # Windows thumbnail cache files 74 | Thumbs.db 75 | ehthumbs.db 76 | ehthumbs_vista.db 77 | 78 | # Folder config file 79 | Desktop.ini 80 | 81 | # Recycle Bin used on file shares 82 | $RECYCLE.BIN/ 83 | 84 | # Windows Installer files 85 | *.cab 86 | *.msi 87 | *.msm 88 | *.msp 89 | 90 | # Windows shortcuts 91 | *.lnk 92 | 93 | 94 | # End of https://www.gitignore.io/api/go,macos,linux,windows,visualstudiocode 95 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | run: 3 | timeout: 5m 4 | allow-parallel-runners: true 5 | linters: 6 | default: none 7 | enable: 8 | - copyloopvar 9 | - dupl 10 | - errcheck 11 | - funlen 12 | - ginkgolinter 13 | - goconst 14 | - gocyclo 15 | - govet 16 | - gocritic 17 | - gosec 18 | - goprintffuncname 19 | - importas 20 | - ineffassign 21 | - lll 22 | - misspell 23 | - mnd 24 | - nakedret 25 | - prealloc 26 | - staticcheck 27 | - unconvert 28 | - unparam 29 | - unused 30 | - whitespace 31 | settings: 32 | dupl: 33 | threshold: 100 34 | funlen: 35 | statements: 50 36 | lines: 100 37 | lll: 38 | line-length: 140 39 | mnd: 40 | # don't include the "operation" and "assign" 41 | checks: ["argument", "case", "condition", "return"] 42 | goconst: 43 | min-len: 2 44 | min-occurrences: 2 45 | gocyclo: 46 | min-complexity: 15 47 | gosec: 48 | excludes: 49 | - G304 50 | ginkgolinter: 51 | forbid-focus-container: true 52 | misspell: 53 | locale: US 54 | staticcheck: 55 | checks: 56 | - all 57 | - -ST1000 58 | - -ST1003 59 | - -ST1016 60 | - -QF1003 61 | - -QF1008 62 | dot-import-whitelist: 63 | - github.com/onsi/ginkgo/v2 64 | - github.com/onsi/gomega 65 | exclusions: 66 | generated: lax 67 | paths: 68 | - .github/* 69 | - deployments/* 70 | - docs/* 71 | - pkg/utils/testing.go 72 | - pkg/resources/testing.go 73 | rules: 74 | - linters: 75 | - dupl 76 | - goconst 77 | - lll 78 | - gosec 79 | path: _test\.go 80 | 81 | issues: 82 | max-issues-per-linter: 0 83 | max-same-issues: 0 84 | formatters: 85 | enable: 86 | - gci 87 | - gofmt 88 | - goimports 89 | settings: 90 | gci: 91 | sections: 92 | - standard 93 | - default 94 | - prefix(github.com/k8snetworkplumbingwg/sriov-network-device-plugin) 95 | custom-order: true 96 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # SRIOV Network Device plugin 2 | 3 | * [Meetings](#meetings) 4 | * [How to Contribute](#contributing-code) 5 | * [Coding Style](#coding-style) 6 | * [Format of the patch](#format-of-the-patch) 7 | * [Contributing Code](#contributing-code) 8 | * [Tools](#Tools) 9 | 10 | ## Meetings 11 | Join us for project discussions at _K8s Network & Resource management_ meetings. 12 | The meetings take place on a weekly basis on Monday and Tuesday in alternating weeks: 13 | 14 | * Time: 15:00 - 16:00 GMT / 10:00-11:00 ET / 07:00-08:00 PST every other Monday 15 | * Time: 14:00 - 15:00 GMT / 09:00-10:00 ET / 06:00-07:00 PST on every other Tuesday 16 | 17 | 18 | * [Meeting notes and agenda](https://docs.google.com/document/d/1sJQMHbxZdeYJPgAWK1aSt6yzZ4K_8es7woVIrwinVwI/edit?usp=sharing) 19 | * [Zoom channel](https://zoom.us/j/2392609689?pwd=K1R4ZEQyWWJVSGV3V1pmVThLMlhqQT09) 20 | 21 | ## How to Contribute 22 | 23 | SRIOV Network Device plugin is [Apache 2.0 licensed](LICENSE) and accepts contributions via GitHub pull requests. 24 | This document outlines some of the conventions on development workflow, commit message formatting, 25 | contact points and other resources to make it easier to get your contribution accepted. 26 | 27 | ## Coding Style 28 | 29 | Please follows the standard formatting recommendations and language idioms set out in [Effective Go](https://golang.org/doc/effective_go.html) and in the [Go Code Review Comments wiki](https://github.com/golang/go/wiki/CodeReviewComments). 30 | 31 | ## Format of the patch 32 | 33 | Each patch is expected to comply with the following format: 34 | 35 | ``` 36 | Change summary 37 | 38 | More detailed explanation of your changes: Why and how. 39 | Wrap it to 72 characters. 40 | See [here] (http://chris.beams.io/posts/git-commit/) 41 | for some more good advices. 42 | 43 | [Fixes #NUMBER (or URL to the issue)] 44 | ``` 45 | 46 | For example: 47 | 48 | ``` 49 | Fix poorly named identifiers 50 | 51 | One identifier, fnname, in func.go was poorly named. It has been renamed 52 | to fnName. Another identifier retval was not needed and has been removed 53 | entirely. 54 | 55 | Fixes #1 56 | ``` 57 | 58 | ## Contributing Code 59 | 60 | We always encourage the contribution for the community project. We like to collaborate with various stake holder on this project. We ask developer to keep following guidelines in mind before the contribution. 61 | 62 | * Make sure to create an [Issue](https://github.com/k8snetworkplumbingwg/sriov-network-device-plugin/issues) for bug fix or the feature request. 63 | Issues are discussed on a regular basis at _K8s Network & Resource management_ meetings. 64 | * **For bugs**: For the bug fixes, please follow the issue template format while creating a issue. If you have already found a fix, feel free to submit a Pull Request referencing the Issue you created. Include the `Fixes #` syntax to link it to the issue you're addressing. 65 | * **For feature requests**, For the feature requests, please follow the issue template format while creating a feature requests. We want to improve upon SRIOV Network device plugin incrementally which means small changes or features at a time. 66 | * Please make sure each PR are compiling or passed by Travis. 67 | * In order to ensure your PR can be reviewed in a timely manner, please keep PRs small 68 | 69 | Once you're ready to contribute code back to this repo, start with these steps: 70 | * Fork the appropriate sub-projects that are affected by your change. 71 | * Clone the fork to your machine: 72 | 73 | ``` 74 | $ git clone https://github.com/k8snetworkplumbingwg/sriov-network-device-plugin.git 75 | ``` 76 | 77 | * Create a topic branch with prefix `dev/` for your change and checkout that branch: 78 | 79 | ``` 80 | $ git checkout -b dev/some-topic-branch 81 | ``` 82 | * Make your changes to the code and add tests to cover contributed code. 83 | * Run `./build.sh` to validate it builds and will not break current functionality. 84 | * Commit your changes and push them to your fork. 85 | * Open a pull request for the appropriate project. 86 | * Contributors will review your pull request, suggest changes, run tests and eventually merge or close the request. 87 | 88 | > We encourage contributor to test SRIOV Network Device plugin with various NICs to check the compatibility. 89 | > 90 | ## Contact Us 91 | - General channel on [NPWG](https://npwg-team.slack.com/) Slack. Request an invite to NPWG slack [here](https://intel-corp.herokuapp.com/). 92 | - Feel free to post GitHub issues and PRs for review 93 | - Attend either K8s Network & Resource mangement or Additional K8s Network & Resource management meetings 94 | -------------------------------------------------------------------------------- /cmd/sriovdp/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Intel Corp. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "flag" 19 | "os" 20 | "os/signal" 21 | "syscall" 22 | 23 | "github.com/golang/glog" 24 | ) 25 | 26 | const ( 27 | defaultConfig = "/etc/pcidp/config.json" 28 | ) 29 | 30 | // flagInit parse command line flags 31 | func flagInit(cp *cliParams) { 32 | flag.StringVar(&cp.configFile, "config-file", defaultConfig, 33 | "JSON device pool config file location") 34 | flag.StringVar(&cp.resourcePrefix, "resource-prefix", "intel.com", 35 | "resource name prefix used for K8s extended resource") 36 | flag.BoolVar(&cp.useCdi, "use-cdi", false, 37 | "Use Container Device Interface to expose devices in containers") 38 | } 39 | 40 | func main() { 41 | cp := &cliParams{} 42 | flagInit(cp) 43 | flag.Parse() 44 | rm := newResourceManager(cp) 45 | 46 | glog.Infof("resource manager reading configs") 47 | if err := rm.readConfig(); err != nil { 48 | glog.Errorf("error getting resources from file %v", err) 49 | return 50 | } 51 | 52 | if len(rm.configList) < 1 { 53 | glog.Errorf("no resource configuration; exiting") 54 | return // No config found 55 | } 56 | 57 | // Validate configs 58 | if !rm.validConfigs() { 59 | glog.Fatalf("Exiting.. one or more invalid configuration(s) given") 60 | return 61 | } 62 | glog.Infof("Discovering host devices") 63 | if err := rm.discoverHostDevices(); err != nil { 64 | glog.Errorf("error discovering host devices%v", err) 65 | return 66 | } 67 | 68 | glog.Infof("Initializing resource servers") 69 | if err := rm.initServers(); err != nil { 70 | glog.Errorf("error initializing resource servers %v", err) 71 | return 72 | } 73 | 74 | glog.Infof("Starting all servers...") 75 | if err := rm.startAllServers(); err != nil { 76 | glog.Errorf("error starting resource servers %v\n", err) 77 | return 78 | } 79 | glog.Infof("All servers started.") 80 | glog.Infof("Listening for term signals") 81 | // respond to syscalls for termination 82 | sigCh := make(chan os.Signal, 1) 83 | signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) 84 | 85 | // Catch termination signals 86 | sig := <-sigCh 87 | glog.Infof("Received signal \"%v\", shutting down.", sig) 88 | if err := rm.stopAllServers(); err != nil { 89 | glog.Errorf("stopping servers produced error: %s", err.Error()) 90 | } 91 | if err := rm.cleanupCDISpecs(); err != nil { 92 | glog.Errorf("cleaning up CDI Specs produced error: %s", err.Error()) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /deployments/README.md: -------------------------------------------------------------------------------- 1 | ## Sample K8s deployment files 2 | This directory contains sample yaml files to deploy workloads with SRIOV resources in Kubernetes using sriov-device-plugin. -------------------------------------------------------------------------------- /deployments/cdi/README.md: -------------------------------------------------------------------------------- 1 | # SR-IOV Network Device Plugin in CDI mode deployment 2 | 3 | SR-IOV Network Device Plugin supports [Container Device Interface (CDI)](https://github.com/container-orchestrated-devices/container-device-interface). 4 | 5 | To enable CDI mode, SR-IOV Network Device Plugin should be started with `--use-cdi` CLI argument. 6 | This mode has different deployment requirements: `sriovdp-daemonset.yaml` 7 | 8 | ```yaml 9 | - mountPath: /var/run/cdi 10 | name: dynamic-cdi 11 | ``` 12 | -------------------------------------------------------------------------------- /deployments/configMap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: sriovdp-config 5 | namespace: kube-system 6 | data: 7 | config.json: | 8 | { 9 | "resourceList": [{ 10 | "resourceName": "intel_sriov_netdevice", 11 | "selectors": { 12 | "vendors": ["8086"], 13 | "devices": ["154c", "10ed", "1889"], 14 | "drivers": ["i40evf", "iavf", "ixgbevf"] 15 | } 16 | }, 17 | { 18 | "resourceName": "intel_sriov_dpdk", 19 | "selectors": { 20 | "vendors": ["8086"], 21 | "devices": ["154c", "10ed", "1889"], 22 | "drivers": ["vfio-pci"], 23 | "pfNames": ["enp0s0f0","enp2s2f1"] 24 | } 25 | }, 26 | { 27 | "resourceName": "mlnx_sriov_rdma", 28 | "selectors": { 29 | "vendors": ["15b3"], 30 | "devices": ["1018"], 31 | "drivers": ["mlx5_ib"], 32 | "isRdma": true 33 | } 34 | }, 35 | { 36 | "resourceName": "brcm_sriov_bnxt", 37 | "selectors": { 38 | "vendors": ["14e4"], 39 | "devices": ["1750"], 40 | "drivers": ["bnxt_en"] 41 | } 42 | } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /deployments/flannel-network.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "k8s.cni.cncf.io/v1" 2 | kind: NetworkAttachmentDefinition 3 | metadata: 4 | name: flannel-net 5 | spec: 6 | config: '{ 7 | "cniVersion": "0.3.0", 8 | "type": "flannel", 9 | "delegate": { 10 | "isDefaultGateway": true 11 | } 12 | }' 13 | -------------------------------------------------------------------------------- /deployments/pod-tc1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: testpod1 5 | annotations: 6 | k8s.v1.cni.cncf.io/networks: sriov-net1 7 | spec: 8 | containers: 9 | - name: appcntr1 10 | image: centos/tools 11 | imagePullPolicy: IfNotPresent 12 | command: [ "/bin/bash", "-c", "--" ] 13 | args: [ "while true; do sleep 300000; done;" ] 14 | resources: 15 | requests: 16 | intel.com/intel_sriov_netdevice: '1' 17 | limits: 18 | intel.com/intel_sriov_netdevice: '1' 19 | -------------------------------------------------------------------------------- /deployments/pod-tc2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: testpod2 5 | annotations: 6 | k8s.v1.cni.cncf.io/networks: sriov-net1 7 | spec: 8 | containers: 9 | - name: appcntr2 10 | image: centos/tools 11 | imagePullPolicy: IfNotPresent 12 | command: [ "/bin/bash", "-c", "--" ] 13 | args: [ "while true; do sleep 300000; done;" ] 14 | resources: 15 | requests: 16 | intel.com/intel_sriov_netdevice: '1' 17 | limits: 18 | intel.com/intel_sriov_netdevice: '1' 19 | -------------------------------------------------------------------------------- /deployments/sriov-crd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "k8s.cni.cncf.io/v1" 2 | kind: NetworkAttachmentDefinition 3 | metadata: 4 | name: sriov-net1 5 | annotations: 6 | k8s.v1.cni.cncf.io/resourceName: intel.com/intel_sriov_netdevice 7 | spec: 8 | config: '{ 9 | "type": "sriov", 10 | "cniVersion": "0.3.1", 11 | "name": "sriov-network", 12 | "ipam": { 13 | "type": "host-local", 14 | "subnet": "10.56.217.0/24", 15 | "routes": [{ 16 | "dst": "0.0.0.0/0" 17 | }], 18 | "gateway": "10.56.217.1" 19 | } 20 | }' 21 | -------------------------------------------------------------------------------- /deployments/sriovdp-daemonset.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: sriov-device-plugin 6 | namespace: kube-system 7 | 8 | --- 9 | apiVersion: apps/v1 10 | kind: DaemonSet 11 | metadata: 12 | name: kube-sriov-device-plugin 13 | namespace: kube-system 14 | labels: 15 | tier: node 16 | app: sriovdp 17 | spec: 18 | selector: 19 | matchLabels: 20 | name: sriov-device-plugin 21 | template: 22 | metadata: 23 | labels: 24 | name: sriov-device-plugin 25 | tier: node 26 | app: sriovdp 27 | spec: 28 | hostNetwork: true 29 | serviceAccountName: sriov-device-plugin 30 | containers: 31 | - name: kube-sriovdp 32 | image: ghcr.io/k8snetworkplumbingwg/sriov-network-device-plugin:latest 33 | imagePullPolicy: IfNotPresent 34 | args: 35 | - --log-dir=sriovdp 36 | - --log-level=10 37 | securityContext: 38 | privileged: true 39 | resources: 40 | requests: 41 | cpu: "250m" 42 | memory: "40Mi" 43 | limits: 44 | cpu: 1 45 | memory: "200Mi" 46 | volumeMounts: 47 | - name: devicesock 48 | mountPath: /var/lib/kubelet/device-plugins 49 | readOnly: false 50 | - name: plugins-registry 51 | mountPath: /var/lib/kubelet/plugins_registry 52 | readOnly: false 53 | - name: log 54 | mountPath: /var/log 55 | - name: config-volume 56 | mountPath: /etc/pcidp 57 | - name: device-info 58 | mountPath: /var/run/k8s.cni.cncf.io/devinfo/dp 59 | volumes: 60 | - name: devicesock 61 | hostPath: 62 | path: /var/lib/kubelet/device-plugins 63 | - name: plugins-registry 64 | hostPath: 65 | path: /var/lib/kubelet/plugins_registry 66 | - name: log 67 | hostPath: 68 | path: /var/log 69 | - name: device-info 70 | hostPath: 71 | path: /var/run/k8s.cni.cncf.io/devinfo/dp 72 | type: DirectoryOrCreate 73 | - name: config-volume 74 | configMap: 75 | name: sriovdp-config 76 | items: 77 | - key: config.json 78 | path: config.json 79 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Additional documentation 2 | This page contains supplimentary documention that users may find useful for various use-cases with SR-IOV Network Device Plugin. 3 | 4 | * [Running DPDK application in Kubernetes](dpdk/) 5 | * [Providing Failover with a bonded interface in Kubernetes](bond/) 6 | * [Using UDEV Rules for changing NIC's network device name](udev/) 7 | * [Running RDMA application in Kubernetes](rdma/) 8 | * [SR-IOV Network Device Plugin with DDP](ddp/) 9 | * [Using node specific config file for running device plugin DaemonSet](config-file) 10 | * [Using vDPA devices in Kubernetes](vdpa/) 11 | * [SR-IOV Network Device Plugin with Scalable Functions](scalable-functions) 12 | -------------------------------------------------------------------------------- /docs/bond/bond-crd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "k8s.cni.cncf.io/v1" 2 | kind: NetworkAttachmentDefinition 3 | metadata: 4 | name: bond-net 5 | spec: 6 | config: '{ 7 | "type": "bond", 8 | "cniVersion": "0.3.1", 9 | "name": "bond-net", 10 | "ifname": "bond0", 11 | "mode": "active-backup", 12 | "failOverMac": 1, 13 | "linksInContainer": true, 14 | "miimon": "100", 15 | "links": [ 16 | {"name": "net1"}, 17 | {"name": "net2"} 18 | ], 19 | "ipam": { 20 | "type": "host-local", 21 | "subnet": "10.56.217.0/24", 22 | "routes": [{ 23 | "dst": "0.0.0.0/0" 24 | }], 25 | "gateway": "10.56.217.1" 26 | } 27 | }' 28 | -------------------------------------------------------------------------------- /docs/bond/sriov-net.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "k8s.cni.cncf.io/v1" 2 | kind: NetworkAttachmentDefinition 3 | metadata: 4 | name: sriov-net 5 | annotations: 6 | k8s.v1.cni.cncf.io/resourceName: intel.com/intel_sriov_netdevice 7 | spec: 8 | config: '{ 9 | "type": "sriov", 10 | "cniVersion": "0.3.1", 11 | "name": "sriov-network" 12 | }' 13 | -------------------------------------------------------------------------------- /docs/bond/test-pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: test-pod 5 | annotations: 6 | k8s.v1.cni.cncf.io/networks: '[ 7 | {"name": "sriov-net", 8 | "interface": "net1" 9 | }, 10 | {"name": "sriov-net", 11 | "interface": "net2" 12 | }, 13 | {"name": "bond-net", 14 | "interface": "bond0" 15 | }]' 16 | spec: # specification of the pod's contents 17 | restartPolicy: Never 18 | containers: 19 | - name: bond-test 20 | image: alpine:latest 21 | command: 22 | - /bin/sh 23 | - "-c" 24 | - "sleep 60m" 25 | imagePullPolicy: IfNotPresent 26 | resources: 27 | requests: 28 | intel.com/intel_sriov_netdevice: '2' 29 | limits: 30 | intel.com/intel_sriov_netdevice: '2' 31 | -------------------------------------------------------------------------------- /docs/config-file/README.md: -------------------------------------------------------------------------------- 1 | # Using node specific config file for running device plugin DaemonSet 2 | 3 | To allow granular and accurate control over which SR-IOV devices can be exposed as kubernetes extended resource, it is sometimes required to define a per-node config file when launching SR-IOV Device Plugin DaemonSet in a heterogeneous cluster. Since SR-IOV Device Plugin provides a command line option `--config-file`, the node specific config file can be achieved by running the following steps: 4 | 5 | 1. Generate configMap with node specific sections: 6 | ``` 7 | apiVersion: v1 8 | kind: ConfigMap 9 | metadata: 10 | name: sriovdp-config 11 | data: 12 | sriov-node-0: '{"resourceList":[{"resourceName":"sriovnics","selectors":{"pfNames":["ens785f0#0-4","ens785f1#0-9"],"IsRdma":false,"NeedVhostNet":false},"SelectorObj":null}]}' 13 | sriov-node-1: '{"resourceList":[{"resourceName":"sriovnics","selectors":{"pfNames":["ens785f0#0-9","ens785f1#0-4"],"IsRdma":false,"NeedVhostNet":false},"SelectorObj":null}]}' 14 | ``` 15 | 16 | `sriov-node-0` and `sriov-node-1` match the kubernetes node names. 17 | 18 | 2. Launch device plugin DaemonSet: 19 | ``` 20 | --- 21 | apiVersion: v1 22 | kind: ServiceAccount 23 | metadata: 24 | name: sriov-device-plugin 25 | namespace: kube-system 26 | 27 | --- 28 | apiVersion: apps/v1 29 | kind: DaemonSet 30 | metadata: 31 | name: kube-sriov-device-plugin-amd64 32 | namespace: kube-system 33 | labels: 34 | tier: node 35 | app: sriovdp 36 | spec: 37 | selector: 38 | matchLabels: 39 | name: sriov-device-plugin 40 | template: 41 | metadata: 42 | labels: 43 | name: sriov-device-plugin 44 | tier: node 45 | app: sriovdp 46 | spec: 47 | hostNetwork: true 48 | nodeSelector: 49 | kubernetes.io/arch: amd64 50 | tolerations: 51 | - key: node-role.kubernetes.io/master 52 | operator: Exists 53 | effect: NoSchedule 54 | serviceAccountName: sriov-device-plugin 55 | containers: 56 | - name: kube-sriovdp 57 | image: ghcr.io/k8snetworkplumbingwg/sriov-network-device-plugin:latest 58 | imagePullPolicy: IfNotPresent 59 | args: 60 | - --log-dir=sriovdp 61 | - --log-level=10 62 | - --config-file=/etc/pcidp/$(NODE_NAME) 63 | env: 64 | - name: NODE_NAME 65 | valueFrom: 66 | fieldRef: 67 | fieldPath: spec.nodeName 68 | securityContext: 69 | privileged: true 70 | volumeMounts: 71 | - name: devicesock 72 | mountPath: /var/lib/kubelet/ 73 | readOnly: false 74 | - name: log 75 | mountPath: /var/log 76 | - name: config-volume 77 | mountPath: /etc/pcidp 78 | volumes: 79 | - name: devicesock 80 | hostPath: 81 | path: /var/lib/kubelet/ 82 | - name: log 83 | hostPath: 84 | path: /var/log 85 | - name: config-volume 86 | configMap: 87 | name: sriovdp-config 88 | ``` 89 | 90 | `sriovdp-config` configMap maps node specific config data to device plugin container volume as separate files such as `sriov-node-0` and `sriov-node-1`. 91 | `NODE_NAME` environment variable is defined from `.spec.nodeName` and is equal to the node name which matches with data entry in `sriovdp-config` configMap. 92 | `--config-file` argument specifies the node specific config file. 93 | -------------------------------------------------------------------------------- /docs/ddp/e800/configMap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: sriovdp-config 5 | namespace: kube-system 6 | data: 7 | config.json: | 8 | { 9 | "resourceList": [{ 10 | "resourceName": "e800_default", 11 | "resourcePrefix": "intel.com", 12 | "selectors": [{ 13 | "vendors": ["8086"], 14 | "devices": ["1889"], 15 | "ddpProfiles": ["ICE OS Default Package"] 16 | }] 17 | }, 18 | { 19 | "resourceName": "e800_comms", 20 | "resourcePrefix": "intel.com", 21 | "selectors": [{ 22 | "vendors": ["8086"], 23 | "devices": ["1889"], 24 | "ddpProfiles": ["ICE COMMS Package"] 25 | }] 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /docs/ddp/e800/crd-sriov-comms.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "k8s.cni.cncf.io/v1" 2 | kind: NetworkAttachmentDefinition 3 | metadata: 4 | name: sriov-net-comms 5 | annotations: 6 | k8s.v1.cni.cncf.io/resourceName: intel.com/e800_comms 7 | spec: 8 | config: '{ 9 | "cniVersion": "0.3.1", 10 | "type": "sriov", 11 | "name": "sriov-net-comms", 12 | "ipam": { 13 | "type": "host-local", 14 | "subnet": "10.56.217.0/24", 15 | "routes": [{ 16 | "dst": "0.0.0.0/0" 17 | }], 18 | "gateway": "10.56.217.1" 19 | } 20 | }' 21 | # Note: requires SRIOV CNI 22 | -------------------------------------------------------------------------------- /docs/ddp/e800/crd-sriov-default.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "k8s.cni.cncf.io/v1" 2 | kind: NetworkAttachmentDefinition 3 | metadata: 4 | name: sriov-net-default 5 | annotations: 6 | k8s.v1.cni.cncf.io/resourceName: intel.com/e800_default 7 | spec: 8 | config: '{ 9 | "cniVersion": "0.3.1", 10 | "type": "sriov", 11 | "name": "sriov-net-default", 12 | "ipam": { 13 | "type": "host-local", 14 | "subnet": "10.56.217.0/24", 15 | "routes": [{ 16 | "dst": "0.0.0.0/0" 17 | }], 18 | "gateway": "10.56.217.1" 19 | } 20 | }' 21 | # Note: requires SRIOV CNI 22 | -------------------------------------------------------------------------------- /docs/ddp/e800/pod-comms.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: testpod-comms 5 | annotations: 6 | k8s.v1.cni.cncf.io/networks: sriov-net-comms 7 | spec: 8 | containers: 9 | - name: cntr1 10 | image: centos/tools 11 | imagePullPolicy: IfNotPresent 12 | command: [ "/bin/bash", "-c", "--" ] 13 | args: [ "while true; do sleep 300000; done;" ] 14 | resources: 15 | requests: 16 | intel.com/e800_comms: 1 17 | limits: 18 | intel.com/e800_comms: 1 19 | # Note: requests / limits can be added automaticaly using 20 | # network resources injector mutating webhook. 21 | -------------------------------------------------------------------------------- /docs/ddp/e800/pod-default.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: testpod-default 5 | annotations: 6 | k8s.v1.cni.cncf.io/networks: sriov-net-default 7 | spec: 8 | containers: 9 | - name: cntr1 10 | image: centos/tools 11 | imagePullPolicy: IfNotPresent 12 | command: [ "/bin/bash", "-c", "--" ] 13 | args: [ "while true; do sleep 300000; done;" ] 14 | resources: 15 | requests: 16 | intel.com/e800_default: 1 17 | limits: 18 | intel.com/e800_default: 1 19 | # Note: requests / limits can be added automaticaly using 20 | # network resources injector mutating webhook. 21 | -------------------------------------------------------------------------------- /docs/ddp/x700/configMap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: sriovdp-config 5 | namespace: kube-system 6 | data: 7 | config.json: | 8 | { 9 | "resourceList": [{ 10 | "resourceName": "x700_gtp", 11 | "resourcePrefix": "intel.com", 12 | "selectors": [{ 13 | "vendors": ["8086"], 14 | "devices": ["154c"], 15 | "ddpProfiles": ["GTPv1-C/U IPv4/IPv6 payload"] 16 | }] 17 | }, 18 | { 19 | "resourceName": "x700_pppoe", 20 | "resourcePrefix": "intel.com", 21 | "selectors": [{ 22 | "vendors": ["8086"], 23 | "devices": ["154c"], 24 | "ddpProfiles": ["E710 PPPoE and PPPoL2TPv2"] 25 | }] 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /docs/ddp/x700/crd-sriov-gtp.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "k8s.cni.cncf.io/v1" 2 | kind: NetworkAttachmentDefinition 3 | metadata: 4 | name: sriov-net-gtp 5 | annotations: 6 | k8s.v1.cni.cncf.io/resourceName: intel.com/x700_gtp 7 | spec: 8 | config: '{ 9 | "cniVersion": "0.3.1", 10 | "type": "sriov", 11 | "name": "sriov-gtp", 12 | "ipam": { 13 | "type": "host-local", 14 | "subnet": "10.56.217.0/24", 15 | "routes": [{ 16 | "dst": "0.0.0.0/0" 17 | }], 18 | "gateway": "10.56.217.1" 19 | } 20 | }' 21 | # Note: requires SRIOV CNI 22 | -------------------------------------------------------------------------------- /docs/ddp/x700/crd-sriov-pppoe.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "k8s.cni.cncf.io/v1" 2 | kind: NetworkAttachmentDefinition 3 | metadata: 4 | name: sriov-net-pppoe 5 | annotations: 6 | k8s.v1.cni.cncf.io/resourceName: intel.com/x700_pppoe 7 | spec: 8 | config: '{ 9 | "cniVersion": "0.3.1", 10 | "type": "sriov", 11 | "name": "sriov-pppoe", 12 | "ipam": { 13 | "type": "host-local", 14 | "subnet": "10.56.217.0/24", 15 | "routes": [{ 16 | "dst": "0.0.0.0/0" 17 | }], 18 | "gateway": "10.56.217.1" 19 | } 20 | }' 21 | # Note: requires SRIOV CNI 22 | -------------------------------------------------------------------------------- /docs/ddp/x700/pod-gtp.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: testpod-gtp 5 | labels: 6 | env: test 7 | annotations: 8 | k8s.v1.cni.cncf.io/networks: sriov-net-gtp 9 | spec: 10 | containers: 11 | - name: gtpcntr1 12 | image: centos/tools 13 | imagePullPolicy: IfNotPresent 14 | command: [ "/bin/bash", "-c", "--" ] 15 | args: [ "while true; do sleep 300000; done;" ] 16 | resources: 17 | requests: 18 | intel.com/x700_gtp: 1 19 | limits: 20 | intel.com/x700_gtp: 1 21 | # Note: requests / limits can be added automaticaly using 22 | # network resources injector mutating webhook. 23 | -------------------------------------------------------------------------------- /docs/ddp/x700/pod-pppoe.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: testpod-pppoe 5 | labels: 6 | env: test 7 | annotations: 8 | k8s.v1.cni.cncf.io/networks: sriov-net-pppoe 9 | spec: 10 | containers: 11 | - name: pppoecntr1 12 | image: centos/tools 13 | imagePullPolicy: IfNotPresent 14 | command: [ "/bin/bash", "-c", "--" ] 15 | args: [ "while true; do sleep 300000; done;" ] 16 | resources: 17 | requests: 18 | intel.com/x700_pppoe: 1 19 | limits: 20 | intel.com/x700_pppoe: 1 21 | # Note: requests / limits can be added automaticaly using 22 | # network resources injector mutating webhook. 23 | -------------------------------------------------------------------------------- /docs/dpdk/README.md: -------------------------------------------------------------------------------- 1 | # Running DPDK applications in Kubernetes 2 | 3 | ## Pre-requisites 4 | 5 | ### Hugepages 6 | DPDK applications require Hugepages memory. Please refer to the [Hugepages section](http://doc.dpdk.org/guides/linux_gsg/sys_reqs.html#use-of-hugepages-in-the-linux-environment) in DPDK getting started guide on hugespages in DPDK. 7 | 8 | Kubernetes nodes can only advertise a single size pre-allocated hugepages. Which means even though one can have both 2M and 1G hugepages in a system, Kubernetes will only recognize the default hugepages as schedulable resources. Workload can request for hugepages using resource requests and limits specifying `hugepage-2Mi` or `hugepage-1Gi` resource references. 9 | 10 | > One important thing to note here is that when requesting hugepage resources, either memory or CPU resource requests need to be specified. 11 | 12 | For more information on hugepage support in Kubernetes please see [here](https://kubernetes.io/docs/tasks/manage-hugepages/scheduling-hugepages/). 13 | 14 | 15 | ### VF drivers 16 | DPDK applications require devices to be attached with supported dpdk backend driver. 17 | * For Intel® x700 series NICs `igb_uio` or `vfio-pci` is required. 18 | * For Mellanox ConnectX®-4 Lx, ConnectX®-5 Adapters `mlx5_core` or `mlx5_ib` is required. 19 | 20 | ### Privileges 21 | Certain privileges are required for dpdk application to function properly in Kubernetes Pod. The level of privileges depend on the application and the host device driver attached. 22 | 23 | For example, devices with `igb_uio` driver requires a Pod to run with full privilege. With `vfio-pci` an application can run in a non-privilege Pod with only **IPC_LOCK** capability added. 24 | 25 | 26 | # Example deployment 27 | This directory includes sample deployment yaml files showing how to deploy a dpdk application in Kubernetes with in non-privileged Pod with SR-IOV VF attached to vfio-pci driver. 28 | 29 | ## Prepare host for vfio support and hugepages memory 30 | 31 | 1. On CentOS 7, edit `/etc/default/grub` file and add the following kernel boot parameters to enable iommu and create 8GB of 2M size hugepages. 32 | 33 | ``` 34 | GRUB_CMDLINE_LINUX="crashkernel=auto nomodeset rhgb quiet iommu=pt intel_iommu=on default_hugepagesz=1G hugepagesz=1G hugepages=16 pci=realloc,assign-busses" 35 | ``` 36 | 37 | 2. Rebuild grub.cfg 38 | ``` 39 | grub2-mkconfig -o /boot/grub2/grub.cfg 40 | ``` 41 | 42 | For UEFI boot, this cmd will be grub2-mkconfig -o /boot/efi/EFI//grub.cfg. 43 | For example, on CentOS: 44 | ``` 45 | grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg 46 | ``` 47 | 48 | 3. Reboot 49 | 50 | 4. Confirm that the system started with above parameter 51 | ``` 52 | # cat /proc/cmdline 53 | BOOT_IMAGE=/boot/vmlinuz-3.10.0-957.10.1.el7.x86_64 root=UUID=5b12f430-394c-4417-9064-7ab8091ff987 ro crashkernel=auto nomodeset rhgb quiet iommu=pt intel_iommu=on default_hugepagesz=1G hugepagesz=1G hugepages=16 pci=realloc,assign-busses 54 | 55 | ``` 56 | 5. Confirm that Hugepages memory are allocated and mounted 57 | ``` 58 | # cat /proc/meminfo | grep -i hugepage 59 | HugePages_Total: 16 60 | HugePages_Free: 16 61 | HugePages_Rsvd: 0 62 | HugePages_Surp: 0 63 | Hugepagesize: 1048576 kB 64 | 65 | # mount | grep hugetlbfs 66 | hugetlbfs on /dev/hugepages type hugetlbfs (rw,relatime) 67 | 68 | ``` 69 | 70 | 6. Load vfio-pci module 71 | ``` 72 | modprobe vfio-pci 73 | ``` 74 | 75 | 7. Create SR-IOV virtual functions and bind those VFs with vfio-pci driver. You can use or `driverctl` or [`dpdk-devbind.py`](https://github.com/DPDK/dpdk/blob/master/usertools/dpdk-devbind.py) to bind/unbind drivers using devices PCI addresses. Please see [here](https://dpdk-guide.gitlab.io/dpdk-guide/setup/binding.html) more information on NIC driver bindings. 76 | 77 | # Performance 78 | It is worth mentioning that to achieve maximum performance from a dpdk application the followings are required: 79 | 80 | 1. Application process needs to be pinned to some dedicated isolated CPUs. Detailing how to achieve this is out of scope of this document. You can refer to [CPU Manager for Kubernetes](https://github.com/intel/CPU-Manager-for-Kubernetes) that provides such functionality in Kubernetes 81 | 82 | 2. All application resources(CPUs, devices and memory) are from same NUMA locality 83 | -------------------------------------------------------------------------------- /docs/dpdk/configMap-all-nics.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: sriovdp-config 5 | namespace: kube-system 6 | data: 7 | config.json: | 8 | { 9 | "resourceList": [ 10 | { 11 | "resourcePrefix": "dpdk.org", 12 | "resourceName": "nic", 13 | "selectors": [{ 14 | "vendors": ["15b3"], 15 | "devices": ["1016"], 16 | "drivers": ["mlx5_core"], 17 | "isRdma": true 18 | }, { 19 | "vendors": ["15b3"], 20 | "devices": ["1018"], 21 | "drivers": ["mlx5_core"], 22 | "isRdma": true 23 | }, { 24 | "vendors": ["8086"], 25 | "devices": ["154c"], 26 | "drivers": ["vfio-pci"] 27 | }] 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /docs/dpdk/configMap-virt.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: sriovdp-config 5 | namespace: kube-system 6 | data: 7 | config.json: | 8 | { 9 | "resourceList": [ 10 | { 11 | "resourceName": "intelnics_radio_downlink", 12 | "selectors": [{ 13 | "drivers": [ 14 | "vfio-pci" 15 | ], 16 | "pciAddresses": [ 17 | "0000:00:09.0", 18 | "0000:00:0a.0" 19 | ], 20 | }], 21 | }, 22 | { 23 | "resourceName": "intelnics_radio_uplink", 24 | "selectors": [{ 25 | "drivers": [ 26 | "vfio-pci" 27 | ], 28 | "pciAddresses": [ 29 | "0000:00:07.0", 30 | "0000:00:08.0" 31 | ], 32 | }], 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /docs/dpdk/configMap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: sriovdp-config 5 | namespace: kube-system 6 | data: 7 | config.json: | 8 | { 9 | "resourceList": [ 10 | { 11 | "resourceName": "intel_x710vfio", 12 | "selectors": [{ 13 | "vendors": ["8086"], 14 | "devices": ["154c"], 15 | "drivers": ["vfio-pci"] 16 | }] 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /docs/dpdk/crd-dpdk-net2000.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "k8s.cni.cncf.io/v1" 2 | kind: NetworkAttachmentDefinition 3 | metadata: 4 | name: sriov-dpdk-net2000 5 | annotations: 6 | k8s.v1.cni.cncf.io/resourceName: intel.com/intel_x710vfio 7 | spec: 8 | config: '{ 9 | "type": "sriov", 10 | "name": "sriov-vfio2000", 11 | "vlan": 2000 12 | }' 13 | -------------------------------------------------------------------------------- /docs/dpdk/mlnx-dpdk/configMap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: sriovdp-config 5 | namespace: kube-system 6 | data: 7 | config.json: | 8 | { 9 | "resourceList": [ 10 | { 11 | "resourcePrefix": "mellanox.com", 12 | "resourceName": "mlnx_connectx4", 13 | "selectors": { 14 | "vendors": ["15b3"], 15 | "devices": ["1016"], 16 | "drivers": ["mlx5_core"], 17 | "isRdma": true 18 | } 19 | }, 20 | { 21 | "resourcePrefix": "mellanox.com", 22 | "resourceName": "mlnx_connectx5", 23 | "selectors": { 24 | "vendors": ["15b3"], 25 | "devices": ["1018"], 26 | "drivers": ["mlx5_core"], 27 | "isRdma": true 28 | } 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /docs/dpdk/mlnx-dpdk/crd-dpdk-connectx4.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "k8s.cni.cncf.io/v1" 2 | kind: NetworkAttachmentDefinition 3 | metadata: 4 | name: sriov-dpdk-mlnx-connectx4 5 | annotations: 6 | k8s.v1.cni.cncf.io/resourceName: mellanox.com/mlnx_connectx4 7 | spec: 8 | config: '{ 9 | "cniVersion": "0.3.1", 10 | "type": "sriov", 11 | "name": "sriov-mlnx-connectx4", 12 | "ipam": { 13 | "type": "host-local", 14 | "subnet": "10.56.217.0/24", 15 | "routes": [{ 16 | "dst": "0.0.0.0/0" 17 | }], 18 | "gateway": "10.56.217.1" 19 | } 20 | }' 21 | -------------------------------------------------------------------------------- /docs/dpdk/mlnx-dpdk/crd-dpdk-connectx5.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "k8s.cni.cncf.io/v1" 2 | kind: NetworkAttachmentDefinition 3 | metadata: 4 | name: sriov-dpdk-mlnx-connectx5 5 | annotations: 6 | k8s.v1.cni.cncf.io/resourceName: mellanox.com/mlnx_connectx5 7 | spec: 8 | config: '{ 9 | "cniVersion": "0.3.1", 10 | "type": "sriov", 11 | "name": "sriov-mlnx-connectx5", 12 | "ipam": { 13 | "type": "host-local", 14 | "subnet": "10.56.217.0/24", 15 | "routes": [{ 16 | "dst": "0.0.0.0/0" 17 | }], 18 | "gateway": "10.56.217.1" 19 | } 20 | }' 21 | -------------------------------------------------------------------------------- /docs/dpdk/mlnx-dpdk/mlnx-pod_testpmd_connectx4.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: testpmd 5 | annotations: 6 | k8s.v1.cni.cncf.io/networks: sriov-dpdk-mlnx-connectx4 7 | spec: 8 | containers: 9 | - name: testpmd 10 | image: 11 | imagePullPolicy: IfNotPresent 12 | securityContext: 13 | capabilities: 14 | add: ["IPC_LOCK"] 15 | volumeMounts: 16 | - mountPath: /dev/hugepages 17 | name: hugepage 18 | resources: 19 | requests: 20 | memory: 1Gi 21 | hugepages-1Gi: 2Gi 22 | mellanox.com/mlnx_connectx4: 2 23 | limits: 24 | hugepages-1Gi: 2Gi 25 | mellanox.com/mlnx_connectx4: 2 26 | command: ["sleep", "infinity"] 27 | volumes: 28 | - name: hugepage 29 | emptyDir: 30 | medium: HugePages 31 | -------------------------------------------------------------------------------- /docs/dpdk/mlnx-dpdk/mlnx-pod_testpmd_connectx5.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: testpmd 5 | annotations: 6 | k8s.v1.cni.cncf.io/networks: sriov-dpdk-mlnx-connectx5 7 | spec: 8 | containers: 9 | - name: testpmd 10 | image: 11 | imagePullPolicy: IfNotPresent 12 | securityContext: 13 | capabilities: 14 | add: ["IPC_LOCK"] 15 | volumeMounts: 16 | - mountPath: /dev/hugepages 17 | name: hugepage 18 | resources: 19 | requests: 20 | memory: 1Gi 21 | hugepages-1Gi: 2Gi 22 | mellanox.com/mlnx_connectx5: 2 23 | limits: 24 | hugepages-1Gi: 2Gi 25 | mellanox.com/mlnx_connectx5: 2 26 | command: ["sleep", "infinity"] 27 | volumes: 28 | - name: hugepage 29 | emptyDir: 30 | medium: HugePages 31 | -------------------------------------------------------------------------------- /docs/dpdk/pod_testpmd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: testpmd 5 | annotations: 6 | k8s.v1.cni.cncf.io/networks: sriov-dpdk-net2000 7 | spec: 8 | containers: 9 | - name: testpmd 10 | image: 11 | imagePullPolicy: Never 12 | securityContext: 13 | capabilities: 14 | add: ["IPC_LOCK"] 15 | volumeMounts: 16 | - mountPath: /dev/hugepages 17 | name: hugepage 18 | resources: 19 | requests: 20 | memory: 1Gi 21 | hugepages-1Gi: 4Gi 22 | intel.com/intel_x710vfio: 2 23 | limits: 24 | hugepages-1Gi: 4Gi 25 | intel.com/intel_x710vfio: 2 26 | command: ["sleep", "infinity"] 27 | volumes: 28 | - name: hugepage 29 | emptyDir: 30 | medium: HugePages 31 | -------------------------------------------------------------------------------- /docs/dpdk/pod_testpmd_virt.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: testpmd 5 | spec: 6 | containers: 7 | - name: testpmd 8 | image: 9 | securityContext: 10 | # This application is DPDK-based 11 | privileged: true 12 | resources: 13 | requests: 14 | openshift.io/intelnics_radio_downlink: "1" 15 | openshift.io/intelnics_radio_uplink: "1" 16 | memory: 1000Mi 17 | hugepages-1Gi: 2Gi 18 | cpu: '1' 19 | limits: 20 | openshift.io/intelnics_radio_downlink: "1" 21 | openshift.io/intelnics_radio_uplink: "1" 22 | hugepages-1Gi: 2Gi 23 | cpu: '1' 24 | memory: 2000Mi 25 | volumeMounts: 26 | - mountPath: /dev/hugepages 27 | name: hugepage 28 | readOnly: False 29 | volumes: 30 | - name: hugepage 31 | emptyDir: 32 | medium: HugePages 33 | -------------------------------------------------------------------------------- /docs/rdma/README.md: -------------------------------------------------------------------------------- 1 | # Running RDMA applications in Kubernetes 2 | 3 | RDMA supports zero-copy networking by enabling the network adapter to transfer data from the wire directly to application memory or from application memory directly to the wire, eliminating the need to copy data between application memory and the data buffers in the operating system. Such transfers require no work to be done by CPUs, caches, or context switches, and transfers continue in parallel with other system operations. This reduces latency in message transfer. 4 | 5 | ## Supported NICs: 6 | * Mellanox ConnectX®-4 Lx Adapter 7 | * Mellanox ConnectX®-5 Adapter 8 | * Intel E810-C Adapter 9 | 10 | ## RDMA Capable Hardware: 11 | * Mellanox ConnectX®-4 Lx Adapter 12 | * Mellanox ConnectX®-5 Adapter 13 | * Intel E810-C Adapter 14 | 15 | ## RDMA modules: 16 | * Mellanox ConnectX®-4 Lx, ConnectX®-5 Adapters mlx5_core or mlx5_ib 17 | * Intel E810-C Adapter ice and iavf 18 | 19 | ## Privileges 20 | IPC_LOCK capability privilege is required for RMA application to function properly in Kubernetes Pod. 21 | 22 | ## Rdma Mounts: 23 | Using Rdma requires mounting special files from `/dev/infiniband` in the container: 24 | ``` 25 | # ls /dev/infiniband 26 | issm2 rdma_cm ucm2 umad1 uverbs2 27 | ``` 28 | __Note__: rdma character devices mounted under `/dev/infiniband` may vary depending on the vendor and loaded kernel modules. 29 | -------------------------------------------------------------------------------- /docs/rdma/configMap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: sriovdp-config 5 | namespace: kube-system 6 | data: 7 | config.json: | 8 | { 9 | "resourceList": [ 10 | { 11 | "resourceName": "mlnx_rdma", 12 | "resourcePrefix": "mellanox.com", 13 | "selectors": [{ 14 | "vendors": ["15b3"], 15 | "devices": ["1016", "1018"], 16 | "drivers": ["mlx5_core"], 17 | "isRdma": true 18 | }] 19 | }, 20 | { 21 | "resourceName": "intel_rdma", 22 | "selectors": { 23 | "vendors": ["8086"], 24 | "devices": ["1889"], 25 | "drivers": ["iavf"], 26 | "isRdma": true 27 | } 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /docs/rdma/crd-rdma-intel.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "k8s.cni.cncf.io/v1" 2 | kind: NetworkAttachmentDefinition 3 | metadata: 4 | name: sriov-rdma-intel 5 | annotations: 6 | k8s.v1.cni.cncf.io/resourceName: intel.com/intel_rdma 7 | spec: 8 | config: '{ 9 | "type": "sriov", 10 | "name": "sriov-rdma" 11 | }' 12 | -------------------------------------------------------------------------------- /docs/rdma/crd-rdma-mlnx.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "k8s.cni.cncf.io/v1" 2 | kind: NetworkAttachmentDefinition 3 | metadata: 4 | name: sriov-rdma-mlnx 5 | annotations: 6 | k8s.v1.cni.cncf.io/resourceName: mellanox.com/mlnx_rdma 7 | spec: 8 | config: '{ 9 | "type": "sriov", 10 | "name": "sriov-rdma" 11 | }' 12 | -------------------------------------------------------------------------------- /docs/rdma/pod_intel_rdma_test.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: rdma-app 5 | annotations: 6 | k8s.v1.cni.cncf.io/networks: sriov-rdma-intel 7 | spec: 8 | containers: 9 | - name: test-rdma 10 | image: 11 | imagePullPolicy: Never 12 | securityContext: 13 | capabilities: 14 | add: ["IPC_LOCK"] 15 | resources: 16 | requests: 17 | intel.com/intel_rdma: 1 18 | limits: 19 | intel.com/intel_rdma: 1 20 | command: ["sleep", "infinity"] 21 | 22 | -------------------------------------------------------------------------------- /docs/rdma/pod_mlx_rdma_test.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: rdma-app 5 | annotations: 6 | k8s.v1.cni.cncf.io/networks: sriov-rdma-mlnx 7 | spec: 8 | containers: 9 | - name: testpmd 10 | image: 11 | imagePullPolicy: Never 12 | securityContext: 13 | capabilities: 14 | add: ["IPC_LOCK"] 15 | resources: 16 | requests: 17 | mellanox.com/mlnx_rdma: 2 18 | limits: 19 | mellanox.com/mlnx_rdma: 2 20 | command: ["sleep", "infinity"] 21 | -------------------------------------------------------------------------------- /docs/subfunctions/README.md: -------------------------------------------------------------------------------- 1 | # Subfunctions 2 | 3 | [Subfunction](https://docs.kernel.org/networking/devlink/devlink-port.html#subfunction) (SF) is a lightweight function that has a parent PCI function on which it is deployed. Subfunction is created and deployed in unit of 1. Unlike SRIOV VFs, a subfunction doesn’t require its own PCI virtual function. A subfunction communicates with the hardware through the parent PCI function. 4 | 5 | ## Usage 6 | 7 | To use a subfunction, 3 steps setup sequence is followed: 8 | 9 | 1. create - create a subfunction; 10 | 2. configure - configure subfunction attributes; 11 | 3. deploy - deploy the subfunction; 12 | 13 | Subfunction management is done using devlink port user interface. User performs setup on the subfunction management device. 14 | 15 | ### 1. Create 16 | A subfunction is created using a devlink port interface. A user adds the subfunction by adding a devlink port of subfunction flavour. The devlink kernel code calls down to subfunction management driver (devlink ops) and asks it to create a subfunction devlink port. Driver then instantiates the subfunction port and any associated objects such as health reporters and representor netdevice. 17 | 18 | ### 2. Configure 19 | A subfunction devlink port is created but it is not active yet. That means the entities are created on devlink side, the e-switch port representor is created, but the subfunction device itself is not created. A user might use e-switch port representor to do settings, putting it into bridge, adding TC rules, etc. A user might as well configure the hardware address (such as MAC address) of the subfunction while subfunction is inactive. 20 | 21 | ### 3. Deploy 22 | Once a subfunction is configured, user must activate it to use it. Upon activation, subfunction management driver asks the subfunction management device to instantiate the subfunction device on particular PCI function. A subfunction device is created on [Auxiliary Bus](https://www.kernel.org/doc/html/latest/driver-api/auxiliary_bus.html). At this point a matching subfunction driver binds to the subfunction’s auxiliary device. 23 | 24 | 25 | ## Nvidia-Mellanox Scalable Functions 26 | 27 | One of the implementation of Subfunctions is [Mellanox/ScalableFunctions](https://github.com/Mellanox/scalablefunctions/wiki). 28 | Mellanox Scalable Function has its own function capabilities and its own resources. This means a Scalable Function has its own dedicated queues(txq, rxq, cq, eq). These queues are neither shared nor stolen from the parent PCI function. There is no special support needed from system BIOS to use Mellanox Scalable Functions. Scalable Functions do not require enabling PCI SR-IOV and co-exist with PCI SR-IOV Virtual Functions. 29 | 30 | To utilize Scalable Functions as a resource following plugin configuration can be used: 31 | 32 | For BlueField-2® NIC: 33 | 34 | ```json 35 | { 36 | "resourceList": [ 37 | { 38 | "resourceName": "bf2_sf", 39 | "resourcePrefix": "nvidia.com", 40 | "deviceType": "auxNetDevice", 41 | "selectors": [{ 42 | "vendors": ["15b3"], 43 | "devices": ["a2d6"], 44 | "pfNames": ["p0#1-5"], 45 | "auxTypes": ["sf"] 46 | }] 47 | } 48 | ] 49 | } 50 | ``` 51 | 52 | For ConnectX-6® Dx NIC for SFs and VFs: 53 | 54 | ```json 55 | { 56 | "resourceList": [ 57 | { 58 | "resourceName": "cx6dx_vf", 59 | "resourcePrefix": "nvidia.com", 60 | "selectors": [{ 61 | "vendors": ["15b3"], 62 | "devices": ["101e"], 63 | }] 64 | }, 65 | { 66 | "resourceName": "cx6dx_sf", 67 | "resourcePrefix": "nvidia.com", 68 | "deviceType": "auxNetDevice", 69 | "selectors": [{ 70 | "vendors": ["15b3"], 71 | "devices": ["101e"], 72 | "auxTypes": ["sf"] 73 | }] 74 | } 75 | ] 76 | } 77 | ``` 78 | 79 | On how to configure and use Scalable Functions please refer to [https://github.com/Mellanox/scalablefunctions/wiki](https://github.com/Mellanox/scalablefunctions/wiki) 80 | -------------------------------------------------------------------------------- /docs/udev/README.md: -------------------------------------------------------------------------------- 1 | # Using UDEV Rules for changing NIC's network device name 2 | 3 | To allow "pfNames" selector to consistently select a specific device across nodes it is required to define a fixed network device name. UDEV rules allow the user to change the NIC's network device name every time the node get rebooted: 4 | 5 | 1. Add a new rule: 6 | ``` 7 | # vi /etc/udev/rules.d/90-netnames.rules 8 | SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", KERNELS=="0000:02:00.0", NAME="pf0s0f1" 9 | ``` 10 | 11 | `SUBSYSTEM` Match against the subsystem of the device 12 | `ACTION` Action to be done at the next reboot 13 | `DRIVER` Match against the name of the driver backing the device 14 | `KERNELS` Match against the kernel name for the device, here the pci address 15 | `NAME` The new name for the NIC 16 | 17 | 2. Run udev commands 18 | ``` 19 | # udevadm control --reload 20 | # udevadm trigger --action=add -attr-match=subsystem=net 21 | ``` 22 | 23 | 3. Reboot the machine or reload the driver for the NIC 24 | -------------------------------------------------------------------------------- /docs/vdpa/README.md: -------------------------------------------------------------------------------- 1 | # Using vDPA devices in Kubernetes 2 | ## Introduction to vDPA 3 | vDPA (Virtio DataPath Acceleration) is a technology that enables the acceleration 4 | of virtIO devices while allowing the implementations of such devices 5 | (e.g: NIC vendors) to use their own control plane. 6 | 7 | The consumers of the virtIO devices (VMs or containers) interact with the devices 8 | using the standard virtIO datapath and virtio-compatible control paths (virtIO, vhost). 9 | While the data-plane is mapped directly to the accelerator device, the control-plane 10 | gets translated by the vDPA kernel framework. 11 | 12 | The vDPA kernel framework is composed of the vDPA bus (/sys/bus/vdpa), vDPA devices 13 | (/sys/bus/vdpa/devices) and vDPA drivers (/sys/bus/vdpa/drivers). 14 | Currently, two vDPA drivers are implemented: 15 | * virtio_vdpa: Exposes the device as a virtio-net netdev 16 | * vhost_vdpa: Exposes the device as a vhost-vdpa device. This device uses an extension 17 | of the vhost-net protocol to allow userspace applications access the rings directly 18 | 19 | For more information about the vDPA framework, read the article on 20 | [LWN.net](https://lwn.net/Articles/816063/) or the blog series written by one of the 21 | main authors ([part 1](https://www.redhat.com/en/blog/vdpa-kernel-framework-part-1-vdpa-bus-abstracting-hardware), 22 | [part 2](https://www.redhat.com/en/blog/vdpa-kernel-framework-part-2-vdpa-bus-drivers-kernel-subsystem-interactions), 23 | [part3](https://www.redhat.com/en/blog/vdpa-kernel-framework-part-3-usage-vms-and-containers)) 24 | 25 | ## vDPA Management 26 | Currently, the management of vDPA devices is performed using the sysfs interface exposed 27 | by the vDPA Framework. However, in order to decouple the management of vDPA devices from 28 | the SR-IOV Device Plugin functionality, this low-level management is done in an external 29 | library called [go-vdpa](https://github.com/k8snetworkplumbingwg/govdpa). 30 | 31 | In the context of the SR-IOV Device Plugin and the SR-IOV CNI, the current plan is to 32 | support only 1:1 mappings between SR-IOV VFs and vDPA devices despite the fact that 33 | the vDPA Framework might support 1:N mappings. 34 | 35 | Note that vDPA and RDMA are mutually exclusive modes. 36 | 37 | ## Tested NICs: 38 | * Mellanox ConnectX®-6 DX 39 | 40 | ## Prerequisites 41 | * Linux Kernel >= 5.12 42 | * iproute >= 5.14 43 | 44 | ## vDPA device creation 45 | Insert the vDPA kernel modules if not present: 46 | 47 | $ modprobe vdpa 48 | $ modprobe virtio-vdpa 49 | $ modprobe vhost-vdpa 50 | 51 | Create a vDPA device using the `vdpa` management tool integrated into iproute2, e.g: 52 | 53 | $ vdpa mgmtdev show 54 | pci/0000:65:00.2: 55 | supported_classes net 56 | $ vdpa dev add name vdpa2 mgmtdev pci/0000:65:00.2 57 | $ vdpa dev list 58 | vdpa2: type network mgmtdev pci/0000:65:00.2 vendor_id 5555 max_vqs 16 max_vq_size 256 59 | 60 | ## Bind the desired vDPA driver 61 | The vDPA bus works similar to the pci bus. To unbind a driver from a device, run: 62 | 63 | echo ${DEV_NAME} > /sys/bus/vdpa/devices/${DEV_NAME}/driver/unbind 64 | 65 | To bind a driver to a device, run: 66 | 67 | echo ${DEV_NAME} > /sys/bus/vdpa/drivers/${DRIVER_NAME}/bind 68 | 69 | ## Configure the SR-IOV Device Plugin 70 | See the sample [configMap](configMap.yaml) for an example of how to configure a vDPA device. 71 | 72 | -------------------------------------------------------------------------------- /docs/vdpa/configMap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: sriovdp-config 5 | namespace: kube-system 6 | data: 7 | config.json: | 8 | { 9 | "resourceList": [{ 10 | { 11 | "resourceName": "vdpa_mlx_virtio", 12 | "selectors": [{ 13 | "vendors": ["15b3"], 14 | "devices": ["101e"], 15 | "drivers": ["mlx5_core"], 16 | "vdpaType": "virtio" 17 | }] 18 | }, 19 | { 20 | "resourceName": "vdpa_mlx_vhost", 21 | "selectors": [{ 22 | "vendors": ["15b3"], 23 | "devices": ["101e"], 24 | "drivers": ["mlx5_core"], 25 | "vdpaType": "vhost" 26 | }] 27 | } 28 | ] 29 | } 30 | 31 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/k8snetworkplumbingwg/sriov-network-device-plugin 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | github.com/Mellanox/rdmamap v1.1.0 7 | github.com/container-orchestrated-devices/container-device-interface v0.5.4 8 | github.com/golang/glog v1.2.5 9 | github.com/jaypipes/ghw v0.19.1 10 | github.com/jaypipes/pcidb v1.1.1 11 | github.com/k8snetworkplumbingwg/govdpa v0.1.4 12 | github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.7.7 13 | github.com/k8snetworkplumbingwg/sriovnet v1.2.1-0.20240128120937-3ca5e43034e6 14 | github.com/onsi/ginkgo v1.16.5 15 | github.com/onsi/gomega v1.38.2 16 | github.com/pkg/errors v0.9.1 17 | github.com/stretchr/testify v1.11.1 18 | github.com/vishvananda/netlink v1.3.1 19 | google.golang.org/grpc v1.75.1 20 | k8s.io/kubelet v0.32.0 21 | ) 22 | 23 | require ( 24 | github.com/Masterminds/semver/v3 v3.4.0 // indirect 25 | github.com/containernetworking/cni v1.2.0-rc1 // indirect 26 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 27 | github.com/emicklei/go-restful/v3 v3.11.0 // indirect 28 | github.com/fsnotify/fsnotify v1.5.1 // indirect 29 | github.com/fxamacker/cbor/v2 v2.7.0 // indirect 30 | github.com/go-logr/logr v1.4.3 // indirect 31 | github.com/go-ole/go-ole v1.3.0 // indirect 32 | github.com/go-openapi/jsonpointer v0.21.0 // indirect 33 | github.com/go-openapi/jsonreference v0.20.2 // indirect 34 | github.com/go-openapi/swag v0.23.0 // indirect 35 | github.com/gogo/protobuf v1.3.2 // indirect 36 | github.com/golang/protobuf v1.5.4 // indirect 37 | github.com/google/gnostic-models v0.6.9 // indirect 38 | github.com/google/go-cmp v0.7.0 // indirect 39 | github.com/google/gofuzz v1.2.0 // indirect 40 | github.com/google/uuid v1.6.0 // indirect 41 | github.com/josharian/intern v1.0.0 // indirect 42 | github.com/json-iterator/go v1.1.12 // indirect 43 | github.com/mailru/easyjson v0.7.7 // indirect 44 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 45 | github.com/modern-go/reflect2 v1.0.2 // indirect 46 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 47 | github.com/nxadm/tail v1.4.8 // indirect 48 | github.com/opencontainers/runc v1.2.8 // indirect 49 | github.com/opencontainers/runtime-spec v1.2.0 // indirect 50 | github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 // indirect 51 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 52 | github.com/spf13/afero v1.9.5 // indirect 53 | github.com/stretchr/objx v0.5.2 // indirect 54 | github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect 55 | github.com/vishvananda/netns v0.0.5 // indirect 56 | github.com/x448/float16 v0.8.4 // indirect 57 | github.com/yusufpapurcu/wmi v1.2.4 // indirect 58 | go.yaml.in/yaml/v3 v3.0.4 // indirect 59 | golang.org/x/mod v0.26.0 // indirect 60 | golang.org/x/net v0.43.0 // indirect 61 | golang.org/x/oauth2 v0.30.0 // indirect 62 | golang.org/x/sys v0.35.0 // indirect 63 | golang.org/x/term v0.34.0 // indirect 64 | golang.org/x/text v0.28.0 // indirect 65 | golang.org/x/time v0.7.0 // indirect 66 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect 67 | google.golang.org/protobuf v1.36.7 // indirect 68 | gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect 69 | gopkg.in/inf.v0 v0.9.1 // indirect 70 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect 71 | gopkg.in/yaml.v3 v3.0.1 // indirect 72 | howett.net/plist v1.0.2-0.20250314012144-ee69052608d9 // indirect 73 | k8s.io/api v0.32.0 // indirect 74 | k8s.io/apimachinery v0.32.0 // indirect 75 | k8s.io/client-go v0.32.0 // indirect 76 | k8s.io/klog/v2 v2.130.1 // indirect 77 | k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect 78 | k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect 79 | sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect 80 | sigs.k8s.io/randfill v1.0.0 // indirect 81 | sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect 82 | sigs.k8s.io/yaml v1.4.0 // indirect 83 | ) 84 | -------------------------------------------------------------------------------- /images/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.23-alpine as builder 2 | 3 | COPY . /usr/src/sriov-network-device-plugin 4 | 5 | ENV HTTP_PROXY $http_proxy 6 | ENV HTTPS_PROXY $https_proxy 7 | RUN apk add --no-cache --virtual build-dependencies build-base 8 | 9 | WORKDIR /usr/src/sriov-network-device-plugin 10 | RUN make clean && \ 11 | make build 12 | 13 | FROM golang:1.20-alpine3.16 as ddp-builder 14 | 15 | ADD images/ddptool-1.0.1.12.tar.gz /tmp/ddptool/ 16 | 17 | ENV HTTP_PROXY $http_proxy 18 | ENV HTTPS_PROXY $https_proxy 19 | RUN apk add --no-cache --virtual build-dependencies build-base linux-headers 20 | 21 | WORKDIR /tmp/ddptool 22 | RUN make 23 | 24 | FROM alpine:3 25 | RUN apk add --no-cache hwdata-pci 26 | COPY --from=builder /usr/src/sriov-network-device-plugin/build/sriovdp /usr/bin/ 27 | COPY --from=ddp-builder /tmp/ddptool/ddptool /usr/bin/ 28 | WORKDIR / 29 | 30 | LABEL io.k8s.display-name="SRIOV Network Device Plugin" 31 | 32 | COPY ./images/entrypoint.sh / 33 | 34 | RUN rm -rf /var/cache/apk/* 35 | 36 | ENTRYPOINT ["/entrypoint.sh"] 37 | -------------------------------------------------------------------------------- /images/Dockerfile.arm64: -------------------------------------------------------------------------------- 1 | FROM arm64v8/golang:alpine as builder 2 | 3 | ADD . /usr/src/sriov-network-device-plugin 4 | 5 | ENV HTTP_PROXY $http_proxy 6 | ENV HTTPS_PROXY $https_proxy 7 | RUN apk add --update --virtual build-dependencies build-base linux-headers make && \ 8 | cd /usr/src/sriov-network-device-plugin && \ 9 | make clean && \ 10 | make build 11 | 12 | FROM arm64v8/alpine 13 | RUN apk add hwdata-pci 14 | COPY --from=builder /usr/src/sriov-network-device-plugin/build/sriovdp /usr/bin/ 15 | WORKDIR / 16 | 17 | LABEL io.k8s.display-name="SRIOV Network Device Plugin PPC64LE" 18 | 19 | ADD ./images/entrypoint.sh / 20 | 21 | ENTRYPOINT ["/entrypoint.sh"] 22 | -------------------------------------------------------------------------------- /images/Dockerfile.ppc64le: -------------------------------------------------------------------------------- 1 | FROM ppc64le/golang:alpine as builder 2 | 3 | ADD . /usr/src/sriov-network-device-plugin 4 | 5 | ENV HTTP_PROXY $http_proxy 6 | ENV HTTPS_PROXY $https_proxy 7 | RUN apk add --update --virtual build-dependencies build-base linux-headers make && \ 8 | cd /usr/src/sriov-network-device-plugin && \ 9 | make clean && \ 10 | make build 11 | 12 | FROM ppc64le/alpine 13 | RUN apk add hwdata-pci 14 | COPY --from=builder /usr/src/sriov-network-device-plugin/build/sriovdp /usr/bin/ 15 | WORKDIR / 16 | 17 | LABEL io.k8s.display-name="SRIOV Network Device Plugin PPC64LE" 18 | 19 | ADD ./images/entrypoint.sh / 20 | 21 | ENTRYPOINT ["/entrypoint.sh"] 22 | -------------------------------------------------------------------------------- /images/README.md: -------------------------------------------------------------------------------- 1 | ## Dockerfile build 2 | 3 | This is used for distribution of SR-IOV Device Plugin binary in a Docker image. 4 | 5 | Typically you'd build this from the root of your SR-IOV network device plugin clone, and you'd set the `-f` flag to specify the Dockerfile during build time. This allows the addition of the entirety of the SR-IOV network device plugin git clone as part of the Docker context. Use the `-f` flag with the root of the clone as the context (e.g. your current work directory would be root of git clone), such as: 6 | 7 | ``` 8 | $ docker build -t ghcr.io/k8snetworkplumbingwg/sriov-network-device-plugin -f ./images/Dockerfile . 9 | ``` 10 | You can run `make image` to build the docker image as well. 11 | 12 | --- 13 | 14 | ## Daemonset deployment 15 | 16 | You may wish to deploy SR-IOV device plugin as a daemonset, you can do so by starting with the example Daemonset shown here: 17 | 18 | ``` 19 | $ kubectl create -f ./deployments/k8s-v1.16/sriovdp-daemonset.yaml 20 | ``` 21 | > For K8s version v1.15 or older use `deployments/k8s-v1.10-v1.15/sriovdp-daemonset.yaml` instead. 22 | 23 | Note: The likely best practice here is to build your own image given the Dockerfile, and then push it to your preferred registry, and change the `image` fields in the Daemonset YAML to reference that image. 24 | 25 | --- 26 | 27 | ### Development notes 28 | 29 | Example docker run command: 30 | 31 | ``` 32 | $ docker run -it -v /var/lib/kubelet/device-plugins:/var/lib/kubelet/device-plugins -v /var/lib/kubelet/plugins_registry:/var/lib/kubelet/plugins_registry -v /sys/class/net:/sys/class/net --entrypoint=/bin/bash ghcr.io/k8snetworkplumbingwg/sriov-network-device-plugin 33 | ``` 34 | 35 | Originally inspired by and is a portmanteau of the [Flannel daemonset](https://github.com/coreos/flannel/blob/master/Documentation/kube-flannel.yml), the [Calico Daemonset](https://github.com/projectcalico/calico/blob/master/v2.0/getting-started/kubernetes/installation/hosted/k8s-backend-addon-manager/calico-daemonset.yaml), and the [Calico CNI install bash script](https://github.com/projectcalico/cni-plugin/blob/be4df4db2e47aa7378b1bdf6933724bac1f348d0/k8s-install/scripts/install-cni.sh#L104-L153). 36 | -------------------------------------------------------------------------------- /images/ddptool-1.0.1.12.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/k8snetworkplumbingwg/sriov-network-device-plugin/0c05e4d7744462d7d56c14a39e3f936cd5e8b377/images/ddptool-1.0.1.12.tar.gz -------------------------------------------------------------------------------- /images/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | SRIOV_DP_SYS_BINARY_DIR="/usr/bin/" 6 | LOG_DIR="" 7 | LOG_LEVEL=10 8 | RESOURCE_PREFIX="" 9 | CONFIG_FILE="" 10 | CLI_PARAMS="" 11 | USE_CDI=false 12 | 13 | usage() 14 | { 15 | /bin/echo -e "This is an entrypoint script for SR-IOV Network Device Plugin" 16 | /bin/echo -e "" 17 | /bin/echo -e "./entrypoint.sh" 18 | /bin/echo -e "\t-h --help" 19 | /bin/echo -e "\t--log-dir=$LOG_DIR" 20 | /bin/echo -e "\t--log-level=$LOG_LEVEL" 21 | /bin/echo -e "\t--resource-prefix=$RESOURCE_PREFIX" 22 | /bin/echo -e "\t--config-file=$CONFIG_FILE" 23 | /bin/echo -e "\t--use-cdi" 24 | } 25 | 26 | while [ "$1" != "" ]; do 27 | PARAM="$(echo "$1" | awk -F= '{print $1}')" 28 | VALUE="$(echo "$1" | awk -F= '{print $2}')" 29 | case $PARAM in 30 | -h | --help) 31 | usage 32 | exit 33 | ;; 34 | --log-dir) 35 | LOG_DIR=$VALUE 36 | ;; 37 | --log-level) 38 | LOG_LEVEL=$VALUE 39 | ;; 40 | --resource-prefix) 41 | RESOURCE_PREFIX=$VALUE 42 | ;; 43 | --config-file) 44 | CONFIG_FILE=$VALUE 45 | ;; 46 | --use-cdi) 47 | USE_CDI=true 48 | ;; 49 | *) 50 | echo "ERROR: unknown parameter \"$PARAM\"" 51 | usage 52 | exit 1 53 | ;; 54 | esac 55 | shift 56 | done 57 | 58 | CLI_PARAMS="-v $LOG_LEVEL" 59 | 60 | if [ "$LOG_DIR" != "" ]; then 61 | mkdir -p "/var/log/$LOG_DIR" 62 | CLI_PARAMS="$CLI_PARAMS --log_dir /var/log/$LOG_DIR --alsologtostderr" 63 | else 64 | CLI_PARAMS="$CLI_PARAMS --logtostderr" 65 | fi 66 | 67 | if [ "$RESOURCE_PREFIX" != "" ]; then 68 | CLI_PARAMS="$CLI_PARAMS --resource-prefix $RESOURCE_PREFIX" 69 | fi 70 | 71 | if [ "$CONFIG_FILE" != "" ]; then 72 | CLI_PARAMS="$CLI_PARAMS --config-file $CONFIG_FILE" 73 | fi 74 | 75 | if [ "$USE_CDI" = true ]; then 76 | CLI_PARAMS="$CLI_PARAMS --use-cdi" 77 | fi 78 | set -f 79 | # shellcheck disable=SC2086 80 | exec $SRIOV_DP_SYS_BINARY_DIR/sriovdp $CLI_PARAMS 81 | -------------------------------------------------------------------------------- /pkg/accelerator/accelDevice.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Intel Corp. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package accelerator 16 | 17 | import ( 18 | "github.com/jaypipes/ghw" 19 | 20 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/devices" 21 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 22 | ) 23 | 24 | // accelDevice extends HostDevice and embedds GenPciDevice 25 | type accelDevice struct { 26 | types.HostDevice 27 | devices.GenPciDevice 28 | } 29 | 30 | // NewAccelDevice returns an instance of AccelDevice interface 31 | func NewAccelDevice(dev *ghw.PCIDevice, rFactory types.ResourceFactory, 32 | rc *types.ResourceConfig) (types.AccelDevice, error) { 33 | hostDev, err := devices.NewHostDeviceImpl(dev, dev.Address, rFactory, rc, nil) 34 | if err != nil { 35 | return nil, err 36 | } 37 | pciDev, err := devices.NewGenPciDevice(dev) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | return &accelDevice{ 43 | HostDevice: hostDev, 44 | GenPciDevice: *pciDev, 45 | }, nil 46 | } 47 | -------------------------------------------------------------------------------- /pkg/accelerator/accelResourcePool.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Intel Corp. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package accelerator 16 | 17 | import ( 18 | "github.com/golang/glog" 19 | pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 20 | 21 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/resources" 22 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 23 | ) 24 | 25 | const ( 26 | accelPoolType = "net-accel" 27 | ) 28 | 29 | type accelResourcePool struct { 30 | *resources.ResourcePoolImpl 31 | } 32 | 33 | var _ types.ResourcePool = &accelResourcePool{} 34 | 35 | // NewAccelResourcePool returns an instance of resourcePool 36 | func NewAccelResourcePool(rc *types.ResourceConfig, devicePool map[string]types.HostDevice) types.ResourcePool { 37 | rp := resources.NewResourcePool(rc, devicePool) 38 | return &accelResourcePool{ 39 | ResourcePoolImpl: rp, 40 | } 41 | } 42 | 43 | // Overrides GetDeviceSpecs 44 | func (rp *accelResourcePool) GetDeviceSpecs(deviceIDs []string) []*pluginapi.DeviceSpec { 45 | glog.Infof("GetDeviceSpecs(): for devices: %v", deviceIDs) 46 | devSpecs := make([]*pluginapi.DeviceSpec, 0) 47 | 48 | devicePool := rp.GetDevicePool() 49 | 50 | // Add device driver specific devices 51 | for _, id := range deviceIDs { 52 | if dev, ok := devicePool[id]; ok { 53 | newSpecs := dev.GetDeviceSpecs() 54 | for _, ds := range newSpecs { 55 | if !rp.DeviceSpecExist(devSpecs, ds) { 56 | devSpecs = append(devSpecs, ds) 57 | } 58 | } 59 | } 60 | } 61 | return devSpecs 62 | } 63 | 64 | // GetCDIKind returns device kind for CDI spec 65 | func (rp *accelResourcePool) GetCDIName() string { 66 | return accelPoolType 67 | } 68 | -------------------------------------------------------------------------------- /pkg/accelerator/accelResourcePool_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Intel Corp. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package accelerator_test 16 | 17 | import ( 18 | . "github.com/onsi/ginkgo" 19 | . "github.com/onsi/gomega" 20 | pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 21 | 22 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/accelerator" 23 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 24 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types/mocks" 25 | ) 26 | 27 | var _ = Describe("AccelResourcePool", func() { 28 | Context("getting a new instance of the pool", func() { 29 | rc := &types.ResourceConfig{ResourceName: "fake", ResourcePrefix: "fake"} 30 | pcis := map[string]types.HostDevice{} 31 | 32 | rp := accelerator.NewAccelResourcePool(rc, pcis) 33 | 34 | It("should return a valid instance of the pool", func() { 35 | Expect(rp).ToNot(BeNil()) 36 | }) 37 | }) 38 | Describe("getting DeviceSpecs", func() { 39 | Context("for accelerator devices", func() { 40 | rc := &types.ResourceConfig{ 41 | ResourceName: "fake", 42 | ResourcePrefix: "fake", 43 | } 44 | 45 | // fake1 will have 2 device specs 46 | fake1 := &mocks.AccelDevice{} 47 | fake1ds := []*pluginapi.DeviceSpec{ 48 | {ContainerPath: "/fake/path", HostPath: "/dev/fake1a"}, 49 | {ContainerPath: "/fake/path", HostPath: "/dev/fake1b"}, 50 | } 51 | fake1.On("GetDeviceSpecs").Return(fake1ds) 52 | 53 | // fake2 will have 1 device spec 54 | fake2 := &mocks.AccelDevice{} 55 | fake2ds := []*pluginapi.DeviceSpec{ 56 | {ContainerPath: "/fake/path", HostPath: "/dev/fake2"}, 57 | } 58 | fake2.On("GetDeviceSpecs").Return(fake2ds) 59 | 60 | // fake3 will have 0 device specs 61 | fake3 := &mocks.AccelDevice{} 62 | fake3ds := []*pluginapi.DeviceSpec{} 63 | fake2.On("GetDeviceSpecs").Return(fake3ds) 64 | 65 | pcis := map[string]types.HostDevice{"fake1": fake1, "fake2": fake2, "fake3": fake3} 66 | 67 | rp := accelerator.NewAccelResourcePool(rc, pcis) 68 | 69 | devIDs := []string{"fake1", "fake2"} 70 | 71 | actual := rp.GetDeviceSpecs(devIDs) 72 | 73 | It("should return valid slice of device specs", func() { 74 | Expect(actual).ToNot(BeNil()) 75 | Expect(actual).To(HaveLen(3)) // fake1 + fake2 => 3 devices 76 | Expect(actual).To(ContainElement(fake1ds[0])) 77 | Expect(actual).To(ContainElement(fake1ds[1])) 78 | Expect(actual).To(ContainElement(fake2ds[0])) 79 | }) 80 | }) 81 | }) 82 | }) 83 | -------------------------------------------------------------------------------- /pkg/accelerator/accelerator_suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Intel Corp. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package accelerator_test 16 | 17 | import ( 18 | "testing" 19 | 20 | . "github.com/onsi/ginkgo" 21 | . "github.com/onsi/gomega" 22 | ) 23 | 24 | func TestAccelerator(t *testing.T) { 25 | RegisterFailHandler(Fail) 26 | RunSpecs(t, "Accelerator Suite") 27 | } 28 | -------------------------------------------------------------------------------- /pkg/auxnetdevice/auxNetDevice.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package auxnetdevice 19 | 20 | import ( 21 | "fmt" 22 | 23 | "github.com/golang/glog" 24 | "github.com/jaypipes/ghw" 25 | 26 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/devices" 27 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/infoprovider" 28 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 29 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils" 30 | ) 31 | 32 | // auxNetDevice extends HostDevice and embedds GenNetDevice 33 | type auxNetDevice struct { 34 | types.HostDevice 35 | devices.GenNetDevice 36 | auxType string 37 | } 38 | 39 | // NewAuxNetDevice returns an instance of AciNetDevice interface 40 | func NewAuxNetDevice(dev *ghw.PCIDevice, deviceID string, rFactory types.ResourceFactory, 41 | rc *types.ResourceConfig, selectorIndex int) (types.AuxNetDevice, error) { 42 | var nf *types.AuxNetDeviceSelectors 43 | driverName, err := utils.GetDriverName(dev.Address) 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | infoProviders := rFactory.GetDefaultInfoProvider(deviceID, driverName) 49 | if rc.AdditionalInfo != nil { 50 | infoProviders = append(infoProviders, infoprovider.NewExtraInfoProvider(dev.Address, rc.AdditionalInfo)) 51 | } 52 | 53 | isRdma := false 54 | ok := false 55 | if selectorIndex >= 0 && selectorIndex < len(rc.SelectorObjs) { 56 | nf, ok = rc.SelectorObjs[selectorIndex].(*types.AuxNetDeviceSelectors) 57 | } 58 | if ok { 59 | if nf.IsRdma { 60 | rdmaSpec := rFactory.GetRdmaSpec(types.AuxNetDeviceType, deviceID) 61 | if rdmaSpec.IsRdma() { 62 | isRdma = true 63 | infoProviders = append(infoProviders, infoprovider.NewRdmaInfoProvider(rdmaSpec)) 64 | } else { 65 | glog.Warningf("RDMA resources for %s not found. Are RDMA modules loaded?", deviceID) 66 | } 67 | } 68 | } 69 | 70 | hostDev, err := devices.NewHostDeviceImpl(dev, deviceID, rFactory, rc, infoProviders) 71 | if err != nil { 72 | return nil, err 73 | } 74 | 75 | netDev, err := devices.NewGenNetDevice(deviceID, types.AuxNetDeviceType, isRdma) 76 | if err != nil { 77 | return nil, err 78 | } 79 | 80 | auxType := utils.ParseAuxDeviceType(deviceID) 81 | if auxType == "" { 82 | return nil, fmt.Errorf("device ID %s doesn't represent auxuliary device", deviceID) 83 | } 84 | 85 | return &auxNetDevice{ 86 | HostDevice: hostDev, 87 | GenNetDevice: *netDev, 88 | auxType: auxType, 89 | }, nil 90 | } 91 | 92 | func (ad *auxNetDevice) GetAuxType() string { 93 | return ad.auxType 94 | } 95 | -------------------------------------------------------------------------------- /pkg/auxnetdevice/auxNetResourcePool.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package auxnetdevice 19 | 20 | import ( 21 | "github.com/golang/glog" 22 | pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 23 | 24 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/resources" 25 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 26 | ) 27 | 28 | const ( 29 | auxPoolType = "net-sf" 30 | ) 31 | 32 | type auxNetResourcePool struct { 33 | *resources.ResourcePoolImpl 34 | } 35 | 36 | var _ types.ResourcePool = &auxNetResourcePool{} 37 | 38 | // NewAuxNetResourcePool returns an instance of resourcePool 39 | func NewAuxNetResourcePool(rc *types.ResourceConfig, devicePool map[string]types.HostDevice) types.ResourcePool { 40 | rp := resources.NewResourcePool(rc, devicePool) 41 | return &auxNetResourcePool{ 42 | ResourcePoolImpl: rp, 43 | } 44 | } 45 | 46 | // Overrides GetDeviceSpecs 47 | func (ap *auxNetResourcePool) GetDeviceSpecs(deviceIDs []string) []*pluginapi.DeviceSpec { 48 | glog.Infof("GetDeviceSpecs(): for devices: %v", deviceIDs) 49 | devSpecs := make([]*pluginapi.DeviceSpec, 0) 50 | 51 | devicePool := ap.GetDevicePool() 52 | 53 | // Add device driver specific and rdma specific devices 54 | for _, id := range deviceIDs { 55 | if dev, ok := devicePool[id]; ok { 56 | auxDev := dev.(types.AuxNetDevice) // convert generic HostDevice to AuxNetDevice 57 | newSpecs := auxDev.GetDeviceSpecs() 58 | for _, ds := range newSpecs { 59 | if !ap.DeviceSpecExist(devSpecs, ds) { 60 | devSpecs = append(devSpecs, ds) 61 | } 62 | } 63 | } 64 | } 65 | return devSpecs 66 | } 67 | 68 | // GetCDIKind returns device kind for CDI spec 69 | func (ap *auxNetResourcePool) GetCDIName() string { 70 | return auxPoolType 71 | } 72 | -------------------------------------------------------------------------------- /pkg/auxnetdevice/auxNetResourcePool_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package auxnetdevice_test 19 | 20 | import ( 21 | . "github.com/onsi/ginkgo" 22 | . "github.com/onsi/gomega" 23 | pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 24 | 25 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/auxnetdevice" 26 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 27 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types/mocks" 28 | ) 29 | 30 | var _ = Describe("AuxNetResourcePool", func() { 31 | Context("getting a new instance of the pool", func() { 32 | rc := &types.ResourceConfig{ 33 | ResourceName: "fake", 34 | ResourcePrefix: "fake", 35 | SelectorObjs: []interface{}{&types.AuxNetDeviceSelectors{}}, 36 | } 37 | devs := map[string]types.HostDevice{} 38 | 39 | rp := auxnetdevice.NewAuxNetResourcePool(rc, devs) 40 | 41 | It("should return a valid instance of the pool", func() { 42 | Expect(rp).ToNot(BeNil()) 43 | }) 44 | }) 45 | Describe("getting DeviceSpecs", func() { 46 | Context("for multiple devices", func() { 47 | rc := &types.ResourceConfig{ 48 | ResourceName: "fake", 49 | ResourcePrefix: "fake", 50 | SelectorObjs: []interface{}{&types.AuxNetDeviceSelectors{ 51 | GenericNetDeviceSelectors: types.GenericNetDeviceSelectors{IsRdma: false}, 52 | }, 53 | }} 54 | 55 | // fake1 will have 2 device specs 56 | fake1 := &mocks.AuxNetDevice{} 57 | fake1ds := []*pluginapi.DeviceSpec{ 58 | {ContainerPath: "/fake/path", HostPath: "/dev/fake1a"}, 59 | {ContainerPath: "/fake/path", HostPath: "/dev/fake1b"}, 60 | } 61 | fake1.On("GetDeviceSpecs").Return(fake1ds) 62 | 63 | // fake2 will have 1 device spec 64 | fake2 := &mocks.AuxNetDevice{} 65 | fake2ds := []*pluginapi.DeviceSpec{ 66 | {ContainerPath: "/fake/path", HostPath: "/dev/fake2"}, 67 | } 68 | fake2.On("GetDeviceSpecs").Return(fake2ds) 69 | 70 | // fake3 will have 0 device specs 71 | fake3 := &mocks.AuxNetDevice{} 72 | fake3ds := []*pluginapi.DeviceSpec{} 73 | fake3.On("GetDeviceSpecs").Return(fake3ds) 74 | 75 | devs := map[string]types.HostDevice{"fake1": fake1, "fake2": fake2, "fake3": fake3} 76 | 77 | rp := auxnetdevice.NewAuxNetResourcePool(rc, devs) 78 | 79 | devIDs := []string{"fake1", "fake2"} 80 | 81 | actual := rp.GetDeviceSpecs(devIDs) 82 | 83 | It("should return valid slice of device specs", func() { 84 | Expect(actual).ToNot(BeNil()) 85 | Expect(actual).To(HaveLen(3)) // fake1 + fake2 => 3 devices 86 | Expect(actual).To(ContainElement(fake1ds[0])) 87 | Expect(actual).To(ContainElement(fake1ds[1])) 88 | Expect(actual).To(ContainElement(fake2ds[0])) 89 | }) 90 | }) 91 | }) 92 | }) 93 | -------------------------------------------------------------------------------- /pkg/cdi/cdi_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package cdi_test 19 | 20 | import ( 21 | "testing" 22 | 23 | . "github.com/onsi/ginkgo" 24 | . "github.com/onsi/gomega" 25 | 26 | cdiPkg "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/cdi" 27 | ) 28 | 29 | func TestCdi(t *testing.T) { 30 | RegisterFailHandler(Fail) 31 | RunSpecs(t, "CDI Suite") 32 | } 33 | 34 | var _ = Describe("Cdi", func() { 35 | Context("successfully create container annotation", func() { 36 | It("should return container annotation", func() { 37 | deviceId := "0000:00:00.1" 38 | cdi := cdiPkg.New() 39 | annotations, err := cdi.CreateContainerAnnotations([]string{deviceId}, "example.com", "net") 40 | Expect(err).NotTo(HaveOccurred()) 41 | Expect(annotations).To(HaveLen(1)) 42 | annoKey := "cdi.k8s.io/example.com_net" 43 | annoVal := "example.com/net=0000:00:00.1" 44 | Expect(annotations[annoKey]).To(Equal(annoVal)) 45 | }) 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /pkg/cdi/mocks/CDI.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.53.3. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | types "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 7 | mock "github.com/stretchr/testify/mock" 8 | ) 9 | 10 | // CDI is an autogenerated mock type for the CDI type 11 | type CDI struct { 12 | mock.Mock 13 | } 14 | 15 | // CleanupSpecs provides a mock function with no fields 16 | func (_m *CDI) CleanupSpecs() error { 17 | ret := _m.Called() 18 | 19 | if len(ret) == 0 { 20 | panic("no return value specified for CleanupSpecs") 21 | } 22 | 23 | var r0 error 24 | if rf, ok := ret.Get(0).(func() error); ok { 25 | r0 = rf() 26 | } else { 27 | r0 = ret.Error(0) 28 | } 29 | 30 | return r0 31 | } 32 | 33 | // CreateCDISpecForPool provides a mock function with given fields: resourcePrefix, rPool 34 | func (_m *CDI) CreateCDISpecForPool(resourcePrefix string, rPool types.ResourcePool) error { 35 | ret := _m.Called(resourcePrefix, rPool) 36 | 37 | if len(ret) == 0 { 38 | panic("no return value specified for CreateCDISpecForPool") 39 | } 40 | 41 | var r0 error 42 | if rf, ok := ret.Get(0).(func(string, types.ResourcePool) error); ok { 43 | r0 = rf(resourcePrefix, rPool) 44 | } else { 45 | r0 = ret.Error(0) 46 | } 47 | 48 | return r0 49 | } 50 | 51 | // CreateContainerAnnotations provides a mock function with given fields: devicesIDs, resourcePrefix, resourceKind 52 | func (_m *CDI) CreateContainerAnnotations(devicesIDs []string, resourcePrefix string, resourceKind string) (map[string]string, error) { 53 | ret := _m.Called(devicesIDs, resourcePrefix, resourceKind) 54 | 55 | if len(ret) == 0 { 56 | panic("no return value specified for CreateContainerAnnotations") 57 | } 58 | 59 | var r0 map[string]string 60 | var r1 error 61 | if rf, ok := ret.Get(0).(func([]string, string, string) (map[string]string, error)); ok { 62 | return rf(devicesIDs, resourcePrefix, resourceKind) 63 | } 64 | if rf, ok := ret.Get(0).(func([]string, string, string) map[string]string); ok { 65 | r0 = rf(devicesIDs, resourcePrefix, resourceKind) 66 | } else { 67 | if ret.Get(0) != nil { 68 | r0 = ret.Get(0).(map[string]string) 69 | } 70 | } 71 | 72 | if rf, ok := ret.Get(1).(func([]string, string, string) error); ok { 73 | r1 = rf(devicesIDs, resourcePrefix, resourceKind) 74 | } else { 75 | r1 = ret.Error(1) 76 | } 77 | 78 | return r0, r1 79 | } 80 | 81 | // NewCDI creates a new instance of CDI. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 82 | // The first argument is typically a *testing.T value. 83 | func NewCDI(t interface { 84 | mock.TestingT 85 | Cleanup(func()) 86 | }) *CDI { 87 | mock := &CDI{} 88 | mock.Mock.Test(t) 89 | 90 | t.Cleanup(func() { mock.AssertExpectations(t) }) 91 | 92 | return mock 93 | } 94 | -------------------------------------------------------------------------------- /pkg/devices/api.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package devices 19 | 20 | import ( 21 | pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 22 | 23 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 24 | ) 25 | 26 | // APIDeviceImpl is an implementation of APIDevice interface 27 | type APIDeviceImpl struct { 28 | device *pluginapi.Device 29 | infoProviders []types.DeviceInfoProvider 30 | } 31 | 32 | // NewAPIDeviceImpl returns an instance implementation of APIDevice interface 33 | func NewAPIDeviceImpl(id string, infoProviders []types.DeviceInfoProvider, nodeNum int) *APIDeviceImpl { 34 | var topology *pluginapi.TopologyInfo 35 | if nodeNum >= 0 { 36 | topology = &pluginapi.TopologyInfo{ 37 | Nodes: []*pluginapi.NUMANode{ 38 | {ID: int64(nodeNum)}, 39 | }, 40 | } 41 | } 42 | return &APIDeviceImpl{ 43 | device: &pluginapi.Device{ 44 | ID: id, 45 | Health: pluginapi.Healthy, 46 | Topology: topology, 47 | }, 48 | infoProviders: infoProviders, 49 | } 50 | } 51 | 52 | // GetDeviceSpecs returns list of device specs 53 | func (ad *APIDeviceImpl) GetDeviceSpecs() []*pluginapi.DeviceSpec { 54 | dSpecs := make([]*pluginapi.DeviceSpec, 0) 55 | for _, infoProvider := range ad.infoProviders { 56 | dSpecs = append(dSpecs, infoProvider.GetDeviceSpecs()...) 57 | } 58 | 59 | return dSpecs 60 | } 61 | 62 | // GetEnvVal returns device environment variables 63 | func (ad *APIDeviceImpl) GetEnvVal() map[string]types.AdditionalInfo { 64 | envValMap := make(map[string]types.AdditionalInfo, 0) 65 | for _, provider := range ad.infoProviders { 66 | envValMap[provider.GetName()] = provider.GetEnvVal() 67 | } 68 | return envValMap 69 | } 70 | 71 | // GetMounts returns list of device host mounts 72 | func (ad *APIDeviceImpl) GetMounts() []*pluginapi.Mount { 73 | mnt := make([]*pluginapi.Mount, 0) 74 | for _, infoProvider := range ad.infoProviders { 75 | mnt = append(mnt, infoProvider.GetMounts()...) 76 | } 77 | 78 | return mnt 79 | } 80 | 81 | // GetAPIDevice returns k8s API device 82 | func (ad *APIDeviceImpl) GetAPIDevice() *pluginapi.Device { 83 | return ad.device 84 | } 85 | -------------------------------------------------------------------------------- /pkg/devices/api_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package devices_test 19 | 20 | import ( 21 | . "github.com/onsi/ginkgo" 22 | . "github.com/onsi/gomega" 23 | "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 24 | 25 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/devices" 26 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 27 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types/mocks" 28 | ) 29 | 30 | var _ = Describe("ApiDevice", func() { 31 | t := GinkgoT() 32 | Context("Create new ApiDevice", func() { 33 | It("with populated fields", func() { 34 | mockInfo1 := &mocks.DeviceInfoProvider{} 35 | mockSpec1 := []*v1beta1.DeviceSpec{ 36 | {HostPath: "/mock/spec/1"}, 37 | } 38 | mockEnv := types.AdditionalInfo{"deviceID": "0000:00:00.1"} 39 | mockInfo1.On("GetName").Return("generic") 40 | mockInfo1.On("GetEnvVal").Return(mockEnv) 41 | mockInfo1.On("GetDeviceSpecs").Return(mockSpec1) 42 | mockInfo1.On("GetMounts").Return(nil) 43 | mockInfo2 := &mocks.DeviceInfoProvider{} 44 | mockSpec2 := []*v1beta1.DeviceSpec{ 45 | {HostPath: "/mock/spec/2"}, 46 | } 47 | mockInfo2.On("GetName").Return("generic") 48 | mockInfo2.On("GetEnvVal").Return(mockEnv) 49 | mockInfo2.On("GetDeviceSpecs").Return(mockSpec2) 50 | mockInfo2.On("GetMounts").Return(nil) 51 | 52 | infoProviders := []types.DeviceInfoProvider{mockInfo1, mockInfo2} 53 | dev := devices.NewAPIDeviceImpl("0000:00:00.1", infoProviders, -1) 54 | 55 | envs := dev.GetEnvVal() 56 | Expect(envs).To(HaveLen(1)) 57 | _, exist := envs["generic"] 58 | Expect(exist).To(BeTrue()) 59 | pci, exist := envs["generic"]["deviceID"] 60 | Expect(exist).To(BeTrue()) 61 | Expect(pci).To(Equal("0000:00:00.1")) 62 | 63 | Expect(dev.GetDeviceSpecs()).To(HaveLen(2)) 64 | Expect(dev.GetMounts()).To(BeEmpty()) 65 | Expect(dev.GetAPIDevice()).NotTo(BeNil()) 66 | Expect(dev.GetAPIDevice().ID).To(Equal("0000:00:00.1")) 67 | Expect(dev.GetAPIDevice().Topology).To(BeNil()) 68 | mockInfo1.AssertExpectations(t) 69 | mockInfo2.AssertExpectations(t) 70 | }) 71 | It("with populated API device topology", func() { 72 | infoProviders := []types.DeviceInfoProvider{} 73 | dev := devices.NewAPIDeviceImpl("0000:00:00.1", infoProviders, 0) 74 | 75 | Expect(dev.GetAPIDevice()).NotTo(BeNil()) 76 | Expect(dev.GetAPIDevice().Topology).NotTo(BeNil()) 77 | Expect(dev.GetAPIDevice().Topology.Nodes[0].ID).To(Equal(int64(0))) 78 | }) 79 | }) 80 | }) 81 | -------------------------------------------------------------------------------- /pkg/devices/devices_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package devices_test 19 | 20 | import ( 21 | "testing" 22 | 23 | . "github.com/onsi/ginkgo" 24 | . "github.com/onsi/gomega" 25 | ) 26 | 27 | func TestResources(t *testing.T) { 28 | RegisterFailHandler(Fail) 29 | RunSpecs(t, "Devices Suite") 30 | } 31 | -------------------------------------------------------------------------------- /pkg/devices/gen_net.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package devices 19 | 20 | import ( 21 | "fmt" 22 | 23 | "github.com/golang/glog" 24 | 25 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 26 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils" 27 | ) 28 | 29 | // GenNetDevice is a generic network device embedded into top level devices 30 | type GenNetDevice struct { 31 | pfName string 32 | pfAddr string 33 | ifName string 34 | linkType string 35 | linkSpeed string 36 | funcID int 37 | isRdma bool 38 | } 39 | 40 | // NewGenNetDevice returns GenNetDevice instance 41 | func NewGenNetDevice(deviceID string, dt types.DeviceType, isRdma bool) (*GenNetDevice, error) { 42 | var netNames []string 43 | var pfName string 44 | var pfAddr string 45 | var funcID int 46 | var err error 47 | 48 | // nolint: exhaustive 49 | switch dt { 50 | case types.NetDeviceType: 51 | if pfName, err = utils.GetPfName(deviceID); err != nil { 52 | glog.Warningf("unable to get PF name %q", err.Error()) 53 | } 54 | if pfAddr, err = utils.GetPfAddr(deviceID); err != nil { 55 | return nil, err 56 | } 57 | if funcID, err = utils.GetVFID(deviceID); err != nil { 58 | return nil, err 59 | } 60 | netNames, _ = utils.GetNetNames(deviceID) 61 | case types.AuxNetDeviceType: 62 | if pfName, err = utils.GetPfNameFromAuxDev(deviceID); err != nil { 63 | // AuxNetDeviceType by design should have PF, return error if failed to get PF name 64 | return nil, err 65 | } 66 | 67 | if pfAddr, err = utils.GetSriovnetProvider().GetPfPciFromAux(deviceID); err != nil { 68 | return nil, err 69 | } 70 | // Only SF auxiliary devices can have an index, for other (-1, err) returned. 71 | // TODO review this check in the future if other auxiliary device types are added 72 | if funcID, err = utils.GetSriovnetProvider().GetSfIndexByAuxDev(deviceID); err != nil { 73 | return nil, err 74 | } 75 | netNames, _ = utils.GetSriovnetProvider().GetNetDevicesFromAux(deviceID) 76 | default: 77 | return nil, fmt.Errorf("generic netdevices not supported for type %s", dt) 78 | } 79 | 80 | ifName := "" 81 | if len(netNames) > 0 { 82 | ifName = netNames[0] 83 | } 84 | 85 | linkType := "" 86 | linkTypeProviderDevice := ifName 87 | // If interface name is not available, derive link type from PF 88 | if linkTypeProviderDevice == "" { 89 | linkTypeProviderDevice = pfName 90 | } 91 | if linkTypeProviderDevice != "" { 92 | la, err := utils.GetNetlinkProvider().GetLinkAttrs(linkTypeProviderDevice) 93 | if err != nil { 94 | return nil, err 95 | } 96 | linkType = la.EncapType 97 | } 98 | 99 | return &GenNetDevice{ 100 | pfName: pfName, 101 | pfAddr: pfAddr, 102 | ifName: ifName, 103 | linkType: linkType, 104 | linkSpeed: "", // TODO: Get this using utils pkg 105 | funcID: funcID, 106 | isRdma: isRdma, 107 | }, nil 108 | } 109 | 110 | // GetPfNetName returns PF netdevice name 111 | func (nd *GenNetDevice) GetPfNetName() string { 112 | return nd.pfName 113 | } 114 | 115 | // GetPfPciAddr returns PF pci address 116 | func (nd *GenNetDevice) GetPfPciAddr() string { 117 | return nd.pfAddr 118 | } 119 | 120 | // GetNetName returns name of the network interface 121 | func (nd *GenNetDevice) GetNetName() string { 122 | return nd.ifName 123 | } 124 | 125 | // GetLinkSpeed returns link speed 126 | func (nd *GenNetDevice) GetLinkSpeed() string { 127 | return nd.linkSpeed 128 | } 129 | 130 | // GetLinkType returns link type 131 | func (nd *GenNetDevice) GetLinkType() string { 132 | return nd.linkType 133 | } 134 | 135 | // GetFuncID returns ID of the function 136 | func (nd *GenNetDevice) GetFuncID() int { 137 | return nd.funcID 138 | } 139 | 140 | // IsRdma returns 141 | func (nd *GenNetDevice) IsRdma() bool { 142 | return nd.isRdma 143 | } 144 | -------------------------------------------------------------------------------- /pkg/devices/gen_pci.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package devices 19 | 20 | import ( 21 | "github.com/jaypipes/ghw" 22 | 23 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils" 24 | ) 25 | 26 | // GenPciDevice implementation 27 | type GenPciDevice struct { 28 | pciAddr string 29 | acpiIndex string 30 | } 31 | 32 | // NewGenPciDevice returns GenPciDevice instance 33 | func NewGenPciDevice(dev *ghw.PCIDevice) (*GenPciDevice, error) { 34 | pciAddr := dev.Address 35 | 36 | acpiIndex, err := utils.GetAcpiIndex(dev.Address) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | return &GenPciDevice{ 42 | pciAddr: pciAddr, 43 | acpiIndex: acpiIndex, 44 | }, nil 45 | } 46 | 47 | // GetPciAddr returns pci address of the device 48 | func (pd *GenPciDevice) GetPciAddr() string { 49 | return pd.pciAddr 50 | } 51 | 52 | // GetAcpiIndex returns ACPI index of the device 53 | func (pd *GenPciDevice) GetAcpiIndex() string { 54 | return pd.acpiIndex 55 | } 56 | -------------------------------------------------------------------------------- /pkg/devices/host.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package devices 19 | 20 | import ( 21 | "github.com/jaypipes/ghw" 22 | 23 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/infoprovider" 24 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 25 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils" 26 | ) 27 | 28 | // HostDeviceImpl is an implementation of HostDevice interface 29 | type HostDeviceImpl struct { 30 | types.APIDevice 31 | vendorID string 32 | deviceCode string 33 | driver string 34 | } 35 | 36 | // NewHostDeviceImpl returns an instance implementation of HostDevice interface 37 | // A list of DeviceInfoProviders can be set externally. 38 | // If empty, the default driver-based selection provided by ResourceFactory will be used 39 | func NewHostDeviceImpl(dev *ghw.PCIDevice, deviceID string, rFactory types.ResourceFactory, 40 | rc *types.ResourceConfig, infoProviders []types.DeviceInfoProvider) (*HostDeviceImpl, error) { 41 | // Get driver info 42 | driverName, err := utils.GetDriverName(dev.Address) 43 | if err != nil { 44 | return nil, err 45 | } 46 | 47 | // Use the default Information Provided if not 48 | if len(infoProviders) == 0 { 49 | infoProviders = rFactory.GetDefaultInfoProvider(deviceID, driverName) 50 | if rc.AdditionalInfo != nil { 51 | infoProviders = append(infoProviders, infoprovider.NewExtraInfoProvider(dev.Address, rc.AdditionalInfo)) 52 | } 53 | } 54 | 55 | nodeNum := -1 56 | if !rc.ExcludeTopology { 57 | nodeNum = utils.GetDevNode(dev.Address) 58 | } 59 | 60 | apiDevice := NewAPIDeviceImpl(deviceID, infoProviders, nodeNum) 61 | 62 | return &HostDeviceImpl{ 63 | APIDevice: apiDevice, 64 | vendorID: dev.Vendor.ID, 65 | deviceCode: dev.Product.ID, 66 | driver: driverName, 67 | }, nil 68 | } 69 | 70 | // GetVendor returns vendor identifier number of the device 71 | func (hd *HostDeviceImpl) GetVendor() string { 72 | return hd.vendorID 73 | } 74 | 75 | // GetDeviceCode returns identifier number of the device 76 | func (hd *HostDeviceImpl) GetDeviceCode() string { 77 | return hd.deviceCode 78 | } 79 | 80 | // GetDeviceID returns device unique identifier 81 | func (hd *HostDeviceImpl) GetDeviceID() string { 82 | return hd.GetAPIDevice().ID 83 | } 84 | 85 | // GetDriver returns driver name of the device 86 | func (hd *HostDeviceImpl) GetDriver() string { 87 | return hd.driver 88 | } 89 | -------------------------------------------------------------------------------- /pkg/devices/rdma.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package devices 19 | 20 | import ( 21 | "github.com/golang/glog" 22 | pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 23 | 24 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 25 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils" 26 | ) 27 | 28 | type rdmaSpec struct { 29 | deviceID string 30 | deviceType types.DeviceType 31 | } 32 | 33 | // NewRdmaSpec returns the RdmaSpec 34 | func NewRdmaSpec(dt types.DeviceType, id string) types.RdmaSpec { 35 | if dt == types.AcceleratorType { 36 | return nil 37 | } 38 | return &rdmaSpec{deviceID: id, deviceType: dt} 39 | } 40 | 41 | func (r *rdmaSpec) IsRdma() bool { 42 | if len(r.getRdmaResources()) > 0 { 43 | return true 44 | } 45 | var bus string 46 | //nolint: exhaustive 47 | switch r.deviceType { 48 | case types.NetDeviceType: 49 | bus = "pci" 50 | case types.AuxNetDeviceType: 51 | bus = "auxiliary" 52 | default: 53 | return false 54 | } 55 | // In case of exclusive RDMA, if the resource is assigned to a pod 56 | // the files used to check if the device support RDMA are removed from the host. 57 | // In order to still report the resource in this state, 58 | // netlink param "enable_rdma" is checked to verify if the device supports RDMA. 59 | // This scenario cann happen if the device is discovered, assigned to a pod and then the plugin is restarted. 60 | rdma, err := utils.HasRdmaParam(bus, r.deviceID) 61 | if err != nil { 62 | glog.Infof("HasRdmaParam(): unable to get Netlink RDMA param for device %s : %q", r.deviceID, err) 63 | return false 64 | } 65 | return rdma 66 | } 67 | 68 | func (r *rdmaSpec) getRdmaResources() []string { 69 | //nolint: exhaustive 70 | switch r.deviceType { 71 | case types.NetDeviceType: 72 | return utils.GetRdmaProvider().GetRdmaDevicesForPcidev(r.deviceID) 73 | case types.AuxNetDeviceType: 74 | return utils.GetRdmaProvider().GetRdmaDevicesForAuxdev(r.deviceID) 75 | default: 76 | return make([]string, 0) 77 | } 78 | } 79 | 80 | func (r *rdmaSpec) GetRdmaDeviceSpec() []*pluginapi.DeviceSpec { 81 | rdmaResources := r.getRdmaResources() 82 | deviceSpec := make([]*pluginapi.DeviceSpec, 0) 83 | if len(rdmaResources) > 0 { 84 | for _, res := range rdmaResources { 85 | resRdmaDevices := utils.GetRdmaProvider().GetRdmaCharDevices(res) 86 | for _, rdmaDevice := range resRdmaDevices { 87 | deviceSpec = append(deviceSpec, &pluginapi.DeviceSpec{ 88 | HostPath: rdmaDevice, 89 | ContainerPath: rdmaDevice, 90 | Permissions: "rw", 91 | }) 92 | } 93 | } 94 | } 95 | return deviceSpec 96 | } 97 | 98 | // GetRdmaDeviceName returns the rdma device name 99 | func (r *rdmaSpec) GetRdmaDeviceName() string { 100 | rdmaResource := r.getRdmaResources() 101 | if len(rdmaResource) > 0 { 102 | return rdmaResource[0] 103 | } 104 | return "" 105 | } 106 | -------------------------------------------------------------------------------- /pkg/devices/vdpa.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Red Hat, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package devices 18 | 19 | import ( 20 | "fmt" 21 | 22 | "github.com/golang/glog" 23 | "github.com/k8snetworkplumbingwg/govdpa/pkg/kvdpa" 24 | 25 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 26 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils" 27 | ) 28 | 29 | type vdpaDevice struct { 30 | kvdpa.VdpaDevice 31 | } 32 | 33 | // GetType returns the VdpaType associated with the VdpaDevice 34 | func (v *vdpaDevice) GetType() types.VdpaType { 35 | currentDriver := v.VdpaDevice.Driver() 36 | for vtype, driver := range types.SupportedVdpaTypes { 37 | if driver == currentDriver { 38 | return vtype 39 | } 40 | } 41 | return types.VdpaInvalidType 42 | } 43 | 44 | func (v *vdpaDevice) GetParent() string { 45 | return v.VdpaDevice.Name() 46 | } 47 | 48 | func (v *vdpaDevice) GetPath() (string, error) { 49 | if v.VhostVdpa() != nil { 50 | return v.VhostVdpa().Path(), nil 51 | } 52 | return "", fmt.Errorf("device %s: path is not applicable for non vhost-vdpa", v.Name()) 53 | } 54 | 55 | // GetVdpaDevice returns a VdpaDevice from a given VF PCI address 56 | func GetVdpaDevice(pciAddr string) types.VdpaDevice { 57 | detailVdpaDev, err := utils.GetVdpaProvider().GetVdpaDeviceByPci(pciAddr) 58 | if err != nil { 59 | glog.Infof("%s - No vDPA device found: %s", pciAddr, err) 60 | return nil 61 | } 62 | return &vdpaDevice{ 63 | detailVdpaDev, 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /pkg/devices/vdpa_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package devices_test 19 | 20 | import ( 21 | "fmt" 22 | 23 | "github.com/k8snetworkplumbingwg/govdpa/pkg/kvdpa" 24 | . "github.com/onsi/ginkgo" 25 | . "github.com/onsi/gomega" 26 | 27 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/devices" 28 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 29 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils" 30 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils/mocks" 31 | ) 32 | 33 | type fakeKvdpaDevice struct { 34 | driver string 35 | } 36 | 37 | func (v *fakeKvdpaDevice) Driver() string { 38 | return v.driver 39 | } 40 | func (v *fakeKvdpaDevice) Name() string { 41 | return "" 42 | } 43 | func (v *fakeKvdpaDevice) MgmtDev() kvdpa.MgmtDev { 44 | return nil 45 | } 46 | func (v *fakeKvdpaDevice) VirtioNet() kvdpa.VirtioNet { 47 | return nil 48 | } 49 | func (v *fakeKvdpaDevice) VhostVdpa() kvdpa.VhostVdpa { 50 | return nil 51 | } 52 | func (v *fakeKvdpaDevice) ParentDevicePath() (string, error) { 53 | return "", nil 54 | } 55 | 56 | var _ = Describe("VdpaDevice", func() { 57 | t := GinkgoT() 58 | Context("getting new device", func() { 59 | It("no valid vdpa device for pci address", func() { 60 | fakeVdpaProvider := mocks.VdpaProvider{} 61 | fakeVdpaProvider.On("GetVdpaDeviceByPci", "0000:00:00.0").Return(nil, fmt.Errorf("ERROR")) 62 | utils.SetVdpaProviderInst(&fakeVdpaProvider) 63 | dev := devices.GetVdpaDevice("0000:00:00.0") 64 | 65 | Expect(dev).To(BeNil()) 66 | fakeVdpaProvider.AssertExpectations(t) 67 | }) 68 | It("unsupported vdpa type", func() { 69 | fakeKvdpaDev := &fakeKvdpaDevice{driver: "not supported"} 70 | fakeVdpaProvider := mocks.VdpaProvider{} 71 | fakeVdpaProvider.On("GetVdpaDeviceByPci", "0000:00:00.0").Return(fakeKvdpaDev, nil) 72 | utils.SetVdpaProviderInst(&fakeVdpaProvider) 73 | dev := devices.GetVdpaDevice("0000:00:00.0") 74 | 75 | Expect(dev).NotTo(BeNil()) 76 | Expect(dev.GetType()).To(Equal(types.VdpaInvalidType)) 77 | fakeVdpaProvider.AssertExpectations(t) 78 | }) 79 | It("supported vdpa type", func() { 80 | fakeKvdpaDev := &fakeKvdpaDevice{driver: types.SupportedVdpaTypes[types.VdpaVirtioType]} 81 | fakeVdpaProvider := mocks.VdpaProvider{} 82 | fakeVdpaProvider.On("GetVdpaDeviceByPci", "0000:00:00.0").Return(fakeKvdpaDev, nil) 83 | utils.SetVdpaProviderInst(&fakeVdpaProvider) 84 | dev := devices.GetVdpaDevice("0000:00:00.0") 85 | 86 | Expect(dev).NotTo(BeNil()) 87 | Expect(dev.GetType()).To(Equal(types.VdpaVirtioType)) 88 | fakeVdpaProvider.AssertExpectations(t) 89 | }) 90 | }) 91 | }) 92 | -------------------------------------------------------------------------------- /pkg/infoprovider/extraInfoProvider.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package infoprovider 16 | 17 | import ( 18 | pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 19 | 20 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 21 | ) 22 | 23 | /* 24 | extraInfoProvider implements DeviceInfoProvider 25 | */ 26 | type extraInfoProvider struct { 27 | pciAddr string 28 | extraInfo map[string]types.AdditionalInfo 29 | } 30 | 31 | // NewExtraInfoProvider create instance of Environment DeviceInfoProvider 32 | func NewExtraInfoProvider(pciAddr string, extraInfo map[string]types.AdditionalInfo) types.DeviceInfoProvider { 33 | return &extraInfoProvider{ 34 | pciAddr: pciAddr, 35 | extraInfo: extraInfo, 36 | } 37 | } 38 | 39 | // ***************************************************************** 40 | /* DeviceInfoProvider Interface */ 41 | 42 | func (rp *extraInfoProvider) GetName() string { 43 | return "extra" 44 | } 45 | 46 | func (rp *extraInfoProvider) GetDeviceSpecs() []*pluginapi.DeviceSpec { 47 | devSpecs := make([]*pluginapi.DeviceSpec, 0) 48 | return devSpecs 49 | } 50 | 51 | func (rp *extraInfoProvider) GetEnvVal() types.AdditionalInfo { 52 | extraInfos := make(map[string]string, 0) 53 | 54 | // first we search for global configuration with the * then we check for specific one to override 55 | for _, value := range []string{"*", rp.pciAddr} { 56 | extraInfoDict, ok := rp.extraInfo[value] 57 | if ok { 58 | for k, v := range extraInfoDict { 59 | extraInfos[k] = v 60 | } 61 | } 62 | } 63 | return extraInfos 64 | } 65 | 66 | func (rp *extraInfoProvider) GetMounts() []*pluginapi.Mount { 67 | mounts := make([]*pluginapi.Mount, 0) 68 | return mounts 69 | } 70 | 71 | // ***************************************************************** 72 | -------------------------------------------------------------------------------- /pkg/infoprovider/extraInfoProvider_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package infoprovider_test 16 | 17 | import ( 18 | . "github.com/onsi/ginkgo" 19 | . "github.com/onsi/gomega" 20 | 21 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/infoprovider" 22 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 23 | ) 24 | 25 | var _ = Describe("ExtraInfoProvider", func() { 26 | Describe("creating new extraInfoProvider", func() { 27 | It("should return valid rdmaInfoProvider object", func() { 28 | dip := infoprovider.NewExtraInfoProvider("fake01", map[string]types.AdditionalInfo{}) 29 | Expect(dip).NotTo(BeNil()) 30 | }) 31 | }) 32 | Describe("GetEnvVal", func() { 33 | It("should return an empty list if there are no environment variables", func() { 34 | dip := infoprovider.NewExtraInfoProvider("fake", nil) 35 | envs := dip.GetEnvVal() 36 | Expect(envs).To(BeEmpty()) 37 | }) 38 | It("should return an object with environment variables", func() { 39 | dip := infoprovider.NewExtraInfoProvider("fake", map[string]types.AdditionalInfo{"*": map[string]string{"test": "test"}}) 40 | envs := dip.GetEnvVal() 41 | Expect(envs).To(HaveLen(1)) 42 | value, exist := envs["test"] 43 | Expect(exist).To(BeTrue()) 44 | Expect(value).To(Equal("test")) 45 | }) 46 | It("should return an object with specific selector for environment variable", func() { 47 | dip := infoprovider.NewExtraInfoProvider("0000:00:00.1", map[string]types.AdditionalInfo{"*": map[string]string{"test": "test"}, "0000:00:00.1": map[string]string{"test": "test1"}}) 48 | envs := dip.GetEnvVal() 49 | value, exist := envs["test"] 50 | Expect(exist).To(BeTrue()) 51 | Expect(value).To(Equal("test1")) 52 | }) 53 | It("should return an object with specific selector for multiple environment variable", func() { 54 | dip := infoprovider.NewExtraInfoProvider("0000:00:00.1", map[string]types.AdditionalInfo{"*": map[string]string{"test": "test", "bla": "bla"}, "0000:00:00.1": map[string]string{"test": "test1"}}) 55 | envs := dip.GetEnvVal() 56 | Expect(envs).To(HaveLen(2)) 57 | value, exist := envs["test"] 58 | Expect(exist).To(BeTrue()) 59 | Expect(value).To(Equal("test1")) 60 | value, exist = envs["bla"] 61 | Expect(exist).To(BeTrue()) 62 | Expect(value).To(Equal("bla")) 63 | }) 64 | It("should return an object with multiple specific selector for environment variable", func() { 65 | dip := infoprovider.NewExtraInfoProvider("0000:00:00.1", map[string]types.AdditionalInfo{"*": map[string]string{"test": "test"}, "0000:00:00.1": map[string]string{"test": "test1", "bla": "bla"}}) 66 | envs := dip.GetEnvVal() 67 | Expect(envs).To(HaveLen(2)) 68 | value, exist := envs["test"] 69 | Expect(exist).To(BeTrue()) 70 | Expect(value).To(Equal("test1")) 71 | value, exist = envs["bla"] 72 | Expect(exist).To(BeTrue()) 73 | Expect(value).To(Equal("bla")) 74 | }) 75 | }) 76 | }) 77 | -------------------------------------------------------------------------------- /pkg/infoprovider/genericInfoProvider.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Intel Corp. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package infoprovider 16 | 17 | import ( 18 | pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 19 | 20 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 21 | ) 22 | 23 | type genericInfoProvider struct { 24 | deviceID string 25 | } 26 | 27 | // NewGenericInfoProvider instantiate a generic DeviceInfoProvider 28 | func NewGenericInfoProvider(deviceID string) types.DeviceInfoProvider { 29 | return &genericInfoProvider{ 30 | deviceID: deviceID, 31 | } 32 | } 33 | 34 | // ***************************************************************** 35 | /* DeviceInfoProvider Interface */ 36 | 37 | func (rp *genericInfoProvider) GetName() string { 38 | return "generic" 39 | } 40 | 41 | func (rp *genericInfoProvider) GetDeviceSpecs() []*pluginapi.DeviceSpec { 42 | devSpecs := make([]*pluginapi.DeviceSpec, 0) 43 | // NO device file, send empty DeviceSpec map 44 | return devSpecs 45 | } 46 | 47 | func (rp *genericInfoProvider) GetEnvVal() types.AdditionalInfo { 48 | envs := make(map[string]string, 0) 49 | envs["deviceID"] = rp.deviceID 50 | return envs 51 | } 52 | 53 | func (rp *genericInfoProvider) GetMounts() []*pluginapi.Mount { 54 | mounts := make([]*pluginapi.Mount, 0) 55 | return mounts 56 | } 57 | 58 | // ***************************************************************** 59 | -------------------------------------------------------------------------------- /pkg/infoprovider/genericInfoProvider_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package infoprovider_test 19 | 20 | import ( 21 | . "github.com/onsi/ginkgo" 22 | . "github.com/onsi/gomega" 23 | 24 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/infoprovider" 25 | ) 26 | 27 | var _ = Describe("genericInfoProvider", func() { 28 | Describe("creating new genericInfoProvider", func() { 29 | It("should return valid genericInfoProvider object", func() { 30 | dip := infoprovider.NewGenericInfoProvider("fakePCIAddr") 31 | Expect(dip).NotTo(BeNil()) 32 | // FIXME: Expect(reflect.TypeOf(dip)).To(Equal(reflect.TypeOf(&genericInfoProvider{}))) 33 | }) 34 | }) 35 | Describe("getting mounts", func() { 36 | It("should always return an empty array", func() { 37 | dip := infoprovider.NewGenericInfoProvider("fakePCIAddr") 38 | Expect(dip.GetMounts()).To(BeEmpty()) 39 | }) 40 | }) 41 | Describe("getting device specs", func() { 42 | It("should always return an empty map", func() { 43 | dip := infoprovider.NewGenericInfoProvider("fakePCIAddr") 44 | Expect(dip.GetDeviceSpecs()).To(BeEmpty()) 45 | }) 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /pkg/infoprovider/infoprovider_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package infoprovider_test 19 | 20 | import ( 21 | "testing" 22 | 23 | . "github.com/onsi/ginkgo" 24 | . "github.com/onsi/gomega" 25 | ) 26 | 27 | func TestResources(t *testing.T) { 28 | RegisterFailHandler(Fail) 29 | RunSpecs(t, "InfoProvider Suite") 30 | } 31 | -------------------------------------------------------------------------------- /pkg/infoprovider/rdmaInfoProvider.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package infoprovider 19 | 20 | import ( 21 | "strings" 22 | 23 | "github.com/golang/glog" 24 | pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 25 | 26 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 27 | ) 28 | 29 | /* 30 | rdmaInfoProvider provides the RDMA information 31 | */ 32 | type rdmaInfoProvider struct { 33 | rdmaSpec types.RdmaSpec 34 | } 35 | 36 | // NewRdmaInfoProvider returns a new Rdma Information Provider 37 | func NewRdmaInfoProvider(rdmaSpec types.RdmaSpec) types.DeviceInfoProvider { 38 | return &rdmaInfoProvider{ 39 | rdmaSpec: rdmaSpec, 40 | } 41 | } 42 | 43 | // ***************************************************************** 44 | /* DeviceInfoProvider Interface */ 45 | 46 | func (ip *rdmaInfoProvider) GetName() string { 47 | return "rdma" 48 | } 49 | 50 | func (ip *rdmaInfoProvider) GetDeviceSpecs() []*pluginapi.DeviceSpec { 51 | if !ip.rdmaSpec.IsRdma() { 52 | glog.Errorf("GetDeviceSpecs(): rdma is required in the configuration but the device is not rdma device") 53 | return nil 54 | } 55 | 56 | devsSpec := ip.rdmaSpec.GetRdmaDeviceSpec() 57 | glog.Infof("GetDeviceSpecs(): GetRdmaDeviceSpec returned %v", devsSpec) 58 | return devsSpec 59 | } 60 | 61 | func (ip *rdmaInfoProvider) GetEnvVal() types.AdditionalInfo { 62 | envs := make(map[string]string, 0) 63 | devsSpec := ip.rdmaSpec.GetRdmaDeviceSpec() 64 | for _, devSpec := range devsSpec { 65 | switch { 66 | case strings.Contains(devSpec.ContainerPath, "uverbs"): 67 | envs["uverbs"] = devSpec.ContainerPath 68 | case strings.Contains(devSpec.ContainerPath, "umad"): 69 | envs["umad"] = devSpec.ContainerPath 70 | case strings.Contains(devSpec.ContainerPath, "issm"): 71 | envs["issm"] = devSpec.ContainerPath 72 | case strings.Contains(devSpec.ContainerPath, "rdma_cm"): 73 | envs["rdma_cm"] = devSpec.ContainerPath 74 | } 75 | } 76 | 77 | rdmadev := ip.rdmaSpec.GetRdmaDeviceName() 78 | if rdmadev != "" { 79 | envs["rdma_dev"] = rdmadev 80 | } 81 | 82 | return envs 83 | } 84 | 85 | func (ip *rdmaInfoProvider) GetMounts() []*pluginapi.Mount { 86 | return nil 87 | } 88 | 89 | // ***************************************************************** 90 | -------------------------------------------------------------------------------- /pkg/infoprovider/rdmaInfoProvider_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package infoprovider_test 19 | 20 | import ( 21 | . "github.com/onsi/ginkgo" 22 | . "github.com/onsi/gomega" 23 | pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 24 | 25 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/infoprovider" 26 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types/mocks" 27 | ) 28 | 29 | var _ = Describe("rdmaInfoProvider", func() { 30 | Describe("creating new rdmaInfoProvider", func() { 31 | It("should return valid rdmaInfoProvider object", func() { 32 | rdma := &mocks.RdmaSpec{} 33 | dip := infoprovider.NewRdmaInfoProvider(rdma) 34 | Expect(dip).NotTo(BeNil()) 35 | // FIXME: Expect(reflect.TypeOf(dip)).To(Equal(reflect.TypeOf(&rdmaInfoProvider{}))) 36 | }) 37 | }) 38 | Describe("GetDeviceSpecs", func() { 39 | It("should return an empty map for non-rdma device", func() { 40 | rdma := &mocks.RdmaSpec{} 41 | rdma.On("IsRdma").Return(false) 42 | dip := infoprovider.NewRdmaInfoProvider(rdma) 43 | Expect(dip.GetDeviceSpecs()).To(BeNil()) 44 | }) 45 | It("should return non empty map for rdma device", func() { 46 | rdma := &mocks.RdmaSpec{} 47 | rdmaSpecs := []*pluginapi.DeviceSpec{ 48 | {ContainerPath: "/fake/path", HostPath: "/dev/fake1a"}, 49 | {ContainerPath: "/fake/path", HostPath: "/dev/fake1b"}, 50 | } 51 | rdma.On("IsRdma").Return(true). 52 | On("GetRdmaDeviceSpec").Return(rdmaSpecs) 53 | 54 | dip := infoprovider.NewRdmaInfoProvider(rdma) 55 | Expect(dip.GetDeviceSpecs()).To(HaveLen(2)) 56 | }) 57 | }) 58 | Describe("GetEnvVal", func() { 59 | It("should the rdma mounts from deviceSpecs", func() { 60 | rdma := &mocks.RdmaSpec{} 61 | rdma.On("IsRdma").Return(true). 62 | On("GetRdmaDeviceSpec").Return([]*pluginapi.DeviceSpec{ 63 | {ContainerPath: "/dev/infiniband/issm4"}, 64 | {ContainerPath: "/dev/infiniband/umad4"}, 65 | {ContainerPath: "/dev/infiniband/uverbs4"}, 66 | {ContainerPath: "/dev/infiniband/rdma_cm"}}). 67 | On("GetRdmaDeviceName").Return("mlx5_3") 68 | dip := infoprovider.NewRdmaInfoProvider(rdma) 69 | dip.GetDeviceSpecs() 70 | envs := dip.GetEnvVal() 71 | Expect(envs).To(HaveLen(5)) 72 | mount, exist := envs["rdma_cm"] 73 | Expect(exist).To(BeTrue()) 74 | Expect(mount).To(Equal("/dev/infiniband/rdma_cm")) 75 | mount, exist = envs["uverbs"] 76 | Expect(exist).To(BeTrue()) 77 | Expect(mount).To(Equal("/dev/infiniband/uverbs4")) 78 | mount, exist = envs["umad"] 79 | Expect(exist).To(BeTrue()) 80 | Expect(mount).To(Equal("/dev/infiniband/umad4")) 81 | mount, exist = envs["issm"] 82 | Expect(exist).To(BeTrue()) 83 | Expect(mount).To(Equal("/dev/infiniband/issm4")) 84 | rdmadev, exist := envs["rdma_dev"] 85 | Expect(exist).To(BeTrue()) 86 | Expect(rdmadev).To(Equal("mlx5_3")) 87 | }) 88 | }) 89 | Describe("GetMounts", func() { 90 | It("should always return an empty array", func() { 91 | rdma := &mocks.RdmaSpec{} 92 | dip := infoprovider.NewRdmaInfoProvider(rdma) 93 | Expect(dip.GetMounts()).To(BeEmpty()) 94 | }) 95 | }) 96 | }) 97 | -------------------------------------------------------------------------------- /pkg/infoprovider/uioInfoProvider.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Intel Corp. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package infoprovider 16 | 17 | import ( 18 | "github.com/golang/glog" 19 | pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 20 | 21 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 22 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils" 23 | ) 24 | 25 | type uioInfoProvider struct { 26 | pciAddr string 27 | uioDev string 28 | } 29 | 30 | // NewUioInfoProvider return instance of uio DeviceInfoProvider 31 | func NewUioInfoProvider(pciAddr string) types.DeviceInfoProvider { 32 | return &uioInfoProvider{ 33 | pciAddr: pciAddr, 34 | } 35 | } 36 | 37 | // ***************************************************************** 38 | /* DeviceInfoProvider Interface */ 39 | 40 | func (rp *uioInfoProvider) GetName() string { 41 | return "uio" 42 | } 43 | 44 | func (rp *uioInfoProvider) GetDeviceSpecs() []*pluginapi.DeviceSpec { 45 | devSpecs := make([]*pluginapi.DeviceSpec, 0) 46 | 47 | uioDev, err := utils.GetUIODeviceFile(rp.pciAddr) 48 | if err != nil { 49 | glog.Errorf("GetDeviceSpecs(): error getting vfio device file for device: %s", rp.pciAddr) 50 | } else { 51 | devSpecs = append(devSpecs, &pluginapi.DeviceSpec{ 52 | HostPath: uioDev, 53 | ContainerPath: uioDev, 54 | Permissions: "rw", 55 | }) 56 | rp.uioDev = uioDev 57 | } 58 | 59 | return devSpecs 60 | } 61 | 62 | func (rp *uioInfoProvider) GetEnvVal() types.AdditionalInfo { 63 | envs := make(map[string]string, 0) 64 | if rp.uioDev != "" { 65 | envs["mount"] = rp.uioDev 66 | } 67 | 68 | return envs 69 | } 70 | 71 | func (rp *uioInfoProvider) GetMounts() []*pluginapi.Mount { 72 | mounts := make([]*pluginapi.Mount, 0) 73 | return mounts 74 | } 75 | 76 | // ***************************************************************** 77 | -------------------------------------------------------------------------------- /pkg/infoprovider/uioInfoProvider_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package infoprovider_test 19 | 20 | import ( 21 | . "github.com/onsi/ginkgo" 22 | . "github.com/onsi/ginkgo/extensions/table" 23 | . "github.com/onsi/gomega" 24 | pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 25 | 26 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/infoprovider" 27 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils" 28 | ) 29 | 30 | var _ = Describe("uioInfoProvider", func() { 31 | Describe("creating new uioInfoProvider", func() { 32 | It("should return valid uioInfoProvider object", func() { 33 | dip := infoprovider.NewUioInfoProvider("fakePCIAddr") 34 | Expect(dip).NotTo(BeNil()) 35 | // FIXME: Expect(reflect.TypeOf(dip)).To(Equal(reflect.TypeOf(&uioInfoProvider{}))) 36 | }) 37 | }) 38 | DescribeTable("getting device specs", 39 | func(fs *utils.FakeFilesystem, pciAddr string, expected []*pluginapi.DeviceSpec) { 40 | defer fs.Use()() 41 | 42 | dip := infoprovider.NewUioInfoProvider(pciAddr) 43 | specs := dip.GetDeviceSpecs() 44 | Expect(specs).To(ConsistOf(expected)) 45 | }, 46 | Entry("empty", &utils.FakeFilesystem{}, "", []*pluginapi.DeviceSpec{}), 47 | Entry("PCI address passed, returns DeviceSpec with paths to its UIO devices", 48 | &utils.FakeFilesystem{ 49 | Dirs: []string{ 50 | "sys/bus/pci/devices/0000:02:00.0/uio/uio0", 51 | }, 52 | }, 53 | "0000:02:00.0", 54 | []*pluginapi.DeviceSpec{ 55 | {HostPath: "/dev/uio0", ContainerPath: "/dev/uio0", Permissions: "rw"}, 56 | }, 57 | ), 58 | ) 59 | Describe("getting mounts", func() { 60 | It("should always return empty array of mounts", func() { 61 | dip := infoprovider.NewUioInfoProvider("fakePCIAddr") 62 | Expect(dip.GetMounts()).To(BeEmpty()) 63 | }) 64 | }) 65 | Describe("getting env val", func() { 66 | It("should return passed PCI address and mounts for device", func() { 67 | pciAddr := "0000:02:00.0" 68 | fs := utils.FakeFilesystem{ 69 | Dirs: []string{ 70 | "sys/bus/pci/devices/0000:02:00.0/uio/uio0", 71 | }, 72 | } 73 | defer fs.Use()() 74 | dip := infoprovider.NewUioInfoProvider(pciAddr) 75 | dip.GetDeviceSpecs() 76 | envs := dip.GetEnvVal() 77 | Expect(envs).To(HaveLen(1)) 78 | mount, exist := envs["mount"] 79 | Expect(exist).To(BeTrue()) 80 | Expect(mount).To(Equal("/dev/uio0")) 81 | }) 82 | }) 83 | }) 84 | -------------------------------------------------------------------------------- /pkg/infoprovider/vdpaInfoProvider.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package infoprovider 19 | 20 | import ( 21 | "fmt" 22 | 23 | "github.com/golang/glog" 24 | pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 25 | 26 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 27 | ) 28 | 29 | // vdpaInfoProvider is a DeviceInfoProvider that handles the API information of vdpa-capable devices. 30 | type vdpaInfoProvider struct { 31 | dev types.VdpaDevice 32 | vdpaType types.VdpaType 33 | vdpaPath string 34 | } 35 | 36 | // NewVdpaInfoProvider returns a new InfoProvider associated with the given VDPAInfo 37 | func NewVdpaInfoProvider(vdpaType types.VdpaType, vdpaDev types.VdpaDevice) types.DeviceInfoProvider { 38 | vdpaInfoProvider := &vdpaInfoProvider{ 39 | dev: vdpaDev, 40 | vdpaType: vdpaType, 41 | } 42 | return vdpaInfoProvider 43 | } 44 | 45 | // ***************************************************************** 46 | /* DeviceInfoProvider Interface */ 47 | 48 | func (vip *vdpaInfoProvider) GetName() string { 49 | return "vdpa" 50 | } 51 | 52 | // GetDeviceSpecs returns the DeviceSpec slice 53 | func (vip *vdpaInfoProvider) GetDeviceSpecs() []*pluginapi.DeviceSpec { 54 | if healthy, err := vip.isHealthy(); !healthy { 55 | glog.Errorf("GetDeviceSpecs(): vDPA is required in the configuration but device does not have a healthy vdpa device: %s", 56 | err) 57 | return nil 58 | } 59 | devSpecs := make([]*pluginapi.DeviceSpec, 0) 60 | 61 | // DeviceSpecs only required for vhost vdpa type as the 62 | if vip.vdpaType == types.VdpaVhostType { 63 | vdpaPath, err := vip.dev.GetPath() 64 | if err != nil { 65 | glog.Errorf("Unexpected error when fetching the vdpa device path: %s", err) 66 | return nil 67 | } 68 | devSpecs = append(devSpecs, &pluginapi.DeviceSpec{ 69 | HostPath: vdpaPath, 70 | ContainerPath: vdpaPath, 71 | Permissions: "rw", 72 | }) 73 | vip.vdpaPath = vdpaPath 74 | } 75 | return devSpecs 76 | } 77 | 78 | // GetEnvVal returns the environment variable value 79 | func (vip *vdpaInfoProvider) GetEnvVal() types.AdditionalInfo { 80 | envs := make(map[string]string, 0) 81 | if vip.vdpaPath != "" { 82 | envs["mount"] = vip.vdpaPath 83 | } 84 | 85 | return envs 86 | } 87 | 88 | // GetMounts returns the mount points (none for this InfoProvider) 89 | func (vip *vdpaInfoProvider) GetMounts() []*pluginapi.Mount { 90 | mounts := make([]*pluginapi.Mount, 0) 91 | return mounts 92 | } 93 | 94 | // isHealthy returns whether the device's vDPA information is healthy 95 | func (vip *vdpaInfoProvider) isHealthy() (bool, error) { 96 | if vip.dev == nil { 97 | return false, fmt.Errorf("no vDPA device found") 98 | } 99 | 100 | if _, ok := types.SupportedVdpaTypes[vip.vdpaType]; !ok { 101 | return false, fmt.Errorf("vdpaType not supported %s", vip.vdpaType) 102 | } 103 | vType := vip.dev.GetType() 104 | if vType == types.VdpaInvalidType { 105 | return false, fmt.Errorf("device does not have a valid vdpa types") 106 | } 107 | if vType != vip.vdpaType { 108 | return false, fmt.Errorf("wrong vdpa type. Config expects %s but device is %s", 109 | vip.vdpaType, vType) 110 | } 111 | return true, nil 112 | } 113 | 114 | // ***************************************************************** 115 | -------------------------------------------------------------------------------- /pkg/infoprovider/vdpaInfoProvider_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package infoprovider_test 19 | 20 | import ( 21 | . "github.com/onsi/ginkgo" 22 | . "github.com/onsi/gomega" 23 | pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 24 | 25 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/infoprovider" 26 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 27 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types/mocks" 28 | ) 29 | 30 | var _ = Describe("vdpaInfoProvider", func() { 31 | Describe("creating new vdpaInfoProvider", func() { 32 | It("should return valid vdpaInfoProvider object", func() { 33 | dip := infoprovider.NewVdpaInfoProvider(types.VdpaVirtioType, nil) 34 | Expect(dip).NotTo(BeNil()) 35 | // FIXME: Expect(reflect.TypeOf(dip)).To(Equal(reflect.TypeOf(&vdpaInfoProvider{}))) 36 | }) 37 | }) 38 | Describe("GetDeviceSpecs", func() { 39 | It("should return nil if no vdpa device provided", func() { 40 | dip := infoprovider.NewVdpaInfoProvider(types.VdpaVirtioType, nil) 41 | Expect(dip.GetDeviceSpecs()).To(BeNil()) 42 | }) 43 | It("should return nil if vdpa type is not supported", func() { 44 | vdpa := &mocks.VdpaDevice{} 45 | dip := infoprovider.NewVdpaInfoProvider(types.VdpaInvalidType, vdpa) 46 | Expect(dip.GetDeviceSpecs()).To(BeNil()) 47 | }) 48 | It("should return nil if vdpa device has invalid type", func() { 49 | vdpa := &mocks.VdpaDevice{} 50 | vdpa.On("GetType").Return(types.VdpaInvalidType) 51 | dip := infoprovider.NewVdpaInfoProvider(types.VdpaVirtioType, vdpa) 52 | Expect(dip.GetDeviceSpecs()).To(BeNil()) 53 | }) 54 | It("should return nil if vdpa device type doesn't match", func() { 55 | vdpa := &mocks.VdpaDevice{} 56 | vdpa.On("GetType").Return(types.VdpaVhostType) 57 | dip := infoprovider.NewVdpaInfoProvider(types.VdpaVirtioType, vdpa) 58 | Expect(dip.GetDeviceSpecs()).To(BeNil()) 59 | }) 60 | It("should return empty map if device type is not a vdpa vhost", func() { 61 | vdpa := &mocks.VdpaDevice{} 62 | vdpa.On("GetType").Return(types.VdpaVirtioType) 63 | dip := infoprovider.NewVdpaInfoProvider(types.VdpaVirtioType, vdpa) 64 | Expect(dip.GetDeviceSpecs()).To(BeEmpty()) 65 | }) 66 | It("should return correct spec for vdpa vhost type device", func() { 67 | vdpa := &mocks.VdpaDevice{} 68 | vdpa.On("GetType").Return(types.VdpaVhostType). 69 | On("GetPath").Return("/dev/vhost-vdpa1", nil) 70 | dip := infoprovider.NewVdpaInfoProvider(types.VdpaVhostType, vdpa) 71 | Expect(dip.GetDeviceSpecs()).To(Equal([]*pluginapi.DeviceSpec{{ 72 | HostPath: "/dev/vhost-vdpa1", 73 | ContainerPath: "/dev/vhost-vdpa1", 74 | Permissions: "rw", 75 | }})) 76 | }) 77 | }) 78 | Describe("GetEnvVal", func() { 79 | It("should return an empty list if there are no mounts", func() { 80 | dip := infoprovider.NewVdpaInfoProvider(types.VdpaVirtioType, nil) 81 | envs := dip.GetEnvVal() 82 | Expect(envs).To(BeEmpty()) 83 | }) 84 | It("should return object with the mounts", func() { 85 | vdpa := &mocks.VdpaDevice{} 86 | vdpa.On("GetType").Return(types.VdpaVhostType). 87 | On("GetPath").Return("/dev/vhost-vdpa1", nil) 88 | dip := infoprovider.NewVdpaInfoProvider(types.VdpaVhostType, vdpa) 89 | dip.GetDeviceSpecs() 90 | envs := dip.GetEnvVal() 91 | Expect(envs).To(HaveLen(1)) 92 | mount, exist := envs["mount"] 93 | Expect(exist).To(BeTrue()) 94 | Expect(mount).To(Equal("/dev/vhost-vdpa1")) 95 | }) 96 | }) 97 | Describe("GetMounts", func() { 98 | It("should always return an empty array", func() { 99 | dip := infoprovider.NewVdpaInfoProvider(types.VdpaVirtioType, nil) 100 | Expect(dip.GetMounts()).To(BeEmpty()) 101 | }) 102 | }) 103 | }) 104 | -------------------------------------------------------------------------------- /pkg/infoprovider/vfioInfoProvider.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Intel Corp. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package infoprovider 16 | 17 | import ( 18 | "github.com/golang/glog" 19 | pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 20 | 21 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 22 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils" 23 | ) 24 | 25 | /* 26 | vfioInfoProvider implements DeviceInfoProvider 27 | */ 28 | type vfioInfoProvider struct { 29 | pciAddr string 30 | vfioMount string 31 | vfioDevContainer string 32 | } 33 | 34 | // NewVfioInfoProvider create instance of VFIO DeviceInfoProvider 35 | func NewVfioInfoProvider(pciAddr string) types.DeviceInfoProvider { 36 | return &vfioInfoProvider{ 37 | pciAddr: pciAddr, 38 | vfioMount: "/dev/vfio/vfio", 39 | } 40 | } 41 | 42 | // ***************************************************************** 43 | /* DeviceInfoProvider Interface */ 44 | 45 | func (rp *vfioInfoProvider) GetName() string { 46 | return "vfio" 47 | } 48 | 49 | func (rp *vfioInfoProvider) GetDeviceSpecs() []*pluginapi.DeviceSpec { 50 | devSpecs := make([]*pluginapi.DeviceSpec, 0) 51 | devSpecs = append(devSpecs, &pluginapi.DeviceSpec{ 52 | HostPath: rp.vfioMount, 53 | ContainerPath: rp.vfioMount, 54 | Permissions: "rw", 55 | }) 56 | 57 | vfioDevHost, vfioDevContainer, err := utils.GetVFIODeviceFile(rp.pciAddr) 58 | if err != nil { 59 | glog.Errorf("GetDeviceSpecs(): error getting vfio device file for device: %s, %s", rp.pciAddr, err.Error()) 60 | } else { 61 | devSpecs = append(devSpecs, &pluginapi.DeviceSpec{ 62 | HostPath: vfioDevHost, 63 | ContainerPath: vfioDevContainer, 64 | Permissions: "rw", 65 | }) 66 | rp.vfioDevContainer = vfioDevContainer 67 | } 68 | 69 | return devSpecs 70 | } 71 | 72 | func (rp *vfioInfoProvider) GetEnvVal() types.AdditionalInfo { 73 | envs := make(map[string]string, 0) 74 | envs["mount"] = "/dev/vfio/vfio" 75 | if rp.vfioDevContainer != "" { 76 | envs["dev-mount"] = rp.vfioDevContainer 77 | } 78 | 79 | return envs 80 | } 81 | 82 | func (rp *vfioInfoProvider) GetMounts() []*pluginapi.Mount { 83 | mounts := make([]*pluginapi.Mount, 0) 84 | return mounts 85 | } 86 | 87 | // ***************************************************************** 88 | -------------------------------------------------------------------------------- /pkg/infoprovider/vfioInfoProvider_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package infoprovider_test 19 | 20 | import ( 21 | . "github.com/onsi/ginkgo" 22 | . "github.com/onsi/ginkgo/extensions/table" 23 | . "github.com/onsi/gomega" 24 | pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 25 | 26 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/infoprovider" 27 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils" 28 | ) 29 | 30 | var _ = Describe("vfioInfoProvider", func() { 31 | Describe("creating new vfioInfoProvider", func() { 32 | It("should return valid vfioInfoProvider object", func() { 33 | dip := infoprovider.NewVfioInfoProvider("fakePCIAddr") 34 | Expect(dip).NotTo(BeNil()) 35 | // FIXME: Expect(reflect.TypeOf(dip)).To(Equal(reflect.TypeOf(&vfioInfoProvider{}))) 36 | }) 37 | }) 38 | DescribeTable("GetDeviceSpecs", 39 | func(fs *utils.FakeFilesystem, pciAddr string, expected []*pluginapi.DeviceSpec) { 40 | defer fs.Use()() 41 | 42 | dip := infoprovider.NewVfioInfoProvider(pciAddr) 43 | specs := dip.GetDeviceSpecs() 44 | Expect(specs).To(ConsistOf(expected)) 45 | }, 46 | Entry("empty and returning default common vfio device file only", 47 | &utils.FakeFilesystem{}, 48 | "", 49 | []*pluginapi.DeviceSpec{ 50 | {HostPath: "/dev/vfio/vfio", ContainerPath: "/dev/vfio/vfio", Permissions: "rw"}, 51 | }, 52 | ), 53 | Entry("PCI address passed, returns DeviceSpec with paths to its VFIO devices and additional default VFIO path", 54 | &utils.FakeFilesystem{ 55 | Dirs: []string{ 56 | "sys/bus/pci/devices/0000:02:00.0", "sys/kernel/iommu_groups/0", 57 | }, 58 | Symlinks: map[string]string{ 59 | "sys/bus/pci/devices/0000:02:00.0/iommu_group": "../../../../kernel/iommu_groups/0", 60 | }, 61 | }, 62 | "0000:02:00.0", 63 | []*pluginapi.DeviceSpec{ 64 | {HostPath: "/dev/vfio/0", ContainerPath: "/dev/vfio/0", Permissions: "rw"}, 65 | {HostPath: "/dev/vfio/vfio", ContainerPath: "/dev/vfio/vfio", Permissions: "rw"}, 66 | }, 67 | ), 68 | ) 69 | Describe("getting mounts", func() { 70 | It("should always return empty array of mounts", func() { 71 | dip := infoprovider.NewVfioInfoProvider("fakeAddr") 72 | Expect(dip.GetMounts()).To(BeEmpty()) 73 | }) 74 | }) 75 | Describe("getting env val", func() { 76 | It("should return passed PCI address and vfio device mount", func() { 77 | pciAddr := "0000:02:00.0" 78 | fs := &utils.FakeFilesystem{ 79 | Dirs: []string{ 80 | "sys/bus/pci/devices/0000:02:00.0", "sys/kernel/iommu_groups/0", 81 | }, 82 | Symlinks: map[string]string{ 83 | "sys/bus/pci/devices/0000:02:00.0/iommu_group": "../../../../kernel/iommu_groups/0", 84 | }, 85 | } 86 | defer fs.Use()() 87 | 88 | dip := infoprovider.NewVfioInfoProvider(pciAddr) 89 | dip.GetDeviceSpecs() 90 | envs := dip.GetEnvVal() 91 | Expect(envs).To(HaveLen(2)) 92 | devMount, exist := envs["dev-mount"] 93 | Expect(exist).To(BeTrue()) 94 | Expect(devMount).To(Equal("/dev/vfio/0")) 95 | vfioMount, exist := envs["mount"] 96 | Expect(exist).To(BeTrue()) 97 | Expect(vfioMount).To(Equal("/dev/vfio/vfio")) 98 | }) 99 | }) 100 | }) 101 | -------------------------------------------------------------------------------- /pkg/infoprovider/vhostNetInfoProvider.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package infoprovider 19 | 20 | import ( 21 | "os" 22 | 23 | "github.com/golang/glog" 24 | pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 25 | 26 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 27 | ) 28 | 29 | var ( 30 | // HostNet variable for vhost-net device path 31 | // used to check path for unit-tests 32 | HostNet = "/dev/vhost-net" 33 | // HostTun variable for tun device path 34 | // used to check path for unit-tests 35 | HostTun = "/dev/net/tun" 36 | ) 37 | 38 | /* 39 | VhostNetInfoProvider wraps any DeviceInfoProvider and adds a vhost-net device 40 | */ 41 | type vhostNetInfoProvider struct { 42 | } 43 | 44 | // NewVhostNetInfoProvider returns a new Vhost Information Provider 45 | func NewVhostNetInfoProvider() types.DeviceInfoProvider { 46 | return &vhostNetInfoProvider{} 47 | } 48 | 49 | // VhostNetDeviceExist returns true if /dev/vhost-net exists 50 | func VhostNetDeviceExist() bool { 51 | _, err := os.Stat(HostNet) 52 | return err == nil 53 | } 54 | 55 | // GetVhostNetDeviceSpec returns an instance of DeviceSpec for vhost-net 56 | func getVhostNetDeviceSpec() []*pluginapi.DeviceSpec { 57 | deviceSpec := make([]*pluginapi.DeviceSpec, 0) 58 | deviceSpec = append(deviceSpec, &pluginapi.DeviceSpec{ 59 | HostPath: "/dev/vhost-net", 60 | ContainerPath: "/dev/vhost-net", 61 | Permissions: "rw", 62 | }) 63 | 64 | return deviceSpec 65 | } 66 | 67 | // TunDeviceExist returns true if /dev/net/tun exists 68 | func tunDeviceExist() bool { 69 | _, err := os.Stat(HostTun) 70 | return err == nil 71 | } 72 | 73 | // GetTunDeviceSpec returns an instance of DeviceSpec for Tun 74 | func getTunDeviceSpec() []*pluginapi.DeviceSpec { 75 | deviceSpec := make([]*pluginapi.DeviceSpec, 0) 76 | deviceSpec = append(deviceSpec, &pluginapi.DeviceSpec{ 77 | HostPath: "/dev/net/tun", 78 | ContainerPath: "/dev/net/tun", 79 | Permissions: "rw", 80 | }) 81 | 82 | return deviceSpec 83 | } 84 | 85 | // ***************************************************************** 86 | /* DeviceInfoProvider Interface */ 87 | 88 | func (ip *vhostNetInfoProvider) GetName() string { 89 | return "vhost" 90 | } 91 | 92 | func (ip *vhostNetInfoProvider) GetDeviceSpecs() []*pluginapi.DeviceSpec { 93 | if !VhostNetDeviceExist() { 94 | glog.Errorf("GetDeviceSpecs(): /dev/vhost-net doesn't exist") 95 | return nil 96 | } 97 | deviceSpec := getVhostNetDeviceSpec() 98 | 99 | if !tunDeviceExist() { 100 | glog.Errorf("GetDeviceSpecs(): /dev/net/tun doesn't exist") 101 | return nil 102 | } 103 | deviceSpec = append(deviceSpec, getTunDeviceSpec()...) 104 | 105 | return deviceSpec 106 | } 107 | 108 | func (ip *vhostNetInfoProvider) GetEnvVal() types.AdditionalInfo { 109 | envs := make(map[string]string, 0) 110 | envs["net-mount"] = "/dev/vhost-net" 111 | envs["tun-mount"] = "/dev/net/tun" 112 | 113 | return envs 114 | } 115 | 116 | func (ip *vhostNetInfoProvider) GetMounts() []*pluginapi.Mount { 117 | return nil 118 | } 119 | 120 | // ***************************************************************** 121 | -------------------------------------------------------------------------------- /pkg/infoprovider/vhostNetInfoProvider_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package infoprovider_test 19 | 20 | import ( 21 | "path/filepath" 22 | 23 | . "github.com/onsi/ginkgo" 24 | . "github.com/onsi/gomega" 25 | pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 26 | 27 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/infoprovider" 28 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils" 29 | ) 30 | 31 | var _ = Describe("vdpaInfoProvider", func() { 32 | Describe("creating new vdpaInfoProvider", func() { 33 | It("should return valid vdpaInfoProvider object", func() { 34 | dip := infoprovider.NewVhostNetInfoProvider() 35 | Expect(dip).NotTo(BeNil()) 36 | // FIXME: Expect(reflect.TypeOf(dip)).To(Equal(reflect.TypeOf(&vdpaInfoProvider{}))) 37 | }) 38 | }) 39 | Describe("GetDeviceSpecs", func() { 40 | It("should return correct specs for vhost net device", func() { 41 | fakeFs := &utils.FakeFilesystem{ 42 | Dirs: []string{"/dev/net/"}, 43 | Files: map[string][]byte{ 44 | "/dev/vhost-net": nil, 45 | "/dev/net/tun": nil}, 46 | } 47 | defer fakeFs.Use()() 48 | //nolint: gocritic 49 | infoprovider.HostNet = filepath.Join(fakeFs.RootDir, "/dev/vhost-net") 50 | //nolint: gocritic 51 | infoprovider.HostTun = filepath.Join(fakeFs.RootDir, "/dev/net/tun") 52 | 53 | dip := infoprovider.NewVhostNetInfoProvider() 54 | Expect(dip.GetDeviceSpecs()).To(Equal([]*pluginapi.DeviceSpec{ 55 | { 56 | HostPath: "/dev/vhost-net", 57 | ContainerPath: "/dev/vhost-net", 58 | Permissions: "rw", 59 | }, 60 | { 61 | HostPath: "/dev/net/tun", 62 | ContainerPath: "/dev/net/tun", 63 | Permissions: "rw", 64 | }, 65 | })) 66 | }) 67 | }) 68 | Describe("GetEnvVal", func() { 69 | It("should always return the device mounts info", func() { 70 | dip := infoprovider.NewVhostNetInfoProvider() 71 | envs := dip.GetEnvVal() 72 | Expect(envs).To(HaveLen(2)) 73 | mount, exist := envs["net-mount"] 74 | Expect(exist).To(BeTrue()) 75 | Expect(mount).To(Equal("/dev/vhost-net")) 76 | mount, exist = envs["tun-mount"] 77 | Expect(exist).To(BeTrue()) 78 | Expect(mount).To(Equal("/dev/net/tun")) 79 | }) 80 | }) 81 | Describe("GetMounts", func() { 82 | It("should always return an empty array", func() { 83 | dip := infoprovider.NewVhostNetInfoProvider() 84 | Expect(dip.GetMounts()).To(BeEmpty()) 85 | }) 86 | }) 87 | }) 88 | -------------------------------------------------------------------------------- /pkg/netdevice/nadutils.go: -------------------------------------------------------------------------------- 1 | package netdevice 2 | 3 | import ( 4 | nettypes "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" 5 | nadutils "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/utils" 6 | 7 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 8 | ) 9 | 10 | // nadutils implements types.NadUtils interface 11 | // It's purpose is to wrap the utilities provided by github.com/k8snetworkplumbingwg/network-attachment-definition-client 12 | // in order to make mocking easy for Unit Tests 13 | type nadUtils struct { 14 | } 15 | 16 | func (nu *nadUtils) SaveDeviceInfoFile(resourceName, deviceID string, devInfo *nettypes.DeviceInfo) error { 17 | return nadutils.SaveDeviceInfoForDP(resourceName, deviceID, devInfo) 18 | } 19 | 20 | func (nu *nadUtils) CleanDeviceInfoFile(resourceName, deviceID string) error { 21 | return nadutils.CleanDeviceInfoForDP(resourceName, deviceID) 22 | } 23 | 24 | // NewNadUtils returns a new NadUtils 25 | func NewNadUtils() types.NadUtils { 26 | return &nadUtils{} 27 | } 28 | -------------------------------------------------------------------------------- /pkg/resources/ddpSelector.go: -------------------------------------------------------------------------------- 1 | package resources 2 | 3 | import ( 4 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 5 | ) 6 | 7 | type ddpSelector struct { 8 | profiles []string 9 | } 10 | 11 | // NewDdpSelector returns a DeviceSelector interface to filter devices based on available DDP profile 12 | func NewDdpSelector(profiles []string) types.DeviceSelector { 13 | return &ddpSelector{profiles: profiles} 14 | } 15 | 16 | func (ds *ddpSelector) Filter(inDevices []types.HostDevice) []types.HostDevice { 17 | filteredList := make([]types.HostDevice, 0) 18 | 19 | for _, dev := range inDevices { 20 | ddpProfile := dev.(types.PciNetDevice).GetDDPProfiles() 21 | if ddpProfile != "" && contains(ds.profiles, ddpProfile) { 22 | filteredList = append(filteredList, dev) 23 | } 24 | } 25 | 26 | return filteredList 27 | } 28 | -------------------------------------------------------------------------------- /pkg/resources/ddpSelector_test.go: -------------------------------------------------------------------------------- 1 | package resources_test 2 | 3 | import ( 4 | "fmt" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | 9 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/resources" 10 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 11 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types/mocks" 12 | ) 13 | 14 | var _ = Describe("DdpSelector", func() { 15 | Describe("DDP selector", func() { 16 | Context("initializing", func() { 17 | It("should populate vendors array", func() { 18 | profiles := []string{"GTPv1-C", "PPPoE"} 19 | sel := resources.NewDdpSelector(profiles) 20 | fmt.Printf("%#v", sel) 21 | // Expect(sel.GetDPProfiles()).To(ConsistOf(profiles)) 22 | }) 23 | }) 24 | Context("filtering", func() { 25 | It("should return devices matching DDP profiles", func() { 26 | profiles := []string{"GTP"} 27 | sel := resources.NewDdpSelector(profiles) 28 | 29 | dev0 := mocks.PciNetDevice{} 30 | dev0.On("GetPciAddr").Return("0000:01:10.0") 31 | dev0.On("GetDDPProfiles").Return("GTP") 32 | 33 | dev1 := mocks.PciNetDevice{} 34 | dev1.On("GetPciAddr").Return("0000:01:10.1") 35 | dev1.On("GetDDPProfiles").Return("PPPoE") 36 | 37 | dev2 := mocks.PciNetDevice{} 38 | dev2.On("GetPciAddr").Return("0000:01:10.2") 39 | dev2.On("GetDDPProfiles").Return("") 40 | 41 | in := []types.HostDevice{&dev0, &dev1} 42 | filtered := sel.Filter(in) 43 | 44 | Expect(filtered).To(ContainElement(&dev0)) 45 | Expect(filtered).NotTo(ContainElement(&dev1)) 46 | Expect(filtered).NotTo(ContainElement(&dev2)) 47 | }) 48 | }) 49 | }) 50 | }) 51 | -------------------------------------------------------------------------------- /pkg/resources/pKeySelector.go: -------------------------------------------------------------------------------- 1 | package resources 2 | 3 | import ( 4 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 5 | ) 6 | 7 | type pKeySelector struct { 8 | pKeys []string 9 | } 10 | 11 | // NewPKeySelector returns a DeviceSelector interface to filter devices based on available PKeys 12 | func NewPKeySelector(pKeys []string) types.DeviceSelector { 13 | return &pKeySelector{pKeys: pKeys} 14 | } 15 | 16 | func (ds *pKeySelector) Filter(inDevices []types.HostDevice) []types.HostDevice { 17 | filteredList := make([]types.HostDevice, 0) 18 | 19 | for _, dev := range inDevices { 20 | pKey := dev.(types.PciNetDevice).GetPKey() 21 | if pKey != "" && contains(ds.pKeys, pKey) { 22 | filteredList = append(filteredList, dev) 23 | } 24 | } 25 | 26 | return filteredList 27 | } 28 | -------------------------------------------------------------------------------- /pkg/resources/pKeySelector_test.go: -------------------------------------------------------------------------------- 1 | package resources_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/resources" 8 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 9 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types/mocks" 10 | ) 11 | 12 | var _ = Describe("PKeySelector", func() { 13 | Describe("PKey selector", func() { 14 | Context("filtering", func() { 15 | It("should return devices matching given PKeys", func() { 16 | pKeys := []string{"0x1", "0x2"} 17 | sel := resources.NewPKeySelector(pKeys) 18 | 19 | dev0 := mocks.PciNetDevice{} 20 | dev0.On("GetPKey").Return("0x1") 21 | 22 | dev1 := mocks.PciNetDevice{} 23 | dev1.On("GetPKey").Return("0x2") 24 | 25 | dev2 := mocks.PciNetDevice{} 26 | dev2.On("GetPKey").Return("0x3") 27 | 28 | in := []types.HostDevice{&dev0, &dev1, &dev2} 29 | filtered := sel.Filter(in) 30 | 31 | Expect(filtered).To(ContainElement(&dev0)) 32 | Expect(filtered).To(ContainElement(&dev1)) 33 | Expect(filtered).NotTo(ContainElement(&dev2)) 34 | }) 35 | }) 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /pkg/resources/resources_suite_test.go: -------------------------------------------------------------------------------- 1 | package resources_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestResources(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Resources Suite") 13 | } 14 | -------------------------------------------------------------------------------- /pkg/types/mocks/APIDevice.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.53.3. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | types "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 7 | mock "github.com/stretchr/testify/mock" 8 | v1beta1 "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 9 | ) 10 | 11 | // APIDevice is an autogenerated mock type for the APIDevice type 12 | type APIDevice struct { 13 | mock.Mock 14 | } 15 | 16 | // GetAPIDevice provides a mock function with no fields 17 | func (_m *APIDevice) GetAPIDevice() *v1beta1.Device { 18 | ret := _m.Called() 19 | 20 | if len(ret) == 0 { 21 | panic("no return value specified for GetAPIDevice") 22 | } 23 | 24 | var r0 *v1beta1.Device 25 | if rf, ok := ret.Get(0).(func() *v1beta1.Device); ok { 26 | r0 = rf() 27 | } else { 28 | if ret.Get(0) != nil { 29 | r0 = ret.Get(0).(*v1beta1.Device) 30 | } 31 | } 32 | 33 | return r0 34 | } 35 | 36 | // GetDeviceSpecs provides a mock function with no fields 37 | func (_m *APIDevice) GetDeviceSpecs() []*v1beta1.DeviceSpec { 38 | ret := _m.Called() 39 | 40 | if len(ret) == 0 { 41 | panic("no return value specified for GetDeviceSpecs") 42 | } 43 | 44 | var r0 []*v1beta1.DeviceSpec 45 | if rf, ok := ret.Get(0).(func() []*v1beta1.DeviceSpec); ok { 46 | r0 = rf() 47 | } else { 48 | if ret.Get(0) != nil { 49 | r0 = ret.Get(0).([]*v1beta1.DeviceSpec) 50 | } 51 | } 52 | 53 | return r0 54 | } 55 | 56 | // GetEnvVal provides a mock function with no fields 57 | func (_m *APIDevice) GetEnvVal() map[string]types.AdditionalInfo { 58 | ret := _m.Called() 59 | 60 | if len(ret) == 0 { 61 | panic("no return value specified for GetEnvVal") 62 | } 63 | 64 | var r0 map[string]types.AdditionalInfo 65 | if rf, ok := ret.Get(0).(func() map[string]types.AdditionalInfo); ok { 66 | r0 = rf() 67 | } else { 68 | if ret.Get(0) != nil { 69 | r0 = ret.Get(0).(map[string]types.AdditionalInfo) 70 | } 71 | } 72 | 73 | return r0 74 | } 75 | 76 | // GetMounts provides a mock function with no fields 77 | func (_m *APIDevice) GetMounts() []*v1beta1.Mount { 78 | ret := _m.Called() 79 | 80 | if len(ret) == 0 { 81 | panic("no return value specified for GetMounts") 82 | } 83 | 84 | var r0 []*v1beta1.Mount 85 | if rf, ok := ret.Get(0).(func() []*v1beta1.Mount); ok { 86 | r0 = rf() 87 | } else { 88 | if ret.Get(0) != nil { 89 | r0 = ret.Get(0).([]*v1beta1.Mount) 90 | } 91 | } 92 | 93 | return r0 94 | } 95 | 96 | // NewAPIDevice creates a new instance of APIDevice. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 97 | // The first argument is typically a *testing.T value. 98 | func NewAPIDevice(t interface { 99 | mock.TestingT 100 | Cleanup(func()) 101 | }) *APIDevice { 102 | mock := &APIDevice{} 103 | mock.Mock.Test(t) 104 | 105 | t.Cleanup(func() { mock.AssertExpectations(t) }) 106 | 107 | return mock 108 | } 109 | -------------------------------------------------------------------------------- /pkg/types/mocks/DeviceInfoProvider.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.53.3. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | types "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 7 | mock "github.com/stretchr/testify/mock" 8 | v1beta1 "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 9 | ) 10 | 11 | // DeviceInfoProvider is an autogenerated mock type for the DeviceInfoProvider type 12 | type DeviceInfoProvider struct { 13 | mock.Mock 14 | } 15 | 16 | // GetDeviceSpecs provides a mock function with no fields 17 | func (_m *DeviceInfoProvider) GetDeviceSpecs() []*v1beta1.DeviceSpec { 18 | ret := _m.Called() 19 | 20 | if len(ret) == 0 { 21 | panic("no return value specified for GetDeviceSpecs") 22 | } 23 | 24 | var r0 []*v1beta1.DeviceSpec 25 | if rf, ok := ret.Get(0).(func() []*v1beta1.DeviceSpec); ok { 26 | r0 = rf() 27 | } else { 28 | if ret.Get(0) != nil { 29 | r0 = ret.Get(0).([]*v1beta1.DeviceSpec) 30 | } 31 | } 32 | 33 | return r0 34 | } 35 | 36 | // GetEnvVal provides a mock function with no fields 37 | func (_m *DeviceInfoProvider) GetEnvVal() types.AdditionalInfo { 38 | ret := _m.Called() 39 | 40 | if len(ret) == 0 { 41 | panic("no return value specified for GetEnvVal") 42 | } 43 | 44 | var r0 types.AdditionalInfo 45 | if rf, ok := ret.Get(0).(func() types.AdditionalInfo); ok { 46 | r0 = rf() 47 | } else { 48 | if ret.Get(0) != nil { 49 | r0 = ret.Get(0).(types.AdditionalInfo) 50 | } 51 | } 52 | 53 | return r0 54 | } 55 | 56 | // GetMounts provides a mock function with no fields 57 | func (_m *DeviceInfoProvider) GetMounts() []*v1beta1.Mount { 58 | ret := _m.Called() 59 | 60 | if len(ret) == 0 { 61 | panic("no return value specified for GetMounts") 62 | } 63 | 64 | var r0 []*v1beta1.Mount 65 | if rf, ok := ret.Get(0).(func() []*v1beta1.Mount); ok { 66 | r0 = rf() 67 | } else { 68 | if ret.Get(0) != nil { 69 | r0 = ret.Get(0).([]*v1beta1.Mount) 70 | } 71 | } 72 | 73 | return r0 74 | } 75 | 76 | // GetName provides a mock function with no fields 77 | func (_m *DeviceInfoProvider) GetName() string { 78 | ret := _m.Called() 79 | 80 | if len(ret) == 0 { 81 | panic("no return value specified for GetName") 82 | } 83 | 84 | var r0 string 85 | if rf, ok := ret.Get(0).(func() string); ok { 86 | r0 = rf() 87 | } else { 88 | r0 = ret.Get(0).(string) 89 | } 90 | 91 | return r0 92 | } 93 | 94 | // NewDeviceInfoProvider creates a new instance of DeviceInfoProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 95 | // The first argument is typically a *testing.T value. 96 | func NewDeviceInfoProvider(t interface { 97 | mock.TestingT 98 | Cleanup(func()) 99 | }) *DeviceInfoProvider { 100 | mock := &DeviceInfoProvider{} 101 | mock.Mock.Test(t) 102 | 103 | t.Cleanup(func() { mock.AssertExpectations(t) }) 104 | 105 | return mock 106 | } 107 | -------------------------------------------------------------------------------- /pkg/types/mocks/DeviceProvider.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.53.3. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | pci "github.com/jaypipes/ghw/pkg/pci" 7 | mock "github.com/stretchr/testify/mock" 8 | 9 | types "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 10 | ) 11 | 12 | // DeviceProvider is an autogenerated mock type for the DeviceProvider type 13 | type DeviceProvider struct { 14 | mock.Mock 15 | } 16 | 17 | // AddTargetDevices provides a mock function with given fields: _a0, _a1 18 | func (_m *DeviceProvider) AddTargetDevices(_a0 []*pci.Device, _a1 int) error { 19 | ret := _m.Called(_a0, _a1) 20 | 21 | if len(ret) == 0 { 22 | panic("no return value specified for AddTargetDevices") 23 | } 24 | 25 | var r0 error 26 | if rf, ok := ret.Get(0).(func([]*pci.Device, int) error); ok { 27 | r0 = rf(_a0, _a1) 28 | } else { 29 | r0 = ret.Error(0) 30 | } 31 | 32 | return r0 33 | } 34 | 35 | // GetDevices provides a mock function with given fields: _a0, _a1 36 | func (_m *DeviceProvider) GetDevices(_a0 *types.ResourceConfig, _a1 int) []types.HostDevice { 37 | ret := _m.Called(_a0, _a1) 38 | 39 | if len(ret) == 0 { 40 | panic("no return value specified for GetDevices") 41 | } 42 | 43 | var r0 []types.HostDevice 44 | if rf, ok := ret.Get(0).(func(*types.ResourceConfig, int) []types.HostDevice); ok { 45 | r0 = rf(_a0, _a1) 46 | } else { 47 | if ret.Get(0) != nil { 48 | r0 = ret.Get(0).([]types.HostDevice) 49 | } 50 | } 51 | 52 | return r0 53 | } 54 | 55 | // GetDiscoveredDevices provides a mock function with no fields 56 | func (_m *DeviceProvider) GetDiscoveredDevices() []*pci.Device { 57 | ret := _m.Called() 58 | 59 | if len(ret) == 0 { 60 | panic("no return value specified for GetDiscoveredDevices") 61 | } 62 | 63 | var r0 []*pci.Device 64 | if rf, ok := ret.Get(0).(func() []*pci.Device); ok { 65 | r0 = rf() 66 | } else { 67 | if ret.Get(0) != nil { 68 | r0 = ret.Get(0).([]*pci.Device) 69 | } 70 | } 71 | 72 | return r0 73 | } 74 | 75 | // GetFilteredDevices provides a mock function with given fields: _a0, _a1, _a2 76 | func (_m *DeviceProvider) GetFilteredDevices(_a0 []types.HostDevice, _a1 *types.ResourceConfig, _a2 int) ([]types.HostDevice, error) { 77 | ret := _m.Called(_a0, _a1, _a2) 78 | 79 | if len(ret) == 0 { 80 | panic("no return value specified for GetFilteredDevices") 81 | } 82 | 83 | var r0 []types.HostDevice 84 | var r1 error 85 | if rf, ok := ret.Get(0).(func([]types.HostDevice, *types.ResourceConfig, int) ([]types.HostDevice, error)); ok { 86 | return rf(_a0, _a1, _a2) 87 | } 88 | if rf, ok := ret.Get(0).(func([]types.HostDevice, *types.ResourceConfig, int) []types.HostDevice); ok { 89 | r0 = rf(_a0, _a1, _a2) 90 | } else { 91 | if ret.Get(0) != nil { 92 | r0 = ret.Get(0).([]types.HostDevice) 93 | } 94 | } 95 | 96 | if rf, ok := ret.Get(1).(func([]types.HostDevice, *types.ResourceConfig, int) error); ok { 97 | r1 = rf(_a0, _a1, _a2) 98 | } else { 99 | r1 = ret.Error(1) 100 | } 101 | 102 | return r0, r1 103 | } 104 | 105 | // ValidConfig provides a mock function with given fields: _a0 106 | func (_m *DeviceProvider) ValidConfig(_a0 *types.ResourceConfig) bool { 107 | ret := _m.Called(_a0) 108 | 109 | if len(ret) == 0 { 110 | panic("no return value specified for ValidConfig") 111 | } 112 | 113 | var r0 bool 114 | if rf, ok := ret.Get(0).(func(*types.ResourceConfig) bool); ok { 115 | r0 = rf(_a0) 116 | } else { 117 | r0 = ret.Get(0).(bool) 118 | } 119 | 120 | return r0 121 | } 122 | 123 | // NewDeviceProvider creates a new instance of DeviceProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 124 | // The first argument is typically a *testing.T value. 125 | func NewDeviceProvider(t interface { 126 | mock.TestingT 127 | Cleanup(func()) 128 | }) *DeviceProvider { 129 | mock := &DeviceProvider{} 130 | mock.Mock.Test(t) 131 | 132 | t.Cleanup(func() { mock.AssertExpectations(t) }) 133 | 134 | return mock 135 | } 136 | -------------------------------------------------------------------------------- /pkg/types/mocks/DeviceSelector.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.53.3. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | types "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 7 | mock "github.com/stretchr/testify/mock" 8 | ) 9 | 10 | // DeviceSelector is an autogenerated mock type for the DeviceSelector type 11 | type DeviceSelector struct { 12 | mock.Mock 13 | } 14 | 15 | // Filter provides a mock function with given fields: _a0 16 | func (_m *DeviceSelector) Filter(_a0 []types.HostDevice) []types.HostDevice { 17 | ret := _m.Called(_a0) 18 | 19 | if len(ret) == 0 { 20 | panic("no return value specified for Filter") 21 | } 22 | 23 | var r0 []types.HostDevice 24 | if rf, ok := ret.Get(0).(func([]types.HostDevice) []types.HostDevice); ok { 25 | r0 = rf(_a0) 26 | } else { 27 | if ret.Get(0) != nil { 28 | r0 = ret.Get(0).([]types.HostDevice) 29 | } 30 | } 31 | 32 | return r0 33 | } 34 | 35 | // NewDeviceSelector creates a new instance of DeviceSelector. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 36 | // The first argument is typically a *testing.T value. 37 | func NewDeviceSelector(t interface { 38 | mock.TestingT 39 | Cleanup(func()) 40 | }) *DeviceSelector { 41 | mock := &DeviceSelector{} 42 | mock.Mock.Test(t) 43 | 44 | t.Cleanup(func() { mock.AssertExpectations(t) }) 45 | 46 | return mock 47 | } 48 | -------------------------------------------------------------------------------- /pkg/types/mocks/LinkWatcher.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.53.3. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // LinkWatcher is an autogenerated mock type for the LinkWatcher type 8 | type LinkWatcher struct { 9 | mock.Mock 10 | } 11 | 12 | // Subscribe provides a mock function with no fields 13 | func (_m *LinkWatcher) Subscribe() { 14 | _m.Called() 15 | } 16 | 17 | // NewLinkWatcher creates a new instance of LinkWatcher. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 18 | // The first argument is typically a *testing.T value. 19 | func NewLinkWatcher(t interface { 20 | mock.TestingT 21 | Cleanup(func()) 22 | }) *LinkWatcher { 23 | mock := &LinkWatcher{} 24 | mock.Mock.Test(t) 25 | 26 | t.Cleanup(func() { mock.AssertExpectations(t) }) 27 | 28 | return mock 29 | } 30 | -------------------------------------------------------------------------------- /pkg/types/mocks/NadUtils.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.53.3. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | mock "github.com/stretchr/testify/mock" 7 | 8 | v1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" 9 | ) 10 | 11 | // NadUtils is an autogenerated mock type for the NadUtils type 12 | type NadUtils struct { 13 | mock.Mock 14 | } 15 | 16 | // CleanDeviceInfoFile provides a mock function with given fields: resourceName, deviceID 17 | func (_m *NadUtils) CleanDeviceInfoFile(resourceName string, deviceID string) error { 18 | ret := _m.Called(resourceName, deviceID) 19 | 20 | if len(ret) == 0 { 21 | panic("no return value specified for CleanDeviceInfoFile") 22 | } 23 | 24 | var r0 error 25 | if rf, ok := ret.Get(0).(func(string, string) error); ok { 26 | r0 = rf(resourceName, deviceID) 27 | } else { 28 | r0 = ret.Error(0) 29 | } 30 | 31 | return r0 32 | } 33 | 34 | // SaveDeviceInfoFile provides a mock function with given fields: resourceName, deviceID, devInfo 35 | func (_m *NadUtils) SaveDeviceInfoFile(resourceName string, deviceID string, devInfo *v1.DeviceInfo) error { 36 | ret := _m.Called(resourceName, deviceID, devInfo) 37 | 38 | if len(ret) == 0 { 39 | panic("no return value specified for SaveDeviceInfoFile") 40 | } 41 | 42 | var r0 error 43 | if rf, ok := ret.Get(0).(func(string, string, *v1.DeviceInfo) error); ok { 44 | r0 = rf(resourceName, deviceID, devInfo) 45 | } else { 46 | r0 = ret.Error(0) 47 | } 48 | 49 | return r0 50 | } 51 | 52 | // NewNadUtils creates a new instance of NadUtils. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 53 | // The first argument is typically a *testing.T value. 54 | func NewNadUtils(t interface { 55 | mock.TestingT 56 | Cleanup(func()) 57 | }) *NadUtils { 58 | mock := &NadUtils{} 59 | mock.Mock.Test(t) 60 | 61 | t.Cleanup(func() { mock.AssertExpectations(t) }) 62 | 63 | return mock 64 | } 65 | -------------------------------------------------------------------------------- /pkg/types/mocks/RdmaSpec.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.53.3. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | mock "github.com/stretchr/testify/mock" 7 | 8 | v1beta1 "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" 9 | ) 10 | 11 | // RdmaSpec is an autogenerated mock type for the RdmaSpec type 12 | type RdmaSpec struct { 13 | mock.Mock 14 | } 15 | 16 | // GetRdmaDeviceName provides a mock function with no fields 17 | func (_m *RdmaSpec) GetRdmaDeviceName() string { 18 | ret := _m.Called() 19 | 20 | if len(ret) == 0 { 21 | panic("no return value specified for GetRdmaDeviceName") 22 | } 23 | 24 | var r0 string 25 | if rf, ok := ret.Get(0).(func() string); ok { 26 | r0 = rf() 27 | } else { 28 | r0 = ret.Get(0).(string) 29 | } 30 | 31 | return r0 32 | } 33 | 34 | // GetRdmaDeviceSpec provides a mock function with no fields 35 | func (_m *RdmaSpec) GetRdmaDeviceSpec() []*v1beta1.DeviceSpec { 36 | ret := _m.Called() 37 | 38 | if len(ret) == 0 { 39 | panic("no return value specified for GetRdmaDeviceSpec") 40 | } 41 | 42 | var r0 []*v1beta1.DeviceSpec 43 | if rf, ok := ret.Get(0).(func() []*v1beta1.DeviceSpec); ok { 44 | r0 = rf() 45 | } else { 46 | if ret.Get(0) != nil { 47 | r0 = ret.Get(0).([]*v1beta1.DeviceSpec) 48 | } 49 | } 50 | 51 | return r0 52 | } 53 | 54 | // IsRdma provides a mock function with no fields 55 | func (_m *RdmaSpec) IsRdma() bool { 56 | ret := _m.Called() 57 | 58 | if len(ret) == 0 { 59 | panic("no return value specified for IsRdma") 60 | } 61 | 62 | var r0 bool 63 | if rf, ok := ret.Get(0).(func() bool); ok { 64 | r0 = rf() 65 | } else { 66 | r0 = ret.Get(0).(bool) 67 | } 68 | 69 | return r0 70 | } 71 | 72 | // NewRdmaSpec creates a new instance of RdmaSpec. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 73 | // The first argument is typically a *testing.T value. 74 | func NewRdmaSpec(t interface { 75 | mock.TestingT 76 | Cleanup(func()) 77 | }) *RdmaSpec { 78 | mock := &RdmaSpec{} 79 | mock.Mock.Test(t) 80 | 81 | t.Cleanup(func() { mock.AssertExpectations(t) }) 82 | 83 | return mock 84 | } 85 | -------------------------------------------------------------------------------- /pkg/types/mocks/VdpaDevice.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.53.3. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | types "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" 7 | mock "github.com/stretchr/testify/mock" 8 | ) 9 | 10 | // VdpaDevice is an autogenerated mock type for the VdpaDevice type 11 | type VdpaDevice struct { 12 | mock.Mock 13 | } 14 | 15 | // GetParent provides a mock function with no fields 16 | func (_m *VdpaDevice) GetParent() string { 17 | ret := _m.Called() 18 | 19 | if len(ret) == 0 { 20 | panic("no return value specified for GetParent") 21 | } 22 | 23 | var r0 string 24 | if rf, ok := ret.Get(0).(func() string); ok { 25 | r0 = rf() 26 | } else { 27 | r0 = ret.Get(0).(string) 28 | } 29 | 30 | return r0 31 | } 32 | 33 | // GetPath provides a mock function with no fields 34 | func (_m *VdpaDevice) GetPath() (string, error) { 35 | ret := _m.Called() 36 | 37 | if len(ret) == 0 { 38 | panic("no return value specified for GetPath") 39 | } 40 | 41 | var r0 string 42 | var r1 error 43 | if rf, ok := ret.Get(0).(func() (string, error)); ok { 44 | return rf() 45 | } 46 | if rf, ok := ret.Get(0).(func() string); ok { 47 | r0 = rf() 48 | } else { 49 | r0 = ret.Get(0).(string) 50 | } 51 | 52 | if rf, ok := ret.Get(1).(func() error); ok { 53 | r1 = rf() 54 | } else { 55 | r1 = ret.Error(1) 56 | } 57 | 58 | return r0, r1 59 | } 60 | 61 | // GetType provides a mock function with no fields 62 | func (_m *VdpaDevice) GetType() types.VdpaType { 63 | ret := _m.Called() 64 | 65 | if len(ret) == 0 { 66 | panic("no return value specified for GetType") 67 | } 68 | 69 | var r0 types.VdpaType 70 | if rf, ok := ret.Get(0).(func() types.VdpaType); ok { 71 | r0 = rf() 72 | } else { 73 | r0 = ret.Get(0).(types.VdpaType) 74 | } 75 | 76 | return r0 77 | } 78 | 79 | // NewVdpaDevice creates a new instance of VdpaDevice. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 80 | // The first argument is typically a *testing.T value. 81 | func NewVdpaDevice(t interface { 82 | mock.TestingT 83 | Cleanup(func()) 84 | }) *VdpaDevice { 85 | mock := &VdpaDevice{} 86 | mock.Mock.Test(t) 87 | 88 | t.Cleanup(func() { mock.AssertExpectations(t) }) 89 | 90 | return mock 91 | } 92 | -------------------------------------------------------------------------------- /pkg/utils/ddp.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "os/exec" 9 | "strings" 10 | ) 11 | 12 | // /* 13 | // Example output of DDP tool 14 | // $ ./ddptool -l -a -j -s 0000:02:00.0 15 | // { 16 | // "DDPInventory": { 17 | // "device": "1572", 18 | // "address": "0000:02:00.0", 19 | // "name": "enp2s0f0", 20 | // "display": "Intel(R) Ethernet Converged Network Adapter X710-4", 21 | // "DDPpackage": { 22 | // "track_id": "80000008", 23 | // "version": "1.0.3.0", 24 | // "name": "GTPv1-C/U IPv4/IPv6 payload" 25 | // } 26 | // } 27 | // } 28 | 29 | // */ 30 | 31 | // DDPInfo is the toplevel container of DDPInventory 32 | type DDPInfo struct { 33 | DDPInventory DDPInventory `json:"DDPInventory"` 34 | } 35 | 36 | // DDPInventory holds a device's DDP information 37 | type DDPInventory struct { 38 | Device string `json:"device"` 39 | Address string `json:"address"` 40 | Name string `json:"name"` 41 | Display string `json:"display"` 42 | DDPpackage DDPpackage `json:"DDPpackage"` 43 | } 44 | 45 | // DDPpackage holds information about DDP profile itself 46 | type DDPpackage struct { 47 | TrackID string `json:"track_id"` 48 | Version string `json:"version"` 49 | Name string `json:"name"` 50 | } 51 | 52 | // 8 is an exit code of ddptool when profile was not found 53 | const ddpNoDDPProfile = 8 54 | 55 | var ddpExecCommand = exec.Command 56 | 57 | // IsDDPToolSupportedByDevice checks if DDPTool can be used with device 58 | func IsDDPToolSupportedByDevice(dev string) bool { 59 | if _, err := GetDDPProfiles(dev); err != nil && !errors.Is(err, ErrProfileNameNotFound) { 60 | if exitError, ok := err.(*exec.ExitError); ok { 61 | if exitError.ExitCode() == ddpNoDDPProfile { 62 | return true 63 | } 64 | } 65 | 66 | return false 67 | } 68 | 69 | return true 70 | } 71 | 72 | // GetDDPProfiles returns running DDP profile name if available 73 | func GetDDPProfiles(dev string) (string, error) { 74 | var stdout bytes.Buffer 75 | cmd := ddpExecCommand("ddptool", "-l", "-a", "-j", "-s", dev) 76 | cmd.Stdout = &stdout 77 | if err := cmd.Run(); err != nil { 78 | if strings.Contains(err.Error(), ErrProfileNameNotFound.Error()) { 79 | return "", fmt.Errorf("error while getting DDP profiles: %w", ErrProfileNameNotFound) 80 | } 81 | return "", err 82 | } 83 | 84 | return getDDPNameFromStdout(stdout.Bytes()) 85 | } 86 | 87 | // ErrProfileNameNotFound error when DDPTool is supported, but package name is empty 88 | var ErrProfileNameNotFound = errors.New("DDP profile name not found") 89 | 90 | func getDDPNameFromStdout(in []byte) (string, error) { 91 | ddpInfo := &DDPInfo{} 92 | if err := json.Unmarshal(in, ddpInfo); err != nil { 93 | return "", err 94 | } 95 | 96 | if ddpInfo.DDPInventory.DDPpackage.Name == "" { 97 | return "", fmt.Errorf("DDP profile name not found") 98 | } 99 | 100 | return ddpInfo.DDPInventory.DDPpackage.Name, nil 101 | } 102 | -------------------------------------------------------------------------------- /pkg/utils/mocks/RdmaProvider.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.53.3. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // RdmaProvider is an autogenerated mock type for the RdmaProvider type 8 | type RdmaProvider struct { 9 | mock.Mock 10 | } 11 | 12 | // GetRdmaCharDevices provides a mock function with given fields: rdmaDeviceName 13 | func (_m *RdmaProvider) GetRdmaCharDevices(rdmaDeviceName string) []string { 14 | ret := _m.Called(rdmaDeviceName) 15 | 16 | if len(ret) == 0 { 17 | panic("no return value specified for GetRdmaCharDevices") 18 | } 19 | 20 | var r0 []string 21 | if rf, ok := ret.Get(0).(func(string) []string); ok { 22 | r0 = rf(rdmaDeviceName) 23 | } else { 24 | if ret.Get(0) != nil { 25 | r0 = ret.Get(0).([]string) 26 | } 27 | } 28 | 29 | return r0 30 | } 31 | 32 | // GetRdmaDevicesForAuxdev provides a mock function with given fields: deviceID 33 | func (_m *RdmaProvider) GetRdmaDevicesForAuxdev(deviceID string) []string { 34 | ret := _m.Called(deviceID) 35 | 36 | if len(ret) == 0 { 37 | panic("no return value specified for GetRdmaDevicesForAuxdev") 38 | } 39 | 40 | var r0 []string 41 | if rf, ok := ret.Get(0).(func(string) []string); ok { 42 | r0 = rf(deviceID) 43 | } else { 44 | if ret.Get(0) != nil { 45 | r0 = ret.Get(0).([]string) 46 | } 47 | } 48 | 49 | return r0 50 | } 51 | 52 | // GetRdmaDevicesForPcidev provides a mock function with given fields: pciAddr 53 | func (_m *RdmaProvider) GetRdmaDevicesForPcidev(pciAddr string) []string { 54 | ret := _m.Called(pciAddr) 55 | 56 | if len(ret) == 0 { 57 | panic("no return value specified for GetRdmaDevicesForPcidev") 58 | } 59 | 60 | var r0 []string 61 | if rf, ok := ret.Get(0).(func(string) []string); ok { 62 | r0 = rf(pciAddr) 63 | } else { 64 | if ret.Get(0) != nil { 65 | r0 = ret.Get(0).([]string) 66 | } 67 | } 68 | 69 | return r0 70 | } 71 | 72 | // NewRdmaProvider creates a new instance of RdmaProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 73 | // The first argument is typically a *testing.T value. 74 | func NewRdmaProvider(t interface { 75 | mock.TestingT 76 | Cleanup(func()) 77 | }) *RdmaProvider { 78 | mock := &RdmaProvider{} 79 | mock.Mock.Test(t) 80 | 81 | t.Cleanup(func() { mock.AssertExpectations(t) }) 82 | 83 | return mock 84 | } 85 | -------------------------------------------------------------------------------- /pkg/utils/mocks/VdpaProvider.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.53.3. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | kvdpa "github.com/k8snetworkplumbingwg/govdpa/pkg/kvdpa" 7 | mock "github.com/stretchr/testify/mock" 8 | ) 9 | 10 | // VdpaProvider is an autogenerated mock type for the VdpaProvider type 11 | type VdpaProvider struct { 12 | mock.Mock 13 | } 14 | 15 | // GetVdpaDeviceByPci provides a mock function with given fields: pciAddr 16 | func (_m *VdpaProvider) GetVdpaDeviceByPci(pciAddr string) (kvdpa.VdpaDevice, error) { 17 | ret := _m.Called(pciAddr) 18 | 19 | if len(ret) == 0 { 20 | panic("no return value specified for GetVdpaDeviceByPci") 21 | } 22 | 23 | var r0 kvdpa.VdpaDevice 24 | var r1 error 25 | if rf, ok := ret.Get(0).(func(string) (kvdpa.VdpaDevice, error)); ok { 26 | return rf(pciAddr) 27 | } 28 | if rf, ok := ret.Get(0).(func(string) kvdpa.VdpaDevice); ok { 29 | r0 = rf(pciAddr) 30 | } else { 31 | if ret.Get(0) != nil { 32 | r0 = ret.Get(0).(kvdpa.VdpaDevice) 33 | } 34 | } 35 | 36 | if rf, ok := ret.Get(1).(func(string) error); ok { 37 | r1 = rf(pciAddr) 38 | } else { 39 | r1 = ret.Error(1) 40 | } 41 | 42 | return r0, r1 43 | } 44 | 45 | // NewVdpaProvider creates a new instance of VdpaProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 46 | // The first argument is typically a *testing.T value. 47 | func NewVdpaProvider(t interface { 48 | mock.TestingT 49 | Cleanup(func()) 50 | }) *VdpaProvider { 51 | mock := &VdpaProvider{} 52 | mock.Mock.Test(t) 53 | 54 | t.Cleanup(func() { mock.AssertExpectations(t) }) 55 | 56 | return mock 57 | } 58 | -------------------------------------------------------------------------------- /pkg/utils/netlink_provider_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "errors" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | "github.com/stretchr/testify/mock" 9 | 10 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils/mocks" 11 | ) 12 | 13 | const ( 14 | fakeFwAppName = "fakeFwAppName" 15 | ) 16 | 17 | var errFakeNetlink = errors.New("fake netlink error") 18 | 19 | var _ = Describe("NetlinkProvider Functions", func() { 20 | var ( 21 | mockProvider *mocks.NetlinkProvider 22 | ) 23 | 24 | BeforeEach(func() { 25 | mockProvider = &mocks.NetlinkProvider{} 26 | SetNetlinkProviderInst(mockProvider) 27 | }) 28 | 29 | Describe("IsDevlinkDDPSupportedByDevice", func() { 30 | It("should return true when device is supported", func() { 31 | mockProvider. 32 | On("GetDevlinkGetDeviceInfoByNameAsMap", mock.AnythingOfType("string"), mock.AnythingOfType("string")). 33 | Return(map[string]string{fwAppNameKey: fakeFwAppName}, nil) 34 | 35 | result := IsDevlinkDDPSupportedByDevice("fakeDevice") 36 | Expect(result).To(BeTrue()) 37 | }) 38 | 39 | It("should return false when device is not supported", func() { 40 | mockProvider. 41 | On("GetDevlinkGetDeviceInfoByNameAsMap", mock.AnythingOfType("string"), mock.AnythingOfType("string")). 42 | Return(nil, errFakeNetlink) 43 | 44 | result := IsDevlinkDDPSupportedByDevice("fakeDevice") 45 | Expect(result).To(BeFalse()) 46 | }) 47 | }) 48 | 49 | Describe("DevlinkGetDDPProfiles", func() { 50 | It("should return DDP profiles when no error occurs", func() { 51 | mockProvider. 52 | On("GetDevlinkGetDeviceInfoByNameAsMap", pciBus, "fakeDevice"). 53 | Return(map[string]string{fwAppNameKey: fakeFwAppName}, nil) 54 | 55 | profile, err := DevlinkGetDDPProfiles("fakeDevice") 56 | Expect(err).ToNot(HaveOccurred()) 57 | Expect(profile).To(Equal(fakeFwAppName)) 58 | }) 59 | 60 | It("should return an error when fetching DDP profiles fails", func() { 61 | mockProvider. 62 | On("GetDevlinkGetDeviceInfoByNameAsMap", pciBus, "fakeDevice"). 63 | Return(nil, errFakeNetlink) 64 | 65 | profile, err := DevlinkGetDDPProfiles("fakeDevice") 66 | Expect(err).To(HaveOccurred()) 67 | Expect(profile).To(BeEmpty()) 68 | }) 69 | }) 70 | 71 | Describe("DevlinkGetDeviceInfoByNameAndKeys", func() { 72 | It("should return device info when key is found", func() { 73 | mockProvider. 74 | On("GetDevlinkGetDeviceInfoByNameAsMap", pciBus, "fakeDevice"). 75 | Return(map[string]string{fwAppNameKey: fakeFwAppName}, nil) 76 | 77 | info, err := DevlinkGetDeviceInfoByNameAndKeys("fakeDevice", []string{fwAppNameKey}) 78 | Expect(err).ToNot(HaveOccurred()) 79 | Expect(info[fwAppNameKey]).To(Equal(fakeFwAppName)) 80 | }) 81 | 82 | It("should return an error when key is not found", func() { 83 | mockProvider. 84 | On("GetDevlinkGetDeviceInfoByNameAsMap", pciBus, "fakeDevice"). 85 | Return(map[string]string{}, nil) 86 | 87 | info, err := DevlinkGetDeviceInfoByNameAndKeys("fakeDevice", []string{fwAppNameKey}) 88 | Expect(err).To(HaveOccurred()) 89 | Expect(info).To(BeNil()) 90 | }) 91 | 92 | It("should return an error when fetching device info fails", func() { 93 | mockProvider. 94 | On("GetDevlinkGetDeviceInfoByNameAsMap", pciBus, "fakeDevice"). 95 | Return(nil, errFakeNetlink) 96 | 97 | info, err := DevlinkGetDeviceInfoByNameAndKeys("fakeDevice", []string{fwAppNameKey}) 98 | Expect(err).To(HaveOccurred()) 99 | Expect(info).To(BeNil()) 100 | }) 101 | }) 102 | }) 103 | 104 | func init() { 105 | SetDefaultMockNetlinkProvider() 106 | } 107 | -------------------------------------------------------------------------------- /pkg/utils/rdma_provider.go: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package utils 19 | 20 | import ( 21 | "github.com/Mellanox/rdmamap" 22 | ) 23 | 24 | // RdmaProvider is a wrapper type over rdmamap library 25 | type RdmaProvider interface { 26 | GetRdmaDevicesForPcidev(pciAddr string) []string 27 | GetRdmaDevicesForAuxdev(deviceID string) []string 28 | GetRdmaCharDevices(rdmaDeviceName string) []string 29 | } 30 | 31 | type defaultRdmaProvider struct { 32 | } 33 | 34 | var rdmaProvider RdmaProvider = &defaultRdmaProvider{} 35 | 36 | // SetRdmaProviderInst method would be used by unit tests in other packages 37 | func SetRdmaProviderInst(inst RdmaProvider) { 38 | rdmaProvider = inst 39 | } 40 | 41 | // GetRdmaProvider will be invoked by functions in other packages that would need access to the vdpa library methods. 42 | func GetRdmaProvider() RdmaProvider { 43 | return rdmaProvider 44 | } 45 | 46 | func (defaultRdmaProvider) GetRdmaDevicesForPcidev(pciAddr string) []string { 47 | return rdmamap.GetRdmaDevicesForPcidev(pciAddr) 48 | } 49 | 50 | func (defaultRdmaProvider) GetRdmaDevicesForAuxdev(deviceID string) []string { 51 | return rdmamap.GetRdmaDevicesForAuxdev(deviceID) 52 | } 53 | 54 | func (defaultRdmaProvider) GetRdmaCharDevices(rdmaDeviceName string) []string { 55 | return rdmamap.GetRdmaCharDevices(rdmaDeviceName) 56 | } 57 | -------------------------------------------------------------------------------- /pkg/utils/sriovnet_provider.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/k8snetworkplumbingwg/sriovnet" 5 | ) 6 | 7 | // SriovnetProvider is a wrapper type over sriovnet library 8 | type SriovnetProvider interface { 9 | GetUplinkRepresentor(vfPciAddress string) (string, error) 10 | GetUplinkRepresentorFromAux(auxDev string) (string, error) 11 | GetPfPciFromAux(auxDev string) (string, error) 12 | GetSfIndexByAuxDev(auxDev string) (int, error) 13 | GetNetDevicesFromAux(auxDev string) ([]string, error) 14 | GetAuxNetDevicesFromPci(pciAddr string) ([]string, error) 15 | GetDefaultPKeyFromPci(pciAddr string) (string, error) 16 | } 17 | 18 | type defaultSriovnetProvider struct { 19 | } 20 | 21 | var sriovnetProvider SriovnetProvider = &defaultSriovnetProvider{} 22 | 23 | // SetSriovnetProviderInst method would be used by unit tests in other packages 24 | func SetSriovnetProviderInst(inst SriovnetProvider) { 25 | sriovnetProvider = inst 26 | } 27 | 28 | // GetSriovnetProvider will be invoked by functions in other packages that would need access to the sriovnet library methods. 29 | func GetSriovnetProvider() SriovnetProvider { 30 | return sriovnetProvider 31 | } 32 | 33 | func (defaultSriovnetProvider) GetUplinkRepresentor(vfPciAddress string) (string, error) { 34 | return sriovnet.GetUplinkRepresentor(vfPciAddress) 35 | } 36 | 37 | func (defaultSriovnetProvider) GetUplinkRepresentorFromAux(auxDev string) (string, error) { 38 | return sriovnet.GetUplinkRepresentorFromAux(auxDev) 39 | } 40 | 41 | func (defaultSriovnetProvider) GetPfPciFromAux(auxDev string) (string, error) { 42 | return sriovnet.GetPfPciFromAux(auxDev) 43 | } 44 | 45 | func (defaultSriovnetProvider) GetSfIndexByAuxDev(auxDev string) (int, error) { 46 | return sriovnet.GetSfIndexByAuxDev(auxDev) 47 | } 48 | 49 | func (defaultSriovnetProvider) GetNetDevicesFromAux(auxDev string) ([]string, error) { 50 | return sriovnet.GetNetDevicesFromAux(auxDev) 51 | } 52 | 53 | func (defaultSriovnetProvider) GetAuxNetDevicesFromPci(pciAddr string) ([]string, error) { 54 | return sriovnet.GetAuxNetDevicesFromPci(pciAddr) 55 | } 56 | 57 | func (defaultSriovnetProvider) GetDefaultPKeyFromPci(pciAddr string) (string, error) { 58 | return sriovnet.GetDefaultPKeyFromPci(pciAddr) 59 | } 60 | -------------------------------------------------------------------------------- /pkg/utils/testing.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path" 7 | "path/filepath" 8 | 9 | "github.com/stretchr/testify/mock" 10 | nl "github.com/vishvananda/netlink" 11 | 12 | "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils/mocks" 13 | ) 14 | 15 | // FakeFilesystem allows to setup isolated fake files structure used for the tests. 16 | type FakeFilesystem struct { 17 | RootDir string 18 | Dirs []string 19 | Files map[string][]byte 20 | Symlinks map[string]string 21 | } 22 | 23 | // Use function creates entire files structure and returns a function to tear it down. Example usage: defer fs.Use()() 24 | func (fs *FakeFilesystem) Use() func() { 25 | // create the new fake fs root dir in /tmp/sriov... 26 | tmpDir, err := os.MkdirTemp("", "sriov") 27 | if err != nil { 28 | panic(fmt.Errorf("error creating fake root dir: %s", err.Error())) 29 | } 30 | fs.RootDir = tmpDir 31 | 32 | for _, dir := range fs.Dirs { 33 | //nolint: mnd 34 | err := os.MkdirAll(path.Join(fs.RootDir, dir), 0755) 35 | if err != nil { 36 | panic(fmt.Errorf("error creating fake directory: %s", err.Error())) 37 | } 38 | } 39 | for filename, body := range fs.Files { 40 | //nolint: mnd 41 | err := os.WriteFile(path.Join(fs.RootDir, filename), body, 0600) 42 | if err != nil { 43 | panic(fmt.Errorf("error creating fake file: %s", err.Error())) 44 | } 45 | } 46 | //nolint: mnd 47 | err = os.MkdirAll(path.Join(fs.RootDir, "usr/share/hwdata"), 0755) 48 | if err != nil { 49 | panic(fmt.Errorf("error creating fake directory: %s", err.Error())) 50 | } 51 | //nolint: mnd 52 | err = os.MkdirAll(path.Join(fs.RootDir, "var/run/cdi"), 0755) 53 | if err != nil { 54 | panic(fmt.Errorf("error creating fake cdi directory: %s", err.Error())) 55 | } 56 | 57 | writePciIds(fs) 58 | 59 | for link, target := range fs.Symlinks { 60 | err = os.Symlink(target, path.Join(fs.RootDir, link)) 61 | if err != nil { 62 | panic(fmt.Errorf("error creating fake symlink: %s", err.Error())) 63 | } 64 | } 65 | 66 | sysBusPci = path.Join(fs.RootDir, "/sys/bus/pci/devices") 67 | sysBusAux = path.Join(fs.RootDir, "/sys/bus/auxiliary/devices") 68 | 69 | return func() { 70 | // remove temporary fake fs 71 | err := os.RemoveAll(fs.RootDir) 72 | if err != nil { 73 | panic(fmt.Errorf("error tearing down fake filesystem: %s", err.Error())) 74 | } 75 | } 76 | } 77 | 78 | // TODO: Remove writing pci.ids file once ghw is mocked 79 | // This is to fix the CI failure where ghw lib fails to 80 | // unzip pci.ids file downloaded from internet, 81 | // or we run inside a container where we don't have the pci ids file 82 | func writePciIds(fs *FakeFilesystem) { 83 | dir, err := os.Getwd() 84 | if err != nil { 85 | panic(fmt.Errorf("error getting working directory: %s", err.Error())) 86 | } 87 | 88 | //nolint: gocritic 89 | pciIdsPath := filepath.Join(dir, "../../pkg/utils/testdata/pci.ids") 90 | pciData, err := os.ReadFile(pciIdsPath) 91 | if err != nil { 92 | panic(fmt.Errorf("error reading testdata pci file working directory %s: %s", pciIdsPath, err.Error())) 93 | } 94 | 95 | //nolint: mnd 96 | err = os.WriteFile(path.Join(fs.RootDir, "usr/share/hwdata/pci.ids"), pciData, 0600) 97 | if err != nil { 98 | panic(fmt.Errorf("error creating fake file: %s", err.Error())) 99 | } 100 | } 101 | 102 | // SetDefaultMockNetlinkProvider sets a mocked instance of NetlinkProvider to be used by unit test in other packages 103 | func SetDefaultMockNetlinkProvider() { 104 | mockProvider := &mocks.NetlinkProvider{} 105 | 106 | mockProvider. 107 | On("GetLinkAttrs", mock.AnythingOfType("string")). 108 | Return(&nl.LinkAttrs{EncapType: "fakeLinkType"}, nil) 109 | mockProvider. 110 | On("GetDevLinkDeviceEswitchAttrs", mock.AnythingOfType("string")). 111 | Return(&nl.DevlinkDevEswitchAttr{Mode: "fakeMode"}, nil) 112 | mockProvider. 113 | On("GetIPv4RouteList", mock.AnythingOfType("string")). 114 | Return([]nl.Route{{Dst: nil}}, nil) 115 | mockProvider. 116 | On("GetDevlinkGetDeviceInfoByNameAsMap", mock.AnythingOfType("string")). 117 | Return(map[string]string{"someKey": "someValue"}, nil) 118 | SetNetlinkProviderInst(mockProvider) 119 | } 120 | -------------------------------------------------------------------------------- /pkg/utils/utils_suite_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestUtils(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Utils Suite") 13 | } 14 | -------------------------------------------------------------------------------- /pkg/utils/vdpa_provider.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/golang/glog" 7 | vdpa "github.com/k8snetworkplumbingwg/govdpa/pkg/kvdpa" 8 | ) 9 | 10 | // VdpaProvider is a wrapper type over go-vdpa library 11 | type VdpaProvider interface { 12 | GetVdpaDeviceByPci(pciAddr string) (vdpa.VdpaDevice, error) 13 | } 14 | 15 | type defaultVdpaProvider struct { 16 | } 17 | 18 | var vdpaProvider VdpaProvider = &defaultVdpaProvider{} 19 | 20 | // SetVdpaProviderInst method would be used by unit tests in other packages 21 | func SetVdpaProviderInst(inst VdpaProvider) { 22 | vdpaProvider = inst 23 | } 24 | 25 | // GetVdpaProvider will be invoked by functions in other packages that would need access to the vdpa library methods. 26 | func GetVdpaProvider() VdpaProvider { 27 | return vdpaProvider 28 | } 29 | 30 | func (defaultVdpaProvider) GetVdpaDeviceByPci(pciAddr string) (vdpa.VdpaDevice, error) { 31 | // the govdpa library requires the pci address to include the "pci/" prefix 32 | fullPciAddr := "pci/" + pciAddr 33 | vdpaDevices, err := vdpa.GetVdpaDevicesByPciAddress(fullPciAddr) 34 | if err != nil { 35 | return nil, err 36 | } 37 | numVdpaDevices := len(vdpaDevices) 38 | if numVdpaDevices == 0 { 39 | return nil, fmt.Errorf("no vdpa device associated to pciAddress %s", pciAddr) 40 | } 41 | if numVdpaDevices > 1 { 42 | glog.Infof("More than one vDPA device found for pciAddress %s, returning the first one", pciAddr) 43 | } 44 | return vdpaDevices[0], nil 45 | } 46 | --------------------------------------------------------------------------------