Skip to content

Commit 88346b1

Browse files
Add dpucniprovisioner source code and complete OVN-Kubernetes extraction
- Add cmd/dpucniprovisioner/main.go with Linux build constraint - Add internal/cniprovisioner/dpu/ with provisioner logic - Add internal/utils/ovsclient/ with OVS client wrapper - Add internal/constants/ for shared constants - Update import paths to ovn-kubernetes-components
1 parent cd9b5cc commit 88346b1

File tree

14 files changed

+3293
-11
lines changed

14 files changed

+3293
-11
lines changed
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
ovnkubernetesresourceinjector
2-
ipallocator
1+
/ovnkubernetesresourceinjector
2+
/ipallocator
3+
/dpucniprovisioner

ovn-kubernetes-components/Dockerfile

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
2424
-gcflags="${gcflags}" \
2525
-o ipallocator ./cmd/ipallocator
2626

27-
# RUN --mount=type=cache,target=/root/.cache/go-build \
28-
# --mount=type=cache,target=/go/pkg/mod \
29-
# CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
30-
# go build -trimpath \
31-
# -ldflags="${ldflags}" \
32-
# -gcflags="${gcflags}" \
33-
# -o dpucniprovisioner ./cmd/dpucniprovisioner
27+
RUN --mount=type=cache,target=/root/.cache/go-build \
28+
--mount=type=cache,target=/go/pkg/mod \
29+
CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
30+
go build -trimpath \
31+
-ldflags="${ldflags}" \
32+
-gcflags="${gcflags}" \
33+
-o dpucniprovisioner ./cmd/dpucniprovisioner
3434

3535
RUN --mount=type=cache,target=/root/.cache/go-build \
3636
--mount=type=cache,target=/go/pkg/mod \
@@ -79,7 +79,7 @@ RUN mkdir -p /var/run/openvswitch
7979

8080
RUN mkdir -p /usr/libexec/cni/
8181
COPY --from=builder /workspace/ipallocator /ipallocator
82-
# COPY --from=builder /workspace/dpucniprovisioner /cniprovisioner
82+
COPY --from=builder /workspace/dpucniprovisioner /cniprovisioner
8383
COPY --from=builder /workspace/ovnkubernetesresourceinjector /ovnkubernetesresourceinjector
8484

8585
# Get all the source code
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
//go:build linux
2+
3+
/*
4+
Copyright 2024 NVIDIA
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
*/
18+
19+
package main
20+
21+
import (
22+
"context"
23+
"encoding/json"
24+
"errors"
25+
"fmt"
26+
"net"
27+
"os"
28+
"os/signal"
29+
"strconv"
30+
"sync"
31+
32+
"github.com/nvidia/doca-platform/pkg/ipallocator"
33+
"github.com/nvidia/doca-platform/pkg/utils/networkhelper"
34+
dpucniprovisioner "github.com/nvidia/ovn-kubernetes-components/internal/cniprovisioner/dpu"
35+
"github.com/nvidia/ovn-kubernetes-components/internal/readyz"
36+
"github.com/nvidia/ovn-kubernetes-components/internal/utils/ovsclient"
37+
38+
"github.com/vishvananda/netlink"
39+
"k8s.io/client-go/kubernetes"
40+
"k8s.io/klog/v2"
41+
"k8s.io/utils/clock"
42+
kexec "k8s.io/utils/exec"
43+
"sigs.k8s.io/controller-runtime/pkg/client/config"
44+
)
45+
46+
const (
47+
// vtepIPAllocationFilePath is the path to the file that contains the VTEP IP allocation done by the IP Allocator.
48+
// We should ensure that the IP Allocation request name is vtep to have this file created correctly.
49+
vtepIPAllocationFilePath = "/tmp/ips/vtep"
50+
// pfIPAllocationFilePath is the path to the file that contains the PF IP allocation done by the IP Allocator.
51+
// We should ensure that the IP Allocation request name is pf to have this file created correctly.
52+
pfIPAllocationFilePath = "/tmp/ips/pf"
53+
)
54+
55+
func main() {
56+
if len(os.Args) != 2 {
57+
klog.Fatal("expecting mode to be specified via args")
58+
}
59+
60+
modeRaw := os.Args[1]
61+
mode, err := parseMode(modeRaw)
62+
if err != nil {
63+
klog.Fatalf("error while parsing mode: %s", err.Error())
64+
}
65+
66+
klog.Info("Starting DPU CNI Provisioner")
67+
68+
node := os.Getenv("NODE_NAME")
69+
if node == "" {
70+
klog.Fatal("NODE_NAME environment variable is not found. This is supposed to be configured via Kubernetes Downward API in production")
71+
}
72+
73+
var vtepIPNet *net.IPNet
74+
var gateway net.IP
75+
var pfIPNet *net.IPNet
76+
var vtepCIDR *net.IPNet
77+
var ovnMTU int
78+
var gatewayDiscoveryNetwork *net.IPNet
79+
if mode == dpucniprovisioner.InternalIPAM {
80+
vtepIPNet, gateway, err = getInfoFromVTEPIPAllocation()
81+
if err != nil {
82+
klog.Fatalf("error while parsing info from the VTEP IP allocation file: %s", err.Error())
83+
}
84+
85+
pfIPNet, err = getPFIP()
86+
if err != nil {
87+
klog.Fatalf("error while the PF IP from the allocation file: %s", err.Error())
88+
}
89+
90+
ovnMTU, err = getOVNMTU()
91+
if err != nil {
92+
klog.Fatalf("error while parsing MTU %s", err.Error())
93+
}
94+
} else {
95+
gatewayDiscoveryNetwork, err = getGatewayDiscoveryNetwork()
96+
if err != nil {
97+
klog.Fatalf("error while parsing the Gateway Discovery Network: %s", err.Error())
98+
}
99+
}
100+
101+
vtepCIDR, err = getVTEPCIDR()
102+
if err != nil {
103+
klog.Fatalf("error while parsing VTEP CIDR: %s", err.Error())
104+
}
105+
106+
hostCIDR, err := getHostCIDR()
107+
if err != nil {
108+
klog.Fatalf("error while parsing Host CIDR %s", err.Error())
109+
}
110+
111+
exec := kexec.New()
112+
113+
ovsClient, err := ovsclient.New(exec)
114+
if err != nil {
115+
klog.Fatal(err)
116+
}
117+
118+
ctx, cancel := context.WithCancel(context.Background())
119+
c := clock.RealClock{}
120+
121+
config, err := config.GetConfig()
122+
if err != nil {
123+
klog.Fatal(err)
124+
}
125+
clientset, err := kubernetes.NewForConfig(config)
126+
if err != nil {
127+
klog.Fatal(err)
128+
}
129+
130+
provisioner := dpucniprovisioner.New(ctx, mode, c, ovsClient, networkhelper.New(), exec, clientset, vtepIPNet, gateway, vtepCIDR, hostCIDR, pfIPNet, node, gatewayDiscoveryNetwork, ovnMTU)
131+
132+
err = provisioner.RunOnce()
133+
if err != nil {
134+
klog.Fatal(err)
135+
}
136+
137+
var wg sync.WaitGroup
138+
wg.Add(1)
139+
go func() {
140+
defer wg.Done()
141+
provisioner.EnsureConfiguration()
142+
}()
143+
144+
err = readyz.ReportReady()
145+
if err != nil {
146+
klog.Fatal(err)
147+
}
148+
149+
klog.Info("DPU CNI Provisioner is ready")
150+
151+
ch := make(chan os.Signal, 1)
152+
signal.Notify(ch, os.Interrupt)
153+
<-ch
154+
klog.Info("Received termination signal, terminating.")
155+
cancel()
156+
provisioner.Stop()
157+
wg.Wait()
158+
}
159+
160+
// getInfoFromVTEPIPAllocation returns the VTEP IP and gateway from a file that contains the VTEP IP allocation done
161+
// by the IP Allocator component.
162+
func getInfoFromVTEPIPAllocation() (*net.IPNet, net.IP, error) {
163+
content, err := os.ReadFile(vtepIPAllocationFilePath)
164+
if err != nil {
165+
return nil, nil, fmt.Errorf("error while reading file %s: %w", vtepIPAllocationFilePath, err)
166+
}
167+
168+
results := []ipallocator.NVIPAMIPAllocatorResult{}
169+
if err := json.Unmarshal(content, &results); err != nil {
170+
return nil, nil, fmt.Errorf("error while unmarshalling IP Allocator results: %w", err)
171+
}
172+
173+
if len(results) != 1 {
174+
return nil, nil, fmt.Errorf("expecting exactly 1 IP allocation for VTEP")
175+
}
176+
177+
vtepIPRaw := results[0].IP
178+
vtepIP, err := netlink.ParseIPNet(vtepIPRaw)
179+
if err != nil {
180+
return nil, nil, fmt.Errorf("error while parsing VTEP IP to net.IPNet: %w", err)
181+
}
182+
183+
gatewayRaw := results[0].Gateway
184+
gateway := net.ParseIP(gatewayRaw)
185+
if gateway == nil {
186+
return nil, nil, errors.New("error while parsing Gateway IP to net.IP: input is not valid")
187+
}
188+
189+
return vtepIP, gateway, nil
190+
}
191+
192+
// getPFIP() returns the PF IP from a file that contains the PF IP allocation done by the IP Allocator
193+
// component.
194+
func getPFIP() (*net.IPNet, error) {
195+
content, err := os.ReadFile(pfIPAllocationFilePath)
196+
if err != nil {
197+
return nil, fmt.Errorf("error while reading file %s: %w", vtepIPAllocationFilePath, err)
198+
}
199+
200+
results := []ipallocator.NVIPAMIPAllocatorResult{}
201+
if err := json.Unmarshal(content, &results); err != nil {
202+
return nil, fmt.Errorf("error while unmarshalling IP Allocator results: %w", err)
203+
}
204+
205+
if len(results) != 1 {
206+
return nil, fmt.Errorf("expecting exactly 1 IP allocation for PF")
207+
}
208+
209+
pfIPRaw := results[0].IP
210+
pfIP, err := netlink.ParseIPNet(pfIPRaw)
211+
if err != nil {
212+
return nil, fmt.Errorf("error while parsing PF IP to net.IPNet: %w", err)
213+
}
214+
215+
return pfIP, nil
216+
}
217+
218+
// getVTEPCIDR returns the VTEP CIDR to be used by the provisioner
219+
func getVTEPCIDR() (*net.IPNet, error) {
220+
vtepCIDRRaw := os.Getenv("VTEP_CIDR")
221+
if vtepCIDRRaw == "" {
222+
return nil, errors.New("required VTEP_CIDR environment variable is not set")
223+
}
224+
225+
_, vtepCIDR, err := net.ParseCIDR(vtepCIDRRaw)
226+
if err != nil {
227+
klog.Fatalf("error while parsing VTEP CIDR %s as net.IPNet: %s", vtepCIDRRaw, err.Error())
228+
}
229+
230+
return vtepCIDR, nil
231+
}
232+
233+
// getHostCIDR returns the Host CIDR to be used by the provisioner
234+
func getHostCIDR() (*net.IPNet, error) {
235+
hostCIDRRaw := os.Getenv("HOST_CIDR")
236+
if hostCIDRRaw == "" {
237+
return nil, errors.New("required HOST_CIDR environment variable is not set")
238+
}
239+
240+
_, hostCIDR, err := net.ParseCIDR(hostCIDRRaw)
241+
if err != nil {
242+
klog.Fatalf("error while parsing Host CIDR %s as net.IPNet: %s", hostCIDRRaw, err.Error())
243+
}
244+
245+
return hostCIDR, nil
246+
}
247+
248+
// getGatewayDiscoveryNetwork returns the Network to be used by the provisioner to discover the gateway
249+
func getGatewayDiscoveryNetwork() (*net.IPNet, error) {
250+
gatewayDiscoveryNetworkRaw := os.Getenv("GATEWAY_DISCOVERY_NETWORK")
251+
if gatewayDiscoveryNetworkRaw == "" {
252+
return nil, errors.New("required GATEWAY_DISCOVERY_NETWORK environment variable is not set")
253+
}
254+
255+
_, gatewayDiscoveryNetwork, err := net.ParseCIDR(gatewayDiscoveryNetworkRaw)
256+
if err != nil {
257+
klog.Fatalf("error while parsing Gateway Discovery Network %s as net.IPNet: %s", gatewayDiscoveryNetwork, err.Error())
258+
}
259+
260+
return gatewayDiscoveryNetwork, nil
261+
}
262+
263+
// getOVNMTU returns the PF MTU to be used by the provisioner
264+
func getOVNMTU() (int, error) {
265+
mtuString := os.Getenv("OVN_MTU")
266+
if mtuString == "" {
267+
return 0, errors.New("required OVN_MTU environment variable is not set")
268+
}
269+
270+
mtu, err := strconv.Atoi(mtuString)
271+
if err != nil {
272+
return 0, fmt.Errorf("parse environment variable OVN_MTU %s as int: %v", mtuString, err)
273+
}
274+
275+
if mtu == 0 {
276+
return 0, errors.New("invalid OVN_MTU value: 0")
277+
}
278+
279+
return mtu, nil
280+
}
281+
282+
// parseMode parses the mode in which the binary should be started
283+
func parseMode(mode string) (dpucniprovisioner.Mode, error) {
284+
m := map[dpucniprovisioner.Mode]struct{}{
285+
dpucniprovisioner.InternalIPAM: {},
286+
dpucniprovisioner.ExternalIPAM: {},
287+
}
288+
modeTyped := dpucniprovisioner.Mode(mode)
289+
if _, ok := m[modeTyped]; !ok {
290+
return "", errors.New("unknown mode")
291+
}
292+
293+
return modeTyped, nil
294+
}

ovn-kubernetes-components/go.mod

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ go 1.24.0
55
require (
66
github.com/containernetworking/cni v1.2.3
77
github.com/nvidia/doca-platform v0.0.0-20251016072527-fc9761393d62
8+
github.com/onsi/ginkgo/v2 v2.23.4
89
github.com/onsi/gomega v1.36.3
10+
github.com/vishvananda/netlink v1.3.0
11+
go.uber.org/mock v0.5.0
912
k8s.io/api v0.33.0
1013
k8s.io/apimachinery v0.33.0
1114
k8s.io/client-go v0.33.0
1215
k8s.io/klog/v2 v2.130.1
16+
k8s.io/utils v0.0.0-20241210054802-24370beab758
1317
sigs.k8s.io/controller-runtime v0.21.0
1418
)
1519

@@ -26,10 +30,12 @@ require (
2630
github.com/go-openapi/jsonpointer v0.21.0 // indirect
2731
github.com/go-openapi/jsonreference v0.21.0 // indirect
2832
github.com/go-openapi/swag v0.23.0 // indirect
33+
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
2934
github.com/gogo/protobuf v1.3.2 // indirect
3035
github.com/google/btree v1.1.3 // indirect
3136
github.com/google/gnostic-models v0.6.9 // indirect
3237
github.com/google/go-cmp v0.7.0 // indirect
38+
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
3339
github.com/google/uuid v1.6.0 // indirect
3440
github.com/josharian/intern v1.0.0 // indirect
3541
github.com/json-iterator/go v1.1.12 // indirect
@@ -43,7 +49,9 @@ require (
4349
github.com/prometheus/common v0.62.0 // indirect
4450
github.com/prometheus/procfs v0.15.1 // indirect
4551
github.com/spf13/pflag v1.0.6-0.20201009195203-85dd5c8bc61c // indirect
52+
github.com/vishvananda/netns v0.0.5 // indirect
4653
github.com/x448/float16 v0.8.4 // indirect
54+
go.uber.org/automaxprocs v1.6.0 // indirect
4755
go.uber.org/multierr v1.11.0 // indirect
4856
go.uber.org/zap v1.27.0 // indirect
4957
golang.org/x/net v0.38.0 // indirect
@@ -53,14 +61,14 @@ require (
5361
golang.org/x/term v0.30.0 // indirect
5462
golang.org/x/text v0.23.0 // indirect
5563
golang.org/x/time v0.9.0 // indirect
64+
golang.org/x/tools v0.31.0 // indirect
5665
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
5766
google.golang.org/protobuf v1.36.5 // indirect
5867
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
5968
gopkg.in/inf.v0 v0.9.1 // indirect
6069
gopkg.in/yaml.v3 v3.0.1 // indirect
6170
k8s.io/apiextensions-apiserver v0.33.0 // indirect
6271
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
63-
k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
6472
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
6573
sigs.k8s.io/randfill v1.0.0 // indirect
6674
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect

0 commit comments

Comments
 (0)