├── .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 |
--------------------------------------------------------------------------------