From 4def4287ab970e1a008243e8c8e90802f4797d76 Mon Sep 17 00:00:00 2001 From: Yury Kulazhenkov Date: Fri, 7 Jun 2024 15:53:56 +0300 Subject: [PATCH 1/5] Add support for static IP arguments on CNI level Supported options: STDIN runtimeConfig[ips] STDIN args[cni][ips] CNI_ARGS env variable, IP field Signed-off-by: Yury Kulazhenkov --- pkg/cni/plugin/plugin.go | 42 +++--------- pkg/cni/plugin/plugin_test.go | 14 +++- pkg/cni/types/mocks/ConfLoader.go | 34 +++++----- pkg/cni/types/types.go | 103 +++++++++++++++++++++++++++--- pkg/cni/types/types_test.go | 68 ++++++++++++++++++-- 5 files changed, 195 insertions(+), 66 deletions(-) diff --git a/pkg/cni/plugin/plugin.go b/pkg/cni/plugin/plugin.go index fa791a4..9cd52a7 100644 --- a/pkg/cni/plugin/plugin.go +++ b/pkg/cni/plugin/plugin.go @@ -126,7 +126,7 @@ func (p *Plugin) prepareCMD(args *skel.CmdArgs) (cmdContext, error) { c cmdContext err error ) - c.Config, err = p.ConfLoader.LoadConf(args.StdinData) + c.Config, err = p.ConfLoader.LoadConf(args) if err != nil { return cmdContext{}, fmt.Errorf("failed to load config. %v", err) } @@ -136,10 +136,8 @@ func (p *Plugin) prepareCMD(args *skel.CmdArgs) (cmdContext, error) { if err != nil { return cmdContext{}, fmt.Errorf("failed to connect to IPAM daemon: %v", err) } - c.ReqParams, err = cniConfToGRPCReq(c.Config, args) - if err != nil { - return cmdContext{}, fmt.Errorf("failed to convert CNI parameters to GRPC request: %v", err) - } + c.ReqParams = cniConfToGRPCReq(c.Config, args) + return c, nil } @@ -192,13 +190,7 @@ func grpcRespToResult(resp *nodev1.AllocateResponse) (*current.Result, error) { return result, nil } -func cniConfToGRPCReq(conf *types.NetConf, args *skel.CmdArgs) (*nodev1.IPAMParameters, error) { - cniExtraArgs := &kubernetesCNIArgs{} - err := cnitypes.LoadArgs(args.Args, cniExtraArgs) - if err != nil { - return nil, fmt.Errorf("failed to load extra CNI args: %v", err) - } - +func cniConfToGRPCReq(conf *types.NetConf, args *skel.CmdArgs) *nodev1.IPAMParameters { poolType := nodev1.PoolType_POOL_TYPE_IPPOOL if conf.IPAM.PoolType == common.PoolTypeCIDRPool { poolType = nodev1.PoolType_POOL_TYPE_CIDRPOOL @@ -209,23 +201,16 @@ func cniConfToGRPCReq(conf *types.NetConf, args *skel.CmdArgs) (*nodev1.IPAMPara CniIfname: args.IfName, CniContainerid: args.ContainerID, Metadata: &nodev1.IPAMMetadata{ - K8SPodName: string(cniExtraArgs.K8S_POD_NAME), - K8SPodNamespace: string(cniExtraArgs.K8S_POD_NAMESPACE), - K8SPodUid: string(cniExtraArgs.K8S_POD_UID), + K8SPodName: conf.IPAM.K8SMetadata.PodName, + K8SPodNamespace: conf.IPAM.K8SMetadata.PodNamespace, + K8SPodUid: conf.IPAM.K8SMetadata.PodUID, DeviceId: conf.DeviceID, }, } - - if req.Metadata.K8SPodName == "" { - return nil, log.Errorf("CNI_ARGS: K8S_POD_NAME is not provided by container runtime") - } - if req.Metadata.K8SPodNamespace == "" { - return nil, log.Errorf("CNI_ARGS: K8S_POD_NAMESPACE is not provided by container runtime") - } if req.Metadata.K8SPodUid == "" { - log.Warningf("CNI_ARGS: K8S_POD_UID is not provided by container runtime") + log.Warningf("K8S_POD_UID is not provided by container runtime") } - return req, nil + return req } // default NewGRPCClientFunc, initializes insecure GRPC connection to provided daemon socket @@ -236,12 +221,3 @@ func defaultNewGRPCClientFunc(daemonSocket string) (GRPCClient, error) { } return nodev1.NewIPAMServiceClient(conn), nil } - -// kubernetesCNIArgs is the container for extra CNI Args which set by container runtimes -// in Kubernetes -type kubernetesCNIArgs struct { - cnitypes.CommonArgs - K8S_POD_NAME cnitypes.UnmarshallableString //nolint - K8S_POD_NAMESPACE cnitypes.UnmarshallableString //nolint - K8S_POD_UID cnitypes.UnmarshallableString //nolint -} diff --git a/pkg/cni/plugin/plugin_test.go b/pkg/cni/plugin/plugin_test.go index fa49bf9..cda9405 100644 --- a/pkg/cni/plugin/plugin_test.go +++ b/pkg/cni/plugin/plugin_test.go @@ -66,6 +66,14 @@ var _ = Describe("plugin tests", func() { Pools: []string{"my-pool"}, LogFile: path.Join(tmpDir, "nv-ipam.log"), LogLevel: "debug", + K8SMetadata: struct { + PodName string + PodNamespace string + PodUID string + }{ + PodName: "test", + PodNamespace: "test", + }, }, } @@ -80,7 +88,7 @@ var _ = Describe("plugin tests", func() { Context("CmdAdd()", func() { It("executes successfully", func() { - mockConfLoader.On("LoadConf", args.StdinData).Return(testConf, nil) + mockConfLoader.On("LoadConf", args).Return(testConf, nil) mockDaemonClient.On("Allocate", mock.Anything, &nodev1.AllocateRequest{ Parameters: &nodev1.IPAMParameters{ Pools: []string{"my-pool"}, @@ -106,7 +114,7 @@ var _ = Describe("plugin tests", func() { Context("CmdDel()", func() { It("executes successfully", func() { - mockConfLoader.On("LoadConf", args.StdinData).Return(testConf, nil) + mockConfLoader.On("LoadConf", args).Return(testConf, nil) mockDaemonClient.On("Deallocate", mock.Anything, mock.Anything).Return(nil, nil) err := p.CmdDel(args) Expect(err).ToNot(HaveOccurred()) @@ -115,7 +123,7 @@ var _ = Describe("plugin tests", func() { Context("CmdCheck()", func() { It("executes successfully", func() { - mockConfLoader.On("LoadConf", args.StdinData).Return(testConf, nil) + mockConfLoader.On("LoadConf", args).Return(testConf, nil) mockDaemonClient.On("IsAllocated", mock.Anything, mock.Anything).Return(nil, nil) err := p.CmdCheck(args) Expect(err).ToNot(HaveOccurred()) diff --git a/pkg/cni/types/mocks/ConfLoader.go b/pkg/cni/types/mocks/ConfLoader.go index 4873481..f19e686 100644 --- a/pkg/cni/types/mocks/ConfLoader.go +++ b/pkg/cni/types/mocks/ConfLoader.go @@ -3,8 +3,10 @@ package mocks import ( - types "github.com/Mellanox/nvidia-k8s-ipam/pkg/cni/types" + skel "github.com/containernetworking/cni/pkg/skel" mock "github.com/stretchr/testify/mock" + + types "github.com/Mellanox/nvidia-k8s-ipam/pkg/cni/types" ) // ConfLoader is an autogenerated mock type for the ConfLoader type @@ -20,25 +22,25 @@ func (_m *ConfLoader) EXPECT() *ConfLoader_Expecter { return &ConfLoader_Expecter{mock: &_m.Mock} } -// LoadConf provides a mock function with given fields: bytes -func (_m *ConfLoader) LoadConf(bytes []byte) (*types.NetConf, error) { - ret := _m.Called(bytes) +// LoadConf provides a mock function with given fields: args +func (_m *ConfLoader) LoadConf(args *skel.CmdArgs) (*types.NetConf, error) { + ret := _m.Called(args) var r0 *types.NetConf var r1 error - if rf, ok := ret.Get(0).(func([]byte) (*types.NetConf, error)); ok { - return rf(bytes) + if rf, ok := ret.Get(0).(func(*skel.CmdArgs) (*types.NetConf, error)); ok { + return rf(args) } - if rf, ok := ret.Get(0).(func([]byte) *types.NetConf); ok { - r0 = rf(bytes) + if rf, ok := ret.Get(0).(func(*skel.CmdArgs) *types.NetConf); ok { + r0 = rf(args) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*types.NetConf) } } - if rf, ok := ret.Get(1).(func([]byte) error); ok { - r1 = rf(bytes) + if rf, ok := ret.Get(1).(func(*skel.CmdArgs) error); ok { + r1 = rf(args) } else { r1 = ret.Error(1) } @@ -52,14 +54,14 @@ type ConfLoader_LoadConf_Call struct { } // LoadConf is a helper method to define mock.On call -// - bytes []byte -func (_e *ConfLoader_Expecter) LoadConf(bytes interface{}) *ConfLoader_LoadConf_Call { - return &ConfLoader_LoadConf_Call{Call: _e.mock.On("LoadConf", bytes)} +// - args *skel.CmdArgs +func (_e *ConfLoader_Expecter) LoadConf(args interface{}) *ConfLoader_LoadConf_Call { + return &ConfLoader_LoadConf_Call{Call: _e.mock.On("LoadConf", args)} } -func (_c *ConfLoader_LoadConf_Call) Run(run func(bytes []byte)) *ConfLoader_LoadConf_Call { +func (_c *ConfLoader_LoadConf_Call) Run(run func(args *skel.CmdArgs)) *ConfLoader_LoadConf_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].([]byte)) + run(args[0].(*skel.CmdArgs)) }) return _c } @@ -69,7 +71,7 @@ func (_c *ConfLoader_LoadConf_Call) Return(_a0 *types.NetConf, _a1 error) *ConfL return _c } -func (_c *ConfLoader_LoadConf_Call) RunAndReturn(run func([]byte) (*types.NetConf, error)) *ConfLoader_LoadConf_Call { +func (_c *ConfLoader_LoadConf_Call) RunAndReturn(run func(*skel.CmdArgs) (*types.NetConf, error)) *ConfLoader_LoadConf_Call { _c.Call.Return(run) return _c } diff --git a/pkg/cni/types/types.go b/pkg/cni/types/types.go index 7024df0..b4614da 100644 --- a/pkg/cni/types/types.go +++ b/pkg/cni/types/types.go @@ -16,10 +16,12 @@ package types import ( "encoding/json" "fmt" + "net" "os" "path/filepath" "strings" + "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" "github.com/Mellanox/nvidia-k8s-ipam/pkg/common" @@ -44,8 +46,8 @@ const ( // //go:generate mockery --name ConfLoader type ConfLoader interface { - // LoadConf loads CNI configuration from Json data - LoadConf(bytes []byte) (*NetConf, error) + // LoadConf loads configuration from CNI CmdArgs + LoadConf(args *skel.CmdArgs) (*NetConf, error) } // IPAMConf is the configuration supported by our CNI plugin @@ -65,15 +67,44 @@ type IPAMConf struct { LogLevel string `json:"logLevel,omitempty"` // internal fields + // holds processed data from poolName field Pools []string `json:"-"` + // k8s metadata parsed from CNI_ARGS + K8SMetadata struct { + PodName string + PodNamespace string + PodUID string + } `json:"-"` + // requested IPs from CNI_ARGS, args and capabilities + RequestedIPs []net.IP `json:"-"` } // NetConf is CNI network config type NetConf struct { - Name string `json:"name"` - CNIVersion string `json:"cniVersion"` - IPAM *IPAMConf `json:"ipam"` - DeviceID string `json:"deviceID"` + Name string `json:"name"` + CNIVersion string `json:"cniVersion"` + IPAM *IPAMConf `json:"ipam"` + DeviceID string `json:"deviceID"` + RuntimeConfig struct { + IPs []string `json:"ips,omitempty"` + } `json:"runtimeConfig,omitempty"` + Args *struct { + ArgsCNI *IPAMArgs `json:"cni"` + } `json:"args"` +} + +// IPAMArgs holds arguments from stdin args["cni"] +type IPAMArgs struct { + IPs []string `json:"ips"` +} + +// IPAMEnvArgs holds arguments from CNI_ARGS env variable +type IPAMEnvArgs struct { + types.CommonArgs + IP types.UnmarshallableString + K8S_POD_NAME types.UnmarshallableString //nolint + K8S_POD_NAMESPACE types.UnmarshallableString //nolint + K8S_POD_UID types.UnmarshallableString //nolint } type confLoader struct{} @@ -83,10 +114,10 @@ func NewConfLoader() ConfLoader { } // LoadConf Loads NetConf from json string provided as []byte -func (cl *confLoader) LoadConf(bytes []byte) (*NetConf, error) { +func (cl *confLoader) LoadConf(args *skel.CmdArgs) (*NetConf, error) { n := &NetConf{} - if err := json.Unmarshal(bytes, &n); err != nil { + if err := json.Unmarshal(args.StdinData, &n); err != nil { return nil, fmt.Errorf("failed to unmarshal configurations. %w", err) } @@ -120,6 +151,26 @@ func (cl *confLoader) LoadConf(bytes []byte) (*NetConf, error) { } cl.overlayConf(defaultConf, n.IPAM) + // static IP address priority: + // stdin runtimeConfig["ips"] > stdin args["cni"]["ips"] > IP argument from CNI_ARGS env variable + requestedIPs := n.RuntimeConfig.IPs + if len(requestedIPs) == 0 { + if n.Args != nil && n.Args.ArgsCNI != nil { + requestedIPs = n.Args.ArgsCNI.IPs + } + } + for _, v := range requestedIPs { + ip := parseIP(v) + if ip == nil { + return nil, fmt.Errorf("static IP request contains invalid IP address") + } + n.IPAM.RequestedIPs = append(n.IPAM.RequestedIPs, ip) + } + + if err := cl.loadEnvCNIArgs(n, args); err != nil { + return nil, err + } + n.IPAM.Pools, err = parsePoolName(n.IPAM.PoolName) if err != nil { return nil, err @@ -130,10 +181,44 @@ func (cl *confLoader) LoadConf(bytes []byte) (*NetConf, error) { return nil, fmt.Errorf("unsupported poolType %s, supported values: %s, %s", n.IPAM.PoolType, common.PoolTypeIPPool, common.PoolTypeCIDRPool) } - return n, nil } +// loads arguments from CNI_ARGS env variable +func (cl *confLoader) loadEnvCNIArgs(conf *NetConf, args *skel.CmdArgs) error { + envArgs := &IPAMEnvArgs{} + err := types.LoadArgs(args.Args, envArgs) + if err != nil { + return err + } + if envArgs.K8S_POD_NAME == "" { + return fmt.Errorf("CNI_ARGS: K8S_POD_NAME is not provided by container runtime") + } + if envArgs.K8S_POD_NAMESPACE == "" { + return fmt.Errorf("CNI_ARGS: K8S_POD_NAMESPACE is not provided by container runtime") + } + + conf.IPAM.K8SMetadata.PodName = string(envArgs.K8S_POD_NAME) + conf.IPAM.K8SMetadata.PodNamespace = string(envArgs.K8S_POD_NAMESPACE) + conf.IPAM.K8SMetadata.PodUID = string(envArgs.K8S_POD_UID) + + // use IP argument from CNI_ARGS env only if IPs are not configured by other methods + if len(conf.IPAM.RequestedIPs) > 0 || envArgs.IP == "" { + return nil + } + parsedIP := parseIP(string(envArgs.IP)) + if parsedIP == nil { + return fmt.Errorf("CNI_ARGS: IP argument contains invalid IP address") + } + conf.IPAM.RequestedIPs = append(conf.IPAM.RequestedIPs, parsedIP) + return nil +} + +func parseIP(s string) net.IP { + s, _, _ = strings.Cut(s, "/") + return net.ParseIP(s) +} + func parsePoolName(poolName string) ([]string, error) { pools := strings.Split(poolName, ",") if len(pools) > 2 { diff --git a/pkg/cni/types/types_test.go b/pkg/cni/types/types_test.go index 2f44142..0c370d7 100644 --- a/pkg/cni/types/types_test.go +++ b/pkg/cni/types/types_test.go @@ -15,9 +15,11 @@ package types_test import ( "fmt" + "net" "os" "path" + "github.com/containernetworking/cni/pkg/skel" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -28,6 +30,7 @@ var _ = Describe("Types Tests", func() { var ( tmpDir string testConfDir string + testArgs = "K8S_POD_NAME=test;K8S_POD_NAMESPACE=test" ) BeforeEach(func() { @@ -46,7 +49,8 @@ var _ = Describe("Types Tests", func() { // Load config testConf := fmt.Sprintf(`{"name": "my-net", "ipam": {"confDir": %q}}`, testConfDir) - conf, err := cniTypes.NewConfLoader().LoadConf([]byte(testConf)) + conf, err := cniTypes.NewConfLoader().LoadConf(&skel.CmdArgs{ + StdinData: []byte(testConf), Args: testArgs}) // Validate Expect(err).ToNot(HaveOccurred()) @@ -64,7 +68,8 @@ var _ = Describe("Types Tests", func() { // Load config testConf := fmt.Sprintf(`{"name": "my-net", "ipam": {"confDir": %q}}`, testConfDir) - conf, err := cniTypes.NewConfLoader().LoadConf([]byte(testConf)) + conf, err := cniTypes.NewConfLoader().LoadConf(&skel.CmdArgs{ + StdinData: []byte(testConf), Args: testArgs}) // Validate Expect(err).ToNot(HaveOccurred()) @@ -81,7 +86,8 @@ var _ = Describe("Types Tests", func() { // Load config testConf := fmt.Sprintf(`{"name": "my-net", "ipam": {"confDir": %q, "poolName": "my-pool", "logLevel": "error"}}`, testConfDir) - conf, err := cniTypes.NewConfLoader().LoadConf([]byte(testConf)) + conf, err := cniTypes.NewConfLoader().LoadConf(&skel.CmdArgs{ + StdinData: []byte(testConf), Args: testArgs}) // Validate Expect(err).ToNot(HaveOccurred()) @@ -92,13 +98,65 @@ var _ = Describe("Types Tests", func() { }) It("Fails if config is invalid json", func() { - _, err := cniTypes.NewConfLoader().LoadConf([]byte("{garbage%^&*")) + _, err := cniTypes.NewConfLoader().LoadConf(&skel.CmdArgs{ + StdinData: []byte("{garbage%^&*"), Args: testArgs}) Expect(err).To(HaveOccurred()) }) It("Fails if config does not contain ipam key", func() { - _, err := cniTypes.NewConfLoader().LoadConf([]byte(`{"name": "my-net", "type": "sriov"}`)) + _, err := cniTypes.NewConfLoader().LoadConf(&skel.CmdArgs{ + StdinData: []byte(`{"name": "my-net", "type": "sriov"}`), Args: testArgs}) Expect(err).To(HaveOccurred()) }) + + It("Missing metadata arguments", func() { + // write config file + confData := `{"logLevel": "debug", "logFile": "some/path.log"}` + Expect(os.WriteFile( + path.Join(testConfDir, cniTypes.ConfFileName), []byte(confData), 0o644)).NotTo(HaveOccurred()) + // Load config + testConf := fmt.Sprintf(`{"name": "my-net", "ipam": {"confDir": %q}}`, testConfDir) + _, err := cniTypes.NewConfLoader().LoadConf(&skel.CmdArgs{StdinData: []byte(testConf), Args: "K8S_POD_NAME=test"}) + Expect(err).To(MatchError(ContainSubstring("K8S_POD_NAMESPACE"))) + _, err = cniTypes.NewConfLoader().LoadConf(&skel.CmdArgs{ + StdinData: []byte(testConf), Args: "K8S_POD_NAMESPACE=test"}) + Expect(err).To(MatchError(ContainSubstring("K8S_POD_NAME"))) + }) + DescribeTable("Static IPs", + func(stdinContent string, args string, expectedValue []net.IP, isValid bool) { + Expect(os.WriteFile(path.Join(testConfDir, cniTypes.ConfFileName), + []byte("{}"), 0o644)).NotTo(HaveOccurred()) + stdinContent = fmt.Sprintf(stdinContent, testConfDir) + if args == "" { + args = testArgs + } else { + args = testArgs + ";" + args + } + conf, err := cniTypes.NewConfLoader().LoadConf(&skel.CmdArgs{ + StdinData: []byte(stdinContent), Args: args}) + if isValid { + Expect(err).NotTo(HaveOccurred()) + Expect(conf.IPAM.RequestedIPs).To(Equal(expectedValue)) + } else { + Expect(err).To(HaveOccurred()) + } + }, + Entry("no IPs by default", `{"name": "my-net", "ipam": {"confDir": %q}}`, "", nil, true), + Entry("env variable", `{"name": "my-net", "ipam": {"confDir": %q}}`, + "IP=192.168.1.1", + []net.IP{net.ParseIP("192.168.1.1")}, true), + Entry("STDIN CNI args", `{"name": "my-net", + "args": {"cni": {"ips": ["1.1.1.1", "2.2.2.2/24"]}}, "ipam": {"confDir": %q}}`, "", + []net.IP{net.ParseIP("1.1.1.1"), net.ParseIP("2.2.2.2")}, true), + Entry("STDIN RuntimeConfig", `{"name": "my-net", + "runtimeConfig": {"ips": ["1.1.1.1", "2.2.2.2/24"]}, "ipam": {"confDir": %q}}`, "", + []net.IP{net.ParseIP("1.1.1.1"), net.ParseIP("2.2.2.2")}, true), + Entry("ipv6", `{"name": "my-net", + "runtimeConfig": {"ips": ["fd52:2eb5:44::1"]}, "ipam": {"confDir": %q}}`, "", + []net.IP{net.ParseIP("fd52:2eb5:44::1")}, true), + Entry("invalid ip", `{"name": "my-net", + "runtimeConfig": {"ips": ["adfdsaf"]}, "ipam": {"confDir": %q}}`, "", + nil, false), + ) }) }) From 4cd4fab2705bf0a259671d4b4a42e8cba0affb56 Mon Sep 17 00:00:00 2001 From: Yury Kulazhenkov Date: Fri, 7 Jun 2024 16:11:48 +0300 Subject: [PATCH 2/5] Add support for static IP request to GRPC API Signed-off-by: Yury Kulazhenkov --- api/grpc/nvidia/ipam/node/v1/node.pb.go | 140 ++++++++++-------- api/grpc/proto/nvidia/ipam/node/v1/node.proto | 2 + 2 files changed, 78 insertions(+), 64 deletions(-) diff --git a/api/grpc/nvidia/ipam/node/v1/node.pb.go b/api/grpc/nvidia/ipam/node/v1/node.pb.go index e355b89..ee79383 100644 --- a/api/grpc/nvidia/ipam/node/v1/node.pb.go +++ b/api/grpc/nvidia/ipam/node/v1/node.pb.go @@ -147,6 +147,8 @@ type IPAMParameters struct { Metadata *IPAMMetadata `protobuf:"bytes,4,opt,name=metadata,proto3" json:"metadata,omitempty"` // type of the pool which is refered by the name in the pools field PoolType PoolType `protobuf:"varint,5,opt,name=pool_type,json=poolType,proto3,enum=nvidia.ipam.node.v1.PoolType" json:"pool_type,omitempty"` + // conatins IP that were statically requested + RequestedIps []string `protobuf:"bytes,6,rep,name=requested_ips,json=requestedIps,proto3" json:"requested_ips,omitempty"` } func (x *IPAMParameters) Reset() { @@ -216,6 +218,13 @@ func (x *IPAMParameters) GetPoolType() PoolType { return PoolType_POOL_TYPE_UNSPECIFIED } +func (x *IPAMParameters) GetRequestedIps() []string { + if x != nil { + return x.RequestedIps + } + return nil +} + // IPAMMetadata contains metadata for IPAM calls type IPAMMetadata struct { state protoimpl.MessageState @@ -604,7 +613,7 @@ var file_nvidia_ipam_node_v1_node_proto_rawDesc = []byte{ 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x50, 0x41, 0x4d, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, - 0x73, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0xe9, 0x01, + 0x73, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0x8e, 0x02, 0x0a, 0x0e, 0x49, 0x50, 0x41, 0x4d, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x6f, 0x6f, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x70, 0x6f, 0x6f, 0x6c, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6e, 0x69, 0x5f, 0x63, 0x6f, @@ -619,70 +628,73 @@ var file_nvidia_ipam_node_v1_node_proto_rawDesc = []byte{ 0x09, 0x70, 0x6f, 0x6f, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x6f, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x52, - 0x08, 0x70, 0x6f, 0x6f, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x22, 0x99, 0x01, 0x0a, 0x0c, 0x49, 0x50, - 0x41, 0x4d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x20, 0x0a, 0x0c, 0x6b, 0x38, - 0x73, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x6b, 0x38, 0x73, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x11, - 0x6b, 0x38, 0x73, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6b, 0x38, 0x73, 0x50, 0x6f, 0x64, 0x4e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1e, 0x0a, 0x0b, 0x6b, 0x38, 0x73, 0x5f, - 0x70, 0x6f, 0x64, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6b, - 0x38, 0x73, 0x50, 0x6f, 0x64, 0x55, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x49, 0x64, 0x22, 0x59, 0x0a, 0x12, 0x49, 0x73, 0x41, 0x6c, 0x6c, 0x6f, 0x63, - 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x0a, 0x70, - 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x23, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, - 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x50, 0x41, 0x4d, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, - 0x74, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, - 0x22, 0x58, 0x0a, 0x11, 0x44, 0x65, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, - 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6e, 0x76, 0x69, 0x64, - 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x49, 0x50, 0x41, 0x4d, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x52, 0x0a, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0x59, 0x0a, 0x10, 0x41, 0x6c, - 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, - 0x0a, 0x0b, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, - 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8a, 0x01, 0x0a, 0x0e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x6f, 0x6c, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6f, 0x6f, 0x6c, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, 0x12, 0x18, 0x0a, 0x07, - 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, - 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x3a, 0x0a, 0x09, 0x70, 0x6f, 0x6f, 0x6c, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x6e, 0x76, 0x69, 0x64, - 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x50, 0x6f, 0x6f, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x70, 0x6f, 0x6f, 0x6c, 0x54, 0x79, - 0x70, 0x65, 0x22, 0x15, 0x0a, 0x13, 0x49, 0x73, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, - 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x14, 0x0a, 0x12, 0x44, 0x65, 0x61, - 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, - 0x53, 0x0a, 0x08, 0x50, 0x6f, 0x6f, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x15, 0x50, - 0x4f, 0x4f, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, - 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x4f, 0x4f, 0x4c, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x49, 0x50, 0x50, 0x4f, 0x4f, 0x4c, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, - 0x50, 0x4f, 0x4f, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x49, 0x44, 0x52, 0x50, 0x4f, - 0x4f, 0x4c, 0x10, 0x02, 0x32, 0xad, 0x02, 0x0a, 0x0b, 0x49, 0x50, 0x41, 0x4d, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x12, 0x59, 0x0a, 0x08, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, - 0x12, 0x24, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, - 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, - 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x6c, - 0x6f, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x62, 0x0a, 0x0b, 0x49, 0x73, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x27, + 0x08, 0x70, 0x6f, 0x6f, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x70, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x0c, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x49, 0x70, 0x73, 0x22, 0x99, + 0x01, 0x0a, 0x0c, 0x49, 0x50, 0x41, 0x4d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, + 0x20, 0x0a, 0x0c, 0x6b, 0x38, 0x73, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6b, 0x38, 0x73, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x6b, 0x38, 0x73, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6b, 0x38, + 0x73, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1e, 0x0a, + 0x0b, 0x6b, 0x38, 0x73, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x6b, 0x38, 0x73, 0x50, 0x6f, 0x64, 0x55, 0x69, 0x64, 0x12, 0x1b, 0x0a, + 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x22, 0x59, 0x0a, 0x12, 0x49, 0x73, + 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x43, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, + 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x50, 0x41, 0x4d, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, + 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0x58, 0x0a, 0x11, 0x44, 0x65, 0x61, 0x6c, 0x6c, 0x6f, 0x63, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x0a, 0x70, 0x61, + 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, + 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x50, 0x41, 0x4d, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, + 0x65, 0x72, 0x73, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, + 0x59, 0x0a, 0x10, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0b, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, + 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x61, + 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8a, 0x01, 0x0a, 0x0e, 0x41, + 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, + 0x04, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6f, 0x6f, + 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, + 0x70, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x3a, 0x0a, 0x09, 0x70, + 0x6f, 0x6f, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, + 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x6f, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x70, + 0x6f, 0x6f, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x22, 0x15, 0x0a, 0x13, 0x49, 0x73, 0x41, 0x6c, 0x6c, + 0x6f, 0x63, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x14, + 0x0a, 0x12, 0x44, 0x65, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x53, 0x0a, 0x08, 0x50, 0x6f, 0x6f, 0x6c, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x19, 0x0a, 0x15, 0x50, 0x4f, 0x4f, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, + 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x50, + 0x4f, 0x4f, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x50, 0x50, 0x4f, 0x4f, 0x4c, 0x10, + 0x01, 0x12, 0x16, 0x0a, 0x12, 0x50, 0x4f, 0x4f, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, + 0x49, 0x44, 0x52, 0x50, 0x4f, 0x4f, 0x4c, 0x10, 0x02, 0x32, 0xad, 0x02, 0x0a, 0x0b, 0x49, 0x50, + 0x41, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x59, 0x0a, 0x08, 0x41, 0x6c, 0x6c, + 0x6f, 0x63, 0x61, 0x74, 0x65, 0x12, 0x24, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, + 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x6e, 0x76, + 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a, 0x0b, 0x49, 0x73, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, + 0x74, 0x65, 0x64, 0x12, 0x27, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, + 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x73, 0x41, 0x6c, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6e, + 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x49, 0x73, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0a, 0x44, 0x65, 0x61, 0x6c, + 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x12, 0x26, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, + 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x61, + 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x73, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x64, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, - 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x73, - 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0a, 0x44, 0x65, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, - 0x65, 0x12, 0x26, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, - 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6e, 0x76, 0x69, 0x64, - 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x44, 0x65, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( diff --git a/api/grpc/proto/nvidia/ipam/node/v1/node.proto b/api/grpc/proto/nvidia/ipam/node/v1/node.proto index f824b9f..074d4af 100644 --- a/api/grpc/proto/nvidia/ipam/node/v1/node.proto +++ b/api/grpc/proto/nvidia/ipam/node/v1/node.proto @@ -73,6 +73,8 @@ message IPAMParameters { IPAMMetadata metadata = 4; // type of the pool which is refered by the name in the pools field PoolType pool_type = 5; + // conatins IP that were statically requested + repeated string requested_ips = 6; } // IPAMMetadata contains metadata for IPAM calls From 7b4934c1c6ed653186330150d45271b883ae299c Mon Sep 17 00:00:00 2001 From: Yury Kulazhenkov Date: Fri, 7 Jun 2024 18:52:37 +0300 Subject: [PATCH 3/5] Add support for static IPs to ipam-node --- pkg/cni/plugin/plugin.go | 8 +++ pkg/cni/plugin/plugin_test.go | 1 + pkg/ipam-node/allocator/allocator.go | 32 ++++++++++- pkg/ipam-node/allocator/allocator_test.go | 57 +++++++++++++++++--- pkg/ipam-node/allocator/mocks/IPAllocator.go | 46 ++++++++-------- pkg/ipam-node/handlers/allocate.go | 37 +++++++++++-- pkg/ipam-node/handlers/handlers.go | 7 +++ pkg/ipam-node/handlers/handlers_test.go | 51 +++++++++++++++--- 8 files changed, 200 insertions(+), 39 deletions(-) diff --git a/pkg/cni/plugin/plugin.go b/pkg/cni/plugin/plugin.go index 9cd52a7..1e87617 100644 --- a/pkg/cni/plugin/plugin.go +++ b/pkg/cni/plugin/plugin.go @@ -195,6 +195,12 @@ func cniConfToGRPCReq(conf *types.NetConf, args *skel.CmdArgs) *nodev1.IPAMParam if conf.IPAM.PoolType == common.PoolTypeCIDRPool { poolType = nodev1.PoolType_POOL_TYPE_CIDRPOOL } + + requestedIPs := make([]string, 0, len(conf.IPAM.RequestedIPs)) + for _, ipAddr := range conf.IPAM.RequestedIPs { + requestedIPs = append(requestedIPs, ipAddr.String()) + } + req := &nodev1.IPAMParameters{ Pools: conf.IPAM.Pools, PoolType: poolType, @@ -206,10 +212,12 @@ func cniConfToGRPCReq(conf *types.NetConf, args *skel.CmdArgs) *nodev1.IPAMParam K8SPodUid: conf.IPAM.K8SMetadata.PodUID, DeviceId: conf.DeviceID, }, + RequestedIps: requestedIPs, } if req.Metadata.K8SPodUid == "" { log.Warningf("K8S_POD_UID is not provided by container runtime") } + return req } diff --git a/pkg/cni/plugin/plugin_test.go b/pkg/cni/plugin/plugin_test.go index cda9405..dba871d 100644 --- a/pkg/cni/plugin/plugin_test.go +++ b/pkg/cni/plugin/plugin_test.go @@ -99,6 +99,7 @@ var _ = Describe("plugin tests", func() { K8SPodName: "test", K8SPodNamespace: "test", }, + RequestedIps: []string{}, }}).Return(&nodev1.AllocateResponse{ Allocations: []*nodev1.AllocationInfo{{ Pool: "my-pool", diff --git a/pkg/ipam-node/allocator/allocator.go b/pkg/ipam-node/allocator/allocator.go index 2ee7259..f350df8 100644 --- a/pkg/ipam-node/allocator/allocator.go +++ b/pkg/ipam-node/allocator/allocator.go @@ -16,6 +16,7 @@ package allocator import ( "errors" + "fmt" "net" current "github.com/containernetworking/cni/pkg/types/100" @@ -35,7 +36,7 @@ var ( //go:generate mockery --name IPAllocator type IPAllocator interface { // Allocate allocates IP address from the range for the container identified by ID and ifName - Allocate(id string, ifName string, meta types.ReservationMetadata) (*current.IPConfig, error) + Allocate(id string, ifName string, meta types.ReservationMetadata, staticIP net.IP) (*current.IPConfig, error) } type allocator struct { @@ -57,10 +58,15 @@ func NewIPAllocator(s *RangeSet, exclusions *RangeSet, } // Allocate allocates an IP -func (a *allocator) Allocate(id string, ifName string, meta types.ReservationMetadata) (*current.IPConfig, error) { +func (a *allocator) Allocate(id string, ifName string, + meta types.ReservationMetadata, staticIP net.IP) (*current.IPConfig, error) { var reservedIP *net.IPNet var gw net.IP + if staticIP != nil { + return a.allocateStaticIP(id, ifName, meta, staticIP) + } + iter := a.getIter() for { reservedIP, gw = iter.Next() @@ -85,6 +91,28 @@ func (a *allocator) Allocate(id string, ifName string, meta types.ReservationMet }, nil } +func (a *allocator) allocateStaticIP(id string, ifName string, + meta types.ReservationMetadata, staticIP net.IP) (*current.IPConfig, error) { + for _, r := range *a.rangeSet { + rangeSubnet := net.IPNet(r.Subnet) + if !rangeSubnet.Contains(staticIP) { + continue + } + lastReservedIP := a.session.GetLastReservedIP(a.poolKey) + err := a.session.Reserve(a.poolKey, id, ifName, meta, staticIP) + if err != nil { + return nil, fmt.Errorf("failed to allocate staticIP %s: %w", staticIP.String(), err) + } + // for static allocations we don't want to change lastReservedIP + a.session.SetLastReservedIP(a.poolKey, lastReservedIP) + return ¤t.IPConfig{ + Address: net.IPNet{IP: staticIP, Mask: r.Subnet.Mask}, + Gateway: r.Gateway, + }, nil + } + return nil, fmt.Errorf("can't find IP range in the allocator for static IP: %s", staticIP.String()) +} + // RangeIter implements iterator over the RangeSet type RangeIter struct { rangeSet *RangeSet diff --git a/pkg/ipam-node/allocator/allocator_test.go b/pkg/ipam-node/allocator/allocator_test.go index b92ca5e..d02a52a 100644 --- a/pkg/ipam-node/allocator/allocator_test.go +++ b/pkg/ipam-node/allocator/allocator_test.go @@ -80,11 +80,11 @@ func (t AllocatorTestCase) run(idx int, session storePkg.Session) (*current.IPCo Expect(p.Canonicalize()).To(Succeed()) alloc := allocator.NewIPAllocator(&p, nil, testPoolName, session) - return alloc.Allocate(testContainerID, testIFName, types.ReservationMetadata{}) + return alloc.Allocate(testContainerID, testIFName, types.ReservationMetadata{}, nil) } func checkAlloc(a allocator.IPAllocator, id string, expectedIP net.IP) { - cfg, err := a.Allocate(id, testIFName, types.ReservationMetadata{}) + cfg, err := a.Allocate(id, testIFName, types.ReservationMetadata{}, nil) if expectedIP == nil { ExpectWithOffset(1, err).To(HaveOccurred()) return @@ -276,14 +276,14 @@ var _ = Describe("allocator", func() { }() alloc := mkAlloc(store) for i := 2; i < 7; i++ { - res, err := alloc.Allocate(fmt.Sprintf("ID%d", i), testIFName, types.ReservationMetadata{}) + res, err := alloc.Allocate(fmt.Sprintf("ID%d", i), testIFName, types.ReservationMetadata{}, nil) Expect(err).ToNot(HaveOccurred()) s := fmt.Sprintf("192.168.1.%d/29", i) Expect(s).To(Equal(res.Address.String())) _, _ = fmt.Fprintln(GinkgoWriter, "got ip", res.Address.String()) } - x, err := alloc.Allocate("ID8", testIFName, types.ReservationMetadata{}) + x, err := alloc.Allocate("ID8", testIFName, types.ReservationMetadata{}, nil) _, _ = fmt.Fprintln(GinkgoWriter, "got ip", x) Expect(err).To(HaveOccurred()) }) @@ -295,13 +295,13 @@ var _ = Describe("allocator", func() { _ = store.Commit() }() alloc := mkAlloc(store) - res, err := alloc.Allocate(testContainerID, testIFName, types.ReservationMetadata{}) + res, err := alloc.Allocate(testContainerID, testIFName, types.ReservationMetadata{}, nil) Expect(err).ToNot(HaveOccurred()) Expect(res.Address.String()).To(Equal("192.168.1.2/29")) store.ReleaseReservationByID(testPoolName, testContainerID, testIFName) - res, err = alloc.Allocate(testContainerID, testIFName, types.ReservationMetadata{}) + res, err = alloc.Allocate(testContainerID, testIFName, types.ReservationMetadata{}, nil) Expect(err).ToNot(HaveOccurred()) Expect(res.Address.String()).To(Equal("192.168.1.3/29")) }) @@ -442,4 +442,49 @@ var _ = Describe("allocator", func() { checkAlloc(a, "2", net.IP{192, 168, 0, 6}) }) }) + Context("Static IP allocation", func() { + It("should allocate IP", func() { + session, err := storePkg.New( + filepath.Join(GinkgoT().TempDir(), "test_store")).Open(context.Background()) + Expect(err).NotTo(HaveOccurred()) + defer func() { + _ = session.Commit() + }() + p := allocator.RangeSet{ + allocator.Range{Subnet: mustSubnet("192.168.0.0/24"), Gateway: net.ParseIP("192.168.0.10")}, + } + // should ignore exclusions while allocating static IPs + e := allocator.RangeSet{ + allocator.Range{Subnet: mustSubnet("192.168.0.0/24"), + RangeStart: net.ParseIP("192.168.0.33"), RangeEnd: net.ParseIP("192.168.0.33")}, + } + staticIP := net.ParseIP("192.168.0.33") + Expect(p.Canonicalize()).NotTo(HaveOccurred()) + Expect(e.Canonicalize()).NotTo(HaveOccurred()) + a := allocator.NewIPAllocator(&p, &e, testPoolName, session) + cfg, err := a.Allocate("0", testIFName, types.ReservationMetadata{}, staticIP) + Expect(err).NotTo(HaveOccurred()) + Expect(cfg.Address.IP).To(Equal(staticIP)) + Expect(cfg.Address.Mask).To(Equal(p[0].Subnet.Mask)) + Expect(cfg.Gateway).To(Equal(p[0].Gateway)) + _, err = a.Allocate("1", testIFName, types.ReservationMetadata{}, staticIP) + Expect(err).To(MatchError(ContainSubstring("ip address is already reserved"))) + }) + It("should fail if static IP is not in the allocator's subnet", func() { + session, err := storePkg.New( + filepath.Join(GinkgoT().TempDir(), "test_store")).Open(context.Background()) + Expect(err).NotTo(HaveOccurred()) + defer func() { + _ = session.Commit() + }() + p := allocator.RangeSet{ + allocator.Range{Subnet: mustSubnet("192.168.0.0/24"), Gateway: net.ParseIP("192.168.0.10")}, + } + staticIP := net.ParseIP("10.10.10.10") + Expect(p.Canonicalize()).NotTo(HaveOccurred()) + a := allocator.NewIPAllocator(&p, nil, testPoolName, session) + _, err = a.Allocate("0", testIFName, types.ReservationMetadata{}, staticIP) + Expect(err).To(MatchError(ContainSubstring("can't find IP range"))) + }) + }) }) diff --git a/pkg/ipam-node/allocator/mocks/IPAllocator.go b/pkg/ipam-node/allocator/mocks/IPAllocator.go index ec06449..a55d0f1 100644 --- a/pkg/ipam-node/allocator/mocks/IPAllocator.go +++ b/pkg/ipam-node/allocator/mocks/IPAllocator.go @@ -1,11 +1,15 @@ -// Code generated by mockery v2.27.1. DO NOT EDIT. +// Code generated by mockery v2.37.1. DO NOT EDIT. package mocks import ( + net "net" + + mock "github.com/stretchr/testify/mock" + types "github.com/Mellanox/nvidia-k8s-ipam/pkg/ipam-node/types" + types100 "github.com/containernetworking/cni/pkg/types/100" - mock "github.com/stretchr/testify/mock" ) // IPAllocator is an autogenerated mock type for the IPAllocator type @@ -21,25 +25,25 @@ func (_m *IPAllocator) EXPECT() *IPAllocator_Expecter { return &IPAllocator_Expecter{mock: &_m.Mock} } -// Allocate provides a mock function with given fields: id, ifName, meta -func (_m *IPAllocator) Allocate(id string, ifName string, meta types.ReservationMetadata) (*types100.IPConfig, error) { - ret := _m.Called(id, ifName, meta) +// Allocate provides a mock function with given fields: id, ifName, meta, staticIP +func (_m *IPAllocator) Allocate(id string, ifName string, meta types.ReservationMetadata, staticIP net.IP) (*types100.IPConfig, error) { + ret := _m.Called(id, ifName, meta, staticIP) var r0 *types100.IPConfig var r1 error - if rf, ok := ret.Get(0).(func(string, string, types.ReservationMetadata) (*types100.IPConfig, error)); ok { - return rf(id, ifName, meta) + if rf, ok := ret.Get(0).(func(string, string, types.ReservationMetadata, net.IP) (*types100.IPConfig, error)); ok { + return rf(id, ifName, meta, staticIP) } - if rf, ok := ret.Get(0).(func(string, string, types.ReservationMetadata) *types100.IPConfig); ok { - r0 = rf(id, ifName, meta) + if rf, ok := ret.Get(0).(func(string, string, types.ReservationMetadata, net.IP) *types100.IPConfig); ok { + r0 = rf(id, ifName, meta, staticIP) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*types100.IPConfig) } } - if rf, ok := ret.Get(1).(func(string, string, types.ReservationMetadata) error); ok { - r1 = rf(id, ifName, meta) + if rf, ok := ret.Get(1).(func(string, string, types.ReservationMetadata, net.IP) error); ok { + r1 = rf(id, ifName, meta, staticIP) } else { r1 = ret.Error(1) } @@ -56,13 +60,14 @@ type IPAllocator_Allocate_Call struct { // - id string // - ifName string // - meta types.ReservationMetadata -func (_e *IPAllocator_Expecter) Allocate(id interface{}, ifName interface{}, meta interface{}) *IPAllocator_Allocate_Call { - return &IPAllocator_Allocate_Call{Call: _e.mock.On("Allocate", id, ifName, meta)} +// - staticIP net.IP +func (_e *IPAllocator_Expecter) Allocate(id interface{}, ifName interface{}, meta interface{}, staticIP interface{}) *IPAllocator_Allocate_Call { + return &IPAllocator_Allocate_Call{Call: _e.mock.On("Allocate", id, ifName, meta, staticIP)} } -func (_c *IPAllocator_Allocate_Call) Run(run func(id string, ifName string, meta types.ReservationMetadata)) *IPAllocator_Allocate_Call { +func (_c *IPAllocator_Allocate_Call) Run(run func(id string, ifName string, meta types.ReservationMetadata, staticIP net.IP)) *IPAllocator_Allocate_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string), args[2].(types.ReservationMetadata)) + run(args[0].(string), args[1].(string), args[2].(types.ReservationMetadata), args[3].(net.IP)) }) return _c } @@ -72,18 +77,17 @@ func (_c *IPAllocator_Allocate_Call) Return(_a0 *types100.IPConfig, _a1 error) * return _c } -func (_c *IPAllocator_Allocate_Call) RunAndReturn(run func(string, string, types.ReservationMetadata) (*types100.IPConfig, error)) *IPAllocator_Allocate_Call { +func (_c *IPAllocator_Allocate_Call) RunAndReturn(run func(string, string, types.ReservationMetadata, net.IP) (*types100.IPConfig, error)) *IPAllocator_Allocate_Call { _c.Call.Return(run) return _c } -type mockConstructorTestingTNewIPAllocator interface { +// NewIPAllocator creates a new instance of IPAllocator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewIPAllocator(t interface { mock.TestingT Cleanup(func()) -} - -// NewIPAllocator creates a new instance of IPAllocator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewIPAllocator(t mockConstructorTestingTNewIPAllocator) *IPAllocator { +}) *IPAllocator { mock := &IPAllocator{} mock.Mock.Test(t) diff --git a/pkg/ipam-node/handlers/allocate.go b/pkg/ipam-node/handlers/allocate.go index e08137c..d47c8ed 100644 --- a/pkg/ipam-node/handlers/allocate.go +++ b/pkg/ipam-node/handlers/allocate.go @@ -78,9 +78,14 @@ func (h *Handlers) allocate(reqLog logr.Logger, session storePkg.Session, params *nodev1.IPAMParameters) ([]PoolAlloc, error) { var err error result := make([]PoolAlloc, 0, len(params.Pools)) + requestedIPs := make([]net.IP, 0, len(params.RequestedIps)) + for _, ipAddr := range params.RequestedIps { + // already validated by validateReq func + requestedIPs = append(requestedIPs, net.ParseIP(ipAddr)) + } for _, poolName := range params.Pools { var alloc PoolAlloc - alloc, err = h.allocateInPool(poolName, reqLog, session, params) + alloc, err = h.allocateInPool(poolName, reqLog, session, params, requestedIPs) if err != nil { break } @@ -89,12 +94,26 @@ func (h *Handlers) allocate(reqLog logr.Logger, if err != nil { return nil, err } + // check that all requested static IPs were allocated + for _, ipAddr := range requestedIPs { + found := false + for _, r := range result { + if r.Address.IP.Equal(ipAddr) { + found = true + break + } + } + if !found { + return nil, status.Errorf(codes.InvalidArgument, "not all requested static IPs can be allocated"+ + " from the ranges available on the node, ip %s has no matching Pool", ipAddr.String()) + } + } return result, nil } func (h *Handlers) allocateInPool(poolName string, reqLog logr.Logger, - session storePkg.Session, params *nodev1.IPAMParameters) (PoolAlloc, error) { + session storePkg.Session, params *nodev1.IPAMParameters, requestedIPs []net.IP) (PoolAlloc, error) { poolType := poolTypeAsString(params.PoolType) poolLog := reqLog.WithValues("pool", poolName, "poolType", poolType) poolKey := common.GetPoolKey(poolName, poolType) @@ -126,6 +145,13 @@ func (h *Handlers) allocateInPool(poolName string, reqLog logr.Logger, return PoolAlloc{}, poolCfgError(poolLog, poolName, poolType, fmt.Sprintf("invalid range config: %s", err.Error())) } + var selectedStaticIP net.IP + for _, ipAddr := range requestedIPs { + if subnet.Contains(ipAddr) { + selectedStaticIP = ipAddr + break + } + } exclusionRangeSet := make(allocator.RangeSet, 0, len(poolCfg.Exclusions)) for _, e := range poolCfg.Exclusions { exclusionRangeSet = append(exclusionRangeSet, allocator.Range{ @@ -151,7 +177,7 @@ func (h *Handlers) allocateInPool(poolName string, reqLog logr.Logger, allocMeta.PodNamespace = params.Metadata.K8SPodNamespace allocMeta.DeviceID = params.Metadata.DeviceId } - result, err := alloc.Allocate(params.CniContainerid, params.CniIfname, allocMeta) + result, err := alloc.Allocate(params.CniContainerid, params.CniIfname, allocMeta, selectedStaticIP) if err != nil { poolLog.Error(err, "failed to allocate IP address") if errors.Is(err, storePkg.ErrReservationAlreadyExist) { @@ -163,6 +189,11 @@ func (h *Handlers) allocateInPool(poolName string, reqLog logr.Logger, "no free addresses in the pool \"%s\", poolType \"%s\"", poolName, poolType) } + if errors.Is(err, storePkg.ErrIPAlreadyReserved) { + return PoolAlloc{}, status.Errorf(codes.ResourceExhausted, + "requested IP is already reserved in the pool \"%s\", poolType \"%s\"", + poolName, poolType) + } return PoolAlloc{}, status.Errorf(codes.Internal, "failed to allocate IP address in pool \"%s\", poolType \"%s\"", poolName, poolType) } diff --git a/pkg/ipam-node/handlers/handlers.go b/pkg/ipam-node/handlers/handlers.go index 3298e94..98734fa 100644 --- a/pkg/ipam-node/handlers/handlers.go +++ b/pkg/ipam-node/handlers/handlers.go @@ -16,6 +16,7 @@ package handlers import ( "context" "fmt" + "net" "github.com/go-logr/logr" "google.golang.org/grpc/codes" @@ -113,6 +114,12 @@ func validateReq(req paramsGetter) error { if params.Metadata.K8SPodNamespace == "" { return fieldIsRequiredError("parameters.metadata.k8s_pod_namespace") } + for i, v := range params.RequestedIps { + ipAddr := net.ParseIP(v) + if ipAddr == nil { + return fieldsIsInvalidError(fmt.Sprintf("parameters.requested_ips[%d]", i)) + } + } return nil } diff --git a/pkg/ipam-node/handlers/handlers_test.go b/pkg/ipam-node/handlers/handlers_test.go index 191470c..6ace033 100644 --- a/pkg/ipam-node/handlers/handlers_test.go +++ b/pkg/ipam-node/handlers/handlers_test.go @@ -106,12 +106,12 @@ var _ = Describe("Handlers", func() { store.On("Open", mock.Anything).Return(session, nil) poolManager.On("GetPoolByKey", testPoolName1).Return(getPoolConfigs()[testPoolName1]) poolManager.On("GetPoolByKey", testPoolName2).Return(getPoolConfigs()[testPoolName2]) - allocators[testPoolName1].On("Allocate", "id1", "net0", mock.Anything).Return( + allocators[testPoolName1].On("Allocate", "id1", "net0", mock.Anything, mock.Anything).Return( ¤t.IPConfig{ Gateway: net.ParseIP("192.168.0.1"), Address: getIPWithMask("192.168.0.2/16"), }, nil) - allocators[testPoolName2].On("Allocate", "id1", "net0", mock.Anything).Return( + allocators[testPoolName2].On("Allocate", "id1", "net0", mock.Anything, mock.Anything).Return( ¤t.IPConfig{ Gateway: net.ParseIP("10.100.0.1"), Address: getIPWithMask("10.100.0.2/16"), @@ -134,6 +134,43 @@ var _ = Describe("Handlers", func() { HaveField("Gateway", "10.100.0.1"), ))) }) + It("Allocate static IP", func() { + store.On("Open", mock.Anything).Return(session, nil) + poolManager.On("GetPoolByKey", testPoolName1).Return(getPoolConfigs()[testPoolName1]) + allocators[testPoolName1].On("Allocate", "id1", "net0", mock.Anything, net.ParseIP("192.168.0.2")).Return( + ¤t.IPConfig{ + Gateway: net.ParseIP("192.168.0.1"), + Address: getIPWithMask("192.168.0.2/16"), + }, nil) + session.On("Commit").Return(nil) + ipamParams := getValidIPAMParams() + ipamParams.Pools = []string{testPoolName1} + ipamParams.RequestedIps = []string{"192.168.0.2"} + resp, err := handlers.Allocate(ctx, &nodev1.AllocateRequest{Parameters: ipamParams}) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.Allocations).To(HaveLen(1)) + Expect(resp.Allocations).To(ContainElement( + And( + HaveField("Pool", testPoolName1), + HaveField("Ip", "192.168.0.2/16"), + HaveField("Gateway", "192.168.0.1"), + ))) + }) + It("Allocate failed: static IP was not allocated", func() { + store.On("Open", mock.Anything).Return(session, nil) + poolManager.On("GetPoolByKey", testPoolName1).Return(getPoolConfigs()[testPoolName1]) + allocators[testPoolName1].On("Allocate", "id1", "net0", mock.Anything, mock.Anything).Return( + ¤t.IPConfig{ + Gateway: net.ParseIP("192.168.0.1"), + Address: getIPWithMask("192.168.0.2/16"), + }, nil) + session.On("Cancel").Return() + ipamParams := getValidIPAMParams() + ipamParams.Pools = []string{testPoolName1} + ipamParams.RequestedIps = []string{"10.10.10.10"} + _, err := handlers.Allocate(ctx, &nodev1.AllocateRequest{Parameters: ipamParams}) + Expect(err).To(MatchError(ContainSubstring("not all requested static IPs can be allocated"))) + }) It("Allocation failed: unknown pool", func() { store.On("Open", mock.Anything).Return(session, nil) poolManager.On("GetPoolByKey", testPoolName1).Return(nil) @@ -157,12 +194,12 @@ var _ = Describe("Handlers", func() { store.On("Open", mock.Anything).Return(session, nil) poolManager.On("GetPoolByKey", testPoolName1).Return(getPoolConfigs()[testPoolName1]) poolManager.On("GetPoolByKey", testPoolName2).Return(getPoolConfigs()[testPoolName2]) - allocators[testPoolName1].On("Allocate", "id1", "net0", mock.Anything).Return( + allocators[testPoolName1].On("Allocate", "id1", "net0", mock.Anything, mock.Anything).Return( ¤t.IPConfig{ Gateway: net.ParseIP("192.168.0.1"), Address: getIPWithMask("192.168.0.2/16"), }, nil) - allocators[testPoolName2].On("Allocate", "id1", "net0", mock.Anything).Return( + allocators[testPoolName2].On("Allocate", "id1", "net0", mock.Anything, mock.Anything).Return( nil, allocatorPkg.ErrNoFreeAddresses) session.On("Cancel").Return() @@ -172,7 +209,7 @@ var _ = Describe("Handlers", func() { It("Allocation failed: already allocated", func() { store.On("Open", mock.Anything).Return(session, nil) poolManager.On("GetPoolByKey", testPoolName1).Return(getPoolConfigs()[testPoolName1]) - allocators[testPoolName1].On("Allocate", "id1", "net0", mock.Anything).Return( + allocators[testPoolName1].On("Allocate", "id1", "net0", mock.Anything, mock.Anything).Return( nil, storePkg.ErrReservationAlreadyExist) session.On("Cancel").Return() _, err := handlers.Allocate(ctx, &nodev1.AllocateRequest{Parameters: getValidIPAMParams()}) @@ -182,12 +219,12 @@ var _ = Describe("Handlers", func() { store.On("Open", mock.Anything).Return(session, nil) poolManager.On("GetPoolByKey", testPoolName1).Return(getPoolConfigs()[testPoolName1]) poolManager.On("GetPoolByKey", testPoolName2).Return(getPoolConfigs()[testPoolName2]) - allocators[testPoolName1].On("Allocate", "id1", "net0", mock.Anything).Return( + allocators[testPoolName1].On("Allocate", "id1", "net0", mock.Anything, mock.Anything).Return( ¤t.IPConfig{ Gateway: net.ParseIP("192.168.0.1"), Address: getIPWithMask("192.168.0.2/16"), }, nil) - allocators[testPoolName2].On("Allocate", "id1", "net0", mock.Anything).Return( + allocators[testPoolName2].On("Allocate", "id1", "net0", mock.Anything, mock.Anything).Return( ¤t.IPConfig{ Gateway: net.ParseIP("10.100.0.1"), Address: getIPWithMask("10.100.0.2/16"), From fb9882565fb3177454cb5c8bb8173eae1aeaba78 Mon Sep 17 00:00:00 2001 From: Yury Kulazhenkov Date: Tue, 11 Jun 2024 15:34:27 +0300 Subject: [PATCH 4/5] Add support for passing poolNames and poolType as CNI Args Signed-off-by: Yury Kulazhenkov --- pkg/cni/types/types.go | 42 ++++++++++++++++++++++------ pkg/cni/types/types_test.go | 55 +++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 9 deletions(-) diff --git a/pkg/cni/types/types.go b/pkg/cni/types/types.go index b4614da..7af204b 100644 --- a/pkg/cni/types/types.go +++ b/pkg/cni/types/types.go @@ -95,7 +95,9 @@ type NetConf struct { // IPAMArgs holds arguments from stdin args["cni"] type IPAMArgs struct { - IPs []string `json:"ips"` + IPs []string `json:"ips"` + PoolNames []string `json:"poolNames"` + PoolType string `json:"poolType"` } // IPAMEnvArgs holds arguments from CNI_ARGS env variable @@ -171,15 +173,13 @@ func (cl *confLoader) LoadConf(args *skel.CmdArgs) (*NetConf, error) { return nil, err } - n.IPAM.Pools, err = parsePoolName(n.IPAM.PoolName) + n.IPAM.Pools, err = getPools(n) if err != nil { return nil, err } - - n.IPAM.PoolType = strings.ToLower(n.IPAM.PoolType) - if n.IPAM.PoolType != common.PoolTypeIPPool && n.IPAM.PoolType != common.PoolTypeCIDRPool { - return nil, fmt.Errorf("unsupported poolType %s, supported values: %s, %s", - n.IPAM.PoolType, common.PoolTypeIPPool, common.PoolTypeCIDRPool) + n.IPAM.PoolType, err = getPoolType(n) + if err != nil { + return nil, err } return n, nil } @@ -219,8 +219,15 @@ func parseIP(s string) net.IP { return net.ParseIP(s) } -func parsePoolName(poolName string) ([]string, error) { - pools := strings.Split(poolName, ",") +// returns list of pools that should be used by the plugin. +// STDIN field "args[cni][poolNames]" has the highest priority +func getPools(n *NetConf) ([]string, error) { + var pools []string + if n.Args != nil && len(n.Args.ArgsCNI.PoolNames) > 0 { + pools = n.Args.ArgsCNI.PoolNames + } else { + pools = strings.Split(n.IPAM.PoolName, ",") + } if len(pools) > 2 { return nil, fmt.Errorf("pool field can't contain more then two entries") } @@ -232,6 +239,23 @@ func parsePoolName(poolName string) ([]string, error) { return pools, nil } +// returns poolType that should be used by the plugin. +// STDIN field "args[cni][poolType]" has the highest priority +func getPoolType(n *NetConf) (string, error) { + var poolType string + if n.Args != nil && n.Args.ArgsCNI.PoolType != "" { + poolType = n.Args.ArgsCNI.PoolType + } else { + poolType = n.IPAM.PoolType + } + poolType = strings.ToLower(poolType) + if poolType != common.PoolTypeIPPool && poolType != common.PoolTypeCIDRPool { + return "", fmt.Errorf("unsupported poolType %s, supported values: %s, %s", + poolType, common.PoolTypeIPPool, common.PoolTypeCIDRPool) + } + return poolType, nil +} + // loadFromConfFile returns *IPAMConf with values from config file located in filePath. func (cl *confLoader) loadFromConfFile(filePath string) (*IPAMConf, error) { data, err := os.ReadFile(filePath) diff --git a/pkg/cni/types/types_test.go b/pkg/cni/types/types_test.go index 0c370d7..21b3a46 100644 --- a/pkg/cni/types/types_test.go +++ b/pkg/cni/types/types_test.go @@ -158,5 +158,60 @@ var _ = Describe("Types Tests", func() { "runtimeConfig": {"ips": ["adfdsaf"]}, "ipam": {"confDir": %q}}`, "", nil, false), ) + DescribeTable("PoolName", + func(fileContent string, stdinContent string, expectedValue []string, isValid bool) { + Expect(os.WriteFile(path.Join(testConfDir, cniTypes.ConfFileName), + []byte(fileContent), 0o644)).NotTo(HaveOccurred()) + stdinContent = fmt.Sprintf(stdinContent, testConfDir) + conf, err := cniTypes.NewConfLoader().LoadConf(&skel.CmdArgs{StdinData: []byte(stdinContent), Args: testArgs}) + if isValid { + Expect(err).NotTo(HaveOccurred()) + Expect(conf.IPAM.Pools).To(Equal(expectedValue)) + } else { + Expect(err).To(HaveOccurred()) + } + }, + Entry("use network name if not set", + `{}`, + `{"name": "my-net", "ipam": {"confDir": %q}}`, + []string{"my-net"}, true), + Entry("from conf file", + `{"poolName": "fromFile"}`, + `{"name": "my-net", "ipam": {"confDir": %q}}`, + []string{"fromFile"}, true), + Entry("from STDIN", + `{"poolName": "fromFile"}`, + `{"name": "my-net", "ipam": {"confDir": %q, "poolName": "fromSTDIN"}}`, + []string{"fromSTDIN"}, true), + Entry("from STDIN CNI args", + `{"poolName": "fromFile"}`, + `{"name": "my-net", "args":{"cni": {"poolNames": ["fromArgs", "fromArgs2"]}}, + "ipam": {"confDir": %q, "poolName": "fromSTDIN"}}`, + []string{"fromArgs", "fromArgs2"}, true), + Entry("too many pools", + `{}`, + `{"name": "my-net", "ipam": {"confDir": %q, "poolName": "pool1,pool2,pool3"}}`, + []string{}, false), + ) + DescribeTable("PoolType", + func(fileContent string, stdinContent string, expectedValue string, isValid bool) { + Expect(os.WriteFile(path.Join(testConfDir, cniTypes.ConfFileName), + []byte(fileContent), 0o644)).NotTo(HaveOccurred()) + stdinContent = fmt.Sprintf(stdinContent, testConfDir) + conf, err := cniTypes.NewConfLoader().LoadConf(&skel.CmdArgs{StdinData: []byte(stdinContent), Args: testArgs}) + if isValid { + Expect(err).NotTo(HaveOccurred()) + Expect(conf.IPAM.PoolType).To(Equal(expectedValue)) + } else { + Expect(err).To(HaveOccurred()) + } + }, + Entry("use IPPool by default", `{}`, `{"name": "my-net", "ipam": {"confDir": %q}}`, "ippool", true), + Entry("from conf file", `{"poolType": "CIDRPool"}`, `{"name": "my-net", "ipam": {"confDir": %q}}`, "cidrpool", true), + Entry("from STDIN", `{}`, `{"name": "my-net", "ipam": {"confDir": %q, "poolType": "cidrPool"}}`, "cidrpool", true), + Entry("from STDIN CNI Args", `{}`, `{"name": "my-net", + "args": {"cni": {"poolType":"cidrpool"}}, "ipam": {"confDir": %q}}`, "cidrpool", true), + Entry("unknown type", `{}`, `{"name": "my-net", "ipam": {"confDir": %q, "poolType": "foobar"}}`, "", false), + ) }) }) From 3917ecc03e16857232491c30d9a85b4bea5330b8 Mon Sep 17 00:00:00 2001 From: Yury Kulazhenkov Date: Tue, 11 Jun 2024 17:37:53 +0300 Subject: [PATCH 5/5] Add support for AllocateDefaultGateway Feature add `allocateDefaultGateway` flag to cni-args that activates the special behaviour. When CNI is called with this flag, the plugin will try to allocate IP address of the pool's default gateway as interface IP for the cotnainer. --- api/grpc/nvidia/ipam/node/v1/node.pb.go | 302 +++++++++++------- api/grpc/proto/nvidia/ipam/node/v1/node.proto | 12 +- pkg/cni/plugin/plugin.go | 3 + pkg/cni/plugin/plugin_test.go | 1 + pkg/cni/types/types.go | 23 +- pkg/ipam-node/handlers/allocate.go | 20 +- pkg/ipam-node/handlers/handlers.go | 4 + 7 files changed, 247 insertions(+), 118 deletions(-) diff --git a/api/grpc/nvidia/ipam/node/v1/node.pb.go b/api/grpc/nvidia/ipam/node/v1/node.pb.go index ee79383..0ebada8 100644 --- a/api/grpc/nvidia/ipam/node/v1/node.pb.go +++ b/api/grpc/nvidia/ipam/node/v1/node.pb.go @@ -145,10 +145,12 @@ type IPAMParameters struct { CniIfname string `protobuf:"bytes,3,opt,name=cni_ifname,json=cniIfname,proto3" json:"cni_ifname,omitempty"` // required, additional metadata to identify IP allocation Metadata *IPAMMetadata `protobuf:"bytes,4,opt,name=metadata,proto3" json:"metadata,omitempty"` - // type of the pool which is refered by the name in the pools field + // optional, type of the pool which is refered by the name in the pools field PoolType PoolType `protobuf:"varint,5,opt,name=pool_type,json=poolType,proto3,enum=nvidia.ipam.node.v1.PoolType" json:"pool_type,omitempty"` - // conatins IP that were statically requested + // optional, conatins IP that were statically requested RequestedIps []string `protobuf:"bytes,6,rep,name=requested_ips,json=requestedIps,proto3" json:"requested_ips,omitempty"` + // optional, conatins extra features requested for the allocation + Features *IPAMFeatures `protobuf:"bytes,7,opt,name=features,proto3" json:"features,omitempty"` } func (x *IPAMParameters) Reset() { @@ -225,6 +227,13 @@ func (x *IPAMParameters) GetRequestedIps() []string { return nil } +func (x *IPAMParameters) GetFeatures() *IPAMFeatures { + if x != nil { + return x.Features + } + return nil +} + // IPAMMetadata contains metadata for IPAM calls type IPAMMetadata struct { state protoimpl.MessageState @@ -301,6 +310,55 @@ func (x *IPAMMetadata) GetDeviceId() string { return "" } +// IPAMFeatures contains extra features requested for the IPAM call +type IPAMFeatures struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // optional, request IP of the default gateway from the pool be allocated for container + AllocateDefaultGateway bool `protobuf:"varint,1,opt,name=allocate_default_gateway,json=allocateDefaultGateway,proto3" json:"allocate_default_gateway,omitempty"` +} + +func (x *IPAMFeatures) Reset() { + *x = IPAMFeatures{} + if protoimpl.UnsafeEnabled { + mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IPAMFeatures) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IPAMFeatures) ProtoMessage() {} + +func (x *IPAMFeatures) ProtoReflect() protoreflect.Message { + mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IPAMFeatures.ProtoReflect.Descriptor instead. +func (*IPAMFeatures) Descriptor() ([]byte, []int) { + return file_nvidia_ipam_node_v1_node_proto_rawDescGZIP(), []int{3} +} + +func (x *IPAMFeatures) GetAllocateDefaultGateway() bool { + if x != nil { + return x.AllocateDefaultGateway + } + return false +} + // IsAllocatedRequest contains parameters for IsAllocated rpc call type IsAllocatedRequest struct { state protoimpl.MessageState @@ -314,7 +372,7 @@ type IsAllocatedRequest struct { func (x *IsAllocatedRequest) Reset() { *x = IsAllocatedRequest{} if protoimpl.UnsafeEnabled { - mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[3] + mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -327,7 +385,7 @@ func (x *IsAllocatedRequest) String() string { func (*IsAllocatedRequest) ProtoMessage() {} func (x *IsAllocatedRequest) ProtoReflect() protoreflect.Message { - mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[3] + mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -340,7 +398,7 @@ func (x *IsAllocatedRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use IsAllocatedRequest.ProtoReflect.Descriptor instead. func (*IsAllocatedRequest) Descriptor() ([]byte, []int) { - return file_nvidia_ipam_node_v1_node_proto_rawDescGZIP(), []int{3} + return file_nvidia_ipam_node_v1_node_proto_rawDescGZIP(), []int{4} } func (x *IsAllocatedRequest) GetParameters() *IPAMParameters { @@ -363,7 +421,7 @@ type DeallocateRequest struct { func (x *DeallocateRequest) Reset() { *x = DeallocateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[4] + mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -376,7 +434,7 @@ func (x *DeallocateRequest) String() string { func (*DeallocateRequest) ProtoMessage() {} func (x *DeallocateRequest) ProtoReflect() protoreflect.Message { - mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[4] + mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -389,7 +447,7 @@ func (x *DeallocateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeallocateRequest.ProtoReflect.Descriptor instead. func (*DeallocateRequest) Descriptor() ([]byte, []int) { - return file_nvidia_ipam_node_v1_node_proto_rawDescGZIP(), []int{4} + return file_nvidia_ipam_node_v1_node_proto_rawDescGZIP(), []int{5} } func (x *DeallocateRequest) GetParameters() *IPAMParameters { @@ -412,7 +470,7 @@ type AllocateResponse struct { func (x *AllocateResponse) Reset() { *x = AllocateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[5] + mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -425,7 +483,7 @@ func (x *AllocateResponse) String() string { func (*AllocateResponse) ProtoMessage() {} func (x *AllocateResponse) ProtoReflect() protoreflect.Message { - mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[5] + mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -438,7 +496,7 @@ func (x *AllocateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AllocateResponse.ProtoReflect.Descriptor instead. func (*AllocateResponse) Descriptor() ([]byte, []int) { - return file_nvidia_ipam_node_v1_node_proto_rawDescGZIP(), []int{5} + return file_nvidia_ipam_node_v1_node_proto_rawDescGZIP(), []int{6} } func (x *AllocateResponse) GetAllocations() []*AllocationInfo { @@ -467,7 +525,7 @@ type AllocationInfo struct { func (x *AllocationInfo) Reset() { *x = AllocationInfo{} if protoimpl.UnsafeEnabled { - mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[6] + mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -480,7 +538,7 @@ func (x *AllocationInfo) String() string { func (*AllocationInfo) ProtoMessage() {} func (x *AllocationInfo) ProtoReflect() protoreflect.Message { - mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[6] + mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -493,7 +551,7 @@ func (x *AllocationInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use AllocationInfo.ProtoReflect.Descriptor instead. func (*AllocationInfo) Descriptor() ([]byte, []int) { - return file_nvidia_ipam_node_v1_node_proto_rawDescGZIP(), []int{6} + return file_nvidia_ipam_node_v1_node_proto_rawDescGZIP(), []int{7} } func (x *AllocationInfo) GetPool() string { @@ -534,7 +592,7 @@ type IsAllocatedResponse struct { func (x *IsAllocatedResponse) Reset() { *x = IsAllocatedResponse{} if protoimpl.UnsafeEnabled { - mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[7] + mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -547,7 +605,7 @@ func (x *IsAllocatedResponse) String() string { func (*IsAllocatedResponse) ProtoMessage() {} func (x *IsAllocatedResponse) ProtoReflect() protoreflect.Message { - mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[7] + mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -560,7 +618,7 @@ func (x *IsAllocatedResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use IsAllocatedResponse.ProtoReflect.Descriptor instead. func (*IsAllocatedResponse) Descriptor() ([]byte, []int) { - return file_nvidia_ipam_node_v1_node_proto_rawDescGZIP(), []int{7} + return file_nvidia_ipam_node_v1_node_proto_rawDescGZIP(), []int{8} } // DeallocateReply contains reply for Deallocate rpc call @@ -573,7 +631,7 @@ type DeallocateResponse struct { func (x *DeallocateResponse) Reset() { *x = DeallocateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[8] + mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -586,7 +644,7 @@ func (x *DeallocateResponse) String() string { func (*DeallocateResponse) ProtoMessage() {} func (x *DeallocateResponse) ProtoReflect() protoreflect.Message { - mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[8] + mi := &file_nvidia_ipam_node_v1_node_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -599,7 +657,7 @@ func (x *DeallocateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeallocateResponse.ProtoReflect.Descriptor instead. func (*DeallocateResponse) Descriptor() ([]byte, []int) { - return file_nvidia_ipam_node_v1_node_proto_rawDescGZIP(), []int{8} + return file_nvidia_ipam_node_v1_node_proto_rawDescGZIP(), []int{9} } var File_nvidia_ipam_node_v1_node_proto protoreflect.FileDescriptor @@ -613,7 +671,7 @@ var file_nvidia_ipam_node_v1_node_proto_rawDesc = []byte{ 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x50, 0x41, 0x4d, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, - 0x73, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0x8e, 0x02, + 0x73, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0xcd, 0x02, 0x0a, 0x0e, 0x49, 0x50, 0x41, 0x4d, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x6f, 0x6f, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x70, 0x6f, 0x6f, 0x6c, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6e, 0x69, 0x5f, 0x63, 0x6f, @@ -630,71 +688,79 @@ var file_nvidia_ipam_node_v1_node_proto_rawDesc = []byte{ 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x6f, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x70, 0x6f, 0x6f, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x70, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x0c, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x49, 0x70, 0x73, 0x22, 0x99, - 0x01, 0x0a, 0x0c, 0x49, 0x50, 0x41, 0x4d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, - 0x20, 0x0a, 0x0c, 0x6b, 0x38, 0x73, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6b, 0x38, 0x73, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x6b, 0x38, 0x73, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6b, 0x38, - 0x73, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1e, 0x0a, - 0x0b, 0x6b, 0x38, 0x73, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x6b, 0x38, 0x73, 0x50, 0x6f, 0x64, 0x55, 0x69, 0x64, 0x12, 0x1b, 0x0a, - 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x22, 0x59, 0x0a, 0x12, 0x49, 0x73, - 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x43, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, - 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x50, 0x41, 0x4d, 0x50, - 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, - 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0x58, 0x0a, 0x11, 0x44, 0x65, 0x61, 0x6c, 0x6c, 0x6f, 0x63, - 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x0a, 0x70, 0x61, - 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, - 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x50, 0x41, 0x4d, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, - 0x65, 0x72, 0x73, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, - 0x59, 0x0a, 0x10, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0b, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, - 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, - 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x61, - 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8a, 0x01, 0x0a, 0x0e, 0x41, - 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, - 0x04, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6f, 0x6f, - 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, - 0x70, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x3a, 0x0a, 0x09, 0x70, - 0x6f, 0x6f, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, + 0x52, 0x0c, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x49, 0x70, 0x73, 0x12, 0x3d, + 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x21, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, + 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x50, 0x41, 0x4d, 0x46, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0x99, 0x01, + 0x0a, 0x0c, 0x49, 0x50, 0x41, 0x4d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x20, + 0x0a, 0x0c, 0x6b, 0x38, 0x73, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6b, 0x38, 0x73, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x2a, 0x0a, 0x11, 0x6b, 0x38, 0x73, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6b, 0x38, 0x73, + 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1e, 0x0a, 0x0b, + 0x6b, 0x38, 0x73, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x6b, 0x38, 0x73, 0x50, 0x6f, 0x64, 0x55, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x22, 0x48, 0x0a, 0x0c, 0x49, 0x50, 0x41, + 0x4d, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x18, 0x61, 0x6c, 0x6c, + 0x6f, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x67, 0x61, + 0x74, 0x65, 0x77, 0x61, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x61, 0x6c, 0x6c, + 0x6f, 0x63, 0x61, 0x74, 0x65, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x47, 0x61, 0x74, 0x65, + 0x77, 0x61, 0x79, 0x22, 0x59, 0x0a, 0x12, 0x49, 0x73, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, + 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x0a, 0x70, 0x61, 0x72, + 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, + 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x50, 0x41, 0x4d, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, + 0x72, 0x73, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0x58, + 0x0a, 0x11, 0x44, 0x65, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, + 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x50, + 0x41, 0x4d, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x70, 0x61, + 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0x59, 0x0a, 0x10, 0x41, 0x6c, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0b, + 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x23, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, + 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x22, 0x8a, 0x01, 0x0a, 0x0e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6f, 0x6f, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x61, + 0x74, 0x65, 0x77, 0x61, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x12, 0x3a, 0x0a, 0x09, 0x70, 0x6f, 0x6f, 0x6c, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, + 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, + 0x6f, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x70, 0x6f, 0x6f, 0x6c, 0x54, 0x79, 0x70, 0x65, + 0x22, 0x15, 0x0a, 0x13, 0x49, 0x73, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x64, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x14, 0x0a, 0x12, 0x44, 0x65, 0x61, 0x6c, 0x6c, + 0x6f, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x53, 0x0a, + 0x08, 0x50, 0x6f, 0x6f, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x15, 0x50, 0x4f, 0x4f, + 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, + 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x4f, 0x4f, 0x4c, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x49, 0x50, 0x50, 0x4f, 0x4f, 0x4c, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x50, 0x4f, + 0x4f, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x49, 0x44, 0x52, 0x50, 0x4f, 0x4f, 0x4c, + 0x10, 0x02, 0x32, 0xad, 0x02, 0x0a, 0x0b, 0x49, 0x50, 0x41, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x12, 0x59, 0x0a, 0x08, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x12, 0x24, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x6f, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x70, - 0x6f, 0x6f, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x22, 0x15, 0x0a, 0x13, 0x49, 0x73, 0x41, 0x6c, 0x6c, - 0x6f, 0x63, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x14, - 0x0a, 0x12, 0x44, 0x65, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x53, 0x0a, 0x08, 0x50, 0x6f, 0x6f, 0x6c, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x19, 0x0a, 0x15, 0x50, 0x4f, 0x4f, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, - 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x50, - 0x4f, 0x4f, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x50, 0x50, 0x4f, 0x4f, 0x4c, 0x10, - 0x01, 0x12, 0x16, 0x0a, 0x12, 0x50, 0x4f, 0x4f, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, - 0x49, 0x44, 0x52, 0x50, 0x4f, 0x4f, 0x4c, 0x10, 0x02, 0x32, 0xad, 0x02, 0x0a, 0x0b, 0x49, 0x50, - 0x41, 0x4d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x59, 0x0a, 0x08, 0x41, 0x6c, 0x6c, - 0x6f, 0x63, 0x61, 0x74, 0x65, 0x12, 0x24, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, - 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, - 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x6e, 0x76, - 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a, 0x0b, 0x49, 0x73, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, - 0x74, 0x65, 0x64, 0x12, 0x27, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, - 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x73, 0x41, 0x6c, 0x6c, 0x6f, - 0x63, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6e, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, + 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a, + 0x0b, 0x49, 0x73, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x27, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x73, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0a, 0x44, 0x65, 0x61, 0x6c, - 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x12, 0x26, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, - 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x61, - 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, - 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, + 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x73, 0x41, 0x6c, + 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x5f, 0x0a, 0x0a, 0x44, 0x65, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x12, + 0x26, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, + 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6e, 0x76, 0x69, 0x64, 0x69, 0x61, + 0x2e, 0x69, 0x70, 0x61, 0x6d, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, + 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -710,38 +776,40 @@ func file_nvidia_ipam_node_v1_node_proto_rawDescGZIP() []byte { } var file_nvidia_ipam_node_v1_node_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_nvidia_ipam_node_v1_node_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_nvidia_ipam_node_v1_node_proto_msgTypes = make([]protoimpl.MessageInfo, 10) var file_nvidia_ipam_node_v1_node_proto_goTypes = []interface{}{ (PoolType)(0), // 0: nvidia.ipam.node.v1.PoolType (*AllocateRequest)(nil), // 1: nvidia.ipam.node.v1.AllocateRequest (*IPAMParameters)(nil), // 2: nvidia.ipam.node.v1.IPAMParameters (*IPAMMetadata)(nil), // 3: nvidia.ipam.node.v1.IPAMMetadata - (*IsAllocatedRequest)(nil), // 4: nvidia.ipam.node.v1.IsAllocatedRequest - (*DeallocateRequest)(nil), // 5: nvidia.ipam.node.v1.DeallocateRequest - (*AllocateResponse)(nil), // 6: nvidia.ipam.node.v1.AllocateResponse - (*AllocationInfo)(nil), // 7: nvidia.ipam.node.v1.AllocationInfo - (*IsAllocatedResponse)(nil), // 8: nvidia.ipam.node.v1.IsAllocatedResponse - (*DeallocateResponse)(nil), // 9: nvidia.ipam.node.v1.DeallocateResponse + (*IPAMFeatures)(nil), // 4: nvidia.ipam.node.v1.IPAMFeatures + (*IsAllocatedRequest)(nil), // 5: nvidia.ipam.node.v1.IsAllocatedRequest + (*DeallocateRequest)(nil), // 6: nvidia.ipam.node.v1.DeallocateRequest + (*AllocateResponse)(nil), // 7: nvidia.ipam.node.v1.AllocateResponse + (*AllocationInfo)(nil), // 8: nvidia.ipam.node.v1.AllocationInfo + (*IsAllocatedResponse)(nil), // 9: nvidia.ipam.node.v1.IsAllocatedResponse + (*DeallocateResponse)(nil), // 10: nvidia.ipam.node.v1.DeallocateResponse } var file_nvidia_ipam_node_v1_node_proto_depIdxs = []int32{ 2, // 0: nvidia.ipam.node.v1.AllocateRequest.parameters:type_name -> nvidia.ipam.node.v1.IPAMParameters 3, // 1: nvidia.ipam.node.v1.IPAMParameters.metadata:type_name -> nvidia.ipam.node.v1.IPAMMetadata 0, // 2: nvidia.ipam.node.v1.IPAMParameters.pool_type:type_name -> nvidia.ipam.node.v1.PoolType - 2, // 3: nvidia.ipam.node.v1.IsAllocatedRequest.parameters:type_name -> nvidia.ipam.node.v1.IPAMParameters - 2, // 4: nvidia.ipam.node.v1.DeallocateRequest.parameters:type_name -> nvidia.ipam.node.v1.IPAMParameters - 7, // 5: nvidia.ipam.node.v1.AllocateResponse.allocations:type_name -> nvidia.ipam.node.v1.AllocationInfo - 0, // 6: nvidia.ipam.node.v1.AllocationInfo.pool_type:type_name -> nvidia.ipam.node.v1.PoolType - 1, // 7: nvidia.ipam.node.v1.IPAMService.Allocate:input_type -> nvidia.ipam.node.v1.AllocateRequest - 4, // 8: nvidia.ipam.node.v1.IPAMService.IsAllocated:input_type -> nvidia.ipam.node.v1.IsAllocatedRequest - 5, // 9: nvidia.ipam.node.v1.IPAMService.Deallocate:input_type -> nvidia.ipam.node.v1.DeallocateRequest - 6, // 10: nvidia.ipam.node.v1.IPAMService.Allocate:output_type -> nvidia.ipam.node.v1.AllocateResponse - 8, // 11: nvidia.ipam.node.v1.IPAMService.IsAllocated:output_type -> nvidia.ipam.node.v1.IsAllocatedResponse - 9, // 12: nvidia.ipam.node.v1.IPAMService.Deallocate:output_type -> nvidia.ipam.node.v1.DeallocateResponse - 10, // [10:13] is the sub-list for method output_type - 7, // [7:10] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name + 4, // 3: nvidia.ipam.node.v1.IPAMParameters.features:type_name -> nvidia.ipam.node.v1.IPAMFeatures + 2, // 4: nvidia.ipam.node.v1.IsAllocatedRequest.parameters:type_name -> nvidia.ipam.node.v1.IPAMParameters + 2, // 5: nvidia.ipam.node.v1.DeallocateRequest.parameters:type_name -> nvidia.ipam.node.v1.IPAMParameters + 8, // 6: nvidia.ipam.node.v1.AllocateResponse.allocations:type_name -> nvidia.ipam.node.v1.AllocationInfo + 0, // 7: nvidia.ipam.node.v1.AllocationInfo.pool_type:type_name -> nvidia.ipam.node.v1.PoolType + 1, // 8: nvidia.ipam.node.v1.IPAMService.Allocate:input_type -> nvidia.ipam.node.v1.AllocateRequest + 5, // 9: nvidia.ipam.node.v1.IPAMService.IsAllocated:input_type -> nvidia.ipam.node.v1.IsAllocatedRequest + 6, // 10: nvidia.ipam.node.v1.IPAMService.Deallocate:input_type -> nvidia.ipam.node.v1.DeallocateRequest + 7, // 11: nvidia.ipam.node.v1.IPAMService.Allocate:output_type -> nvidia.ipam.node.v1.AllocateResponse + 9, // 12: nvidia.ipam.node.v1.IPAMService.IsAllocated:output_type -> nvidia.ipam.node.v1.IsAllocatedResponse + 10, // 13: nvidia.ipam.node.v1.IPAMService.Deallocate:output_type -> nvidia.ipam.node.v1.DeallocateResponse + 11, // [11:14] is the sub-list for method output_type + 8, // [8:11] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name } func init() { file_nvidia_ipam_node_v1_node_proto_init() } @@ -787,7 +855,7 @@ func file_nvidia_ipam_node_v1_node_proto_init() { } } file_nvidia_ipam_node_v1_node_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*IsAllocatedRequest); i { + switch v := v.(*IPAMFeatures); i { case 0: return &v.state case 1: @@ -799,7 +867,7 @@ func file_nvidia_ipam_node_v1_node_proto_init() { } } file_nvidia_ipam_node_v1_node_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeallocateRequest); i { + switch v := v.(*IsAllocatedRequest); i { case 0: return &v.state case 1: @@ -811,7 +879,7 @@ func file_nvidia_ipam_node_v1_node_proto_init() { } } file_nvidia_ipam_node_v1_node_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AllocateResponse); i { + switch v := v.(*DeallocateRequest); i { case 0: return &v.state case 1: @@ -823,7 +891,7 @@ func file_nvidia_ipam_node_v1_node_proto_init() { } } file_nvidia_ipam_node_v1_node_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AllocationInfo); i { + switch v := v.(*AllocateResponse); i { case 0: return &v.state case 1: @@ -835,7 +903,7 @@ func file_nvidia_ipam_node_v1_node_proto_init() { } } file_nvidia_ipam_node_v1_node_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*IsAllocatedResponse); i { + switch v := v.(*AllocationInfo); i { case 0: return &v.state case 1: @@ -847,6 +915,18 @@ func file_nvidia_ipam_node_v1_node_proto_init() { } } file_nvidia_ipam_node_v1_node_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IsAllocatedResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_nvidia_ipam_node_v1_node_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DeallocateResponse); i { case 0: return &v.state @@ -865,7 +945,7 @@ func file_nvidia_ipam_node_v1_node_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_nvidia_ipam_node_v1_node_proto_rawDesc, NumEnums: 1, - NumMessages: 9, + NumMessages: 10, NumExtensions: 0, NumServices: 1, }, diff --git a/api/grpc/proto/nvidia/ipam/node/v1/node.proto b/api/grpc/proto/nvidia/ipam/node/v1/node.proto index 074d4af..1c27a2a 100644 --- a/api/grpc/proto/nvidia/ipam/node/v1/node.proto +++ b/api/grpc/proto/nvidia/ipam/node/v1/node.proto @@ -71,10 +71,12 @@ message IPAMParameters { string cni_ifname = 3; // required, additional metadata to identify IP allocation IPAMMetadata metadata = 4; - // type of the pool which is refered by the name in the pools field + // optional, type of the pool which is refered by the name in the pools field PoolType pool_type = 5; - // conatins IP that were statically requested + // optional, conatins IP that were statically requested repeated string requested_ips = 6; + // optional, conatins extra features requested for the allocation + IPAMFeatures features = 7; } // IPAMMetadata contains metadata for IPAM calls @@ -89,6 +91,12 @@ message IPAMMetadata { string device_id = 4; } +// IPAMFeatures contains extra features requested for the IPAM call +message IPAMFeatures { + // optional, request IP of the default gateway from the pool be allocated for container + bool allocate_default_gateway = 1; +} + // IsAllocatedRequest contains parameters for IsAllocated rpc call message IsAllocatedRequest { // required, IPAMParameters contains parameters IPAM parameters related to the request diff --git a/pkg/cni/plugin/plugin.go b/pkg/cni/plugin/plugin.go index 1e87617..01b7c8e 100644 --- a/pkg/cni/plugin/plugin.go +++ b/pkg/cni/plugin/plugin.go @@ -213,6 +213,9 @@ func cniConfToGRPCReq(conf *types.NetConf, args *skel.CmdArgs) *nodev1.IPAMParam DeviceId: conf.DeviceID, }, RequestedIps: requestedIPs, + Features: &nodev1.IPAMFeatures{ + AllocateDefaultGateway: conf.IPAM.Features.AllocateDefaultGateway, + }, } if req.Metadata.K8SPodUid == "" { log.Warningf("K8S_POD_UID is not provided by container runtime") diff --git a/pkg/cni/plugin/plugin_test.go b/pkg/cni/plugin/plugin_test.go index dba871d..6dc1091 100644 --- a/pkg/cni/plugin/plugin_test.go +++ b/pkg/cni/plugin/plugin_test.go @@ -100,6 +100,7 @@ var _ = Describe("plugin tests", func() { K8SPodNamespace: "test", }, RequestedIps: []string{}, + Features: &nodev1.IPAMFeatures{}, }}).Return(&nodev1.AllocateResponse{ Allocations: []*nodev1.AllocationInfo{{ Pool: "my-pool", diff --git a/pkg/cni/types/types.go b/pkg/cni/types/types.go index 7af204b..668b022 100644 --- a/pkg/cni/types/types.go +++ b/pkg/cni/types/types.go @@ -77,6 +77,12 @@ type IPAMConf struct { } `json:"-"` // requested IPs from CNI_ARGS, args and capabilities RequestedIPs []net.IP `json:"-"` + // internal representation of the requested features + Features struct { + // request to allocate pool's default gateway as + // interface IP address for the container + AllocateDefaultGateway bool + } `json:"-"` } // NetConf is CNI network config @@ -95,9 +101,10 @@ type NetConf struct { // IPAMArgs holds arguments from stdin args["cni"] type IPAMArgs struct { - IPs []string `json:"ips"` - PoolNames []string `json:"poolNames"` - PoolType string `json:"poolType"` + IPs []string `json:"ips"` + PoolNames []string `json:"poolNames"` + PoolType string `json:"poolType"` + AllocateDefaultGateway bool `json:"allocateDefaultGateway"` } // IPAMEnvArgs holds arguments from CNI_ARGS env variable @@ -173,6 +180,16 @@ func (cl *confLoader) LoadConf(args *skel.CmdArgs) (*NetConf, error) { return nil, err } + if n.Args != nil && n.Args.ArgsCNI != nil { + n.IPAM.Features.AllocateDefaultGateway = n.Args.ArgsCNI.AllocateDefaultGateway + } + + if n.IPAM.Features.AllocateDefaultGateway { + if len(n.IPAM.RequestedIPs) > 0 { + return nil, fmt.Errorf("allocatedDefaultGateway can't be used together with static IP request") + } + } + n.IPAM.Pools, err = getPools(n) if err != nil { return nil, err diff --git a/pkg/ipam-node/handlers/allocate.go b/pkg/ipam-node/handlers/allocate.go index d47c8ed..19570ee 100644 --- a/pkg/ipam-node/handlers/allocate.go +++ b/pkg/ipam-node/handlers/allocate.go @@ -135,11 +135,12 @@ func (h *Handlers) allocateInPool(poolName string, reqLog logr.Logger, if err != nil || subnet == nil || subnet.IP == nil || subnet.Mask == nil { return PoolAlloc{}, poolCfgError(poolLog, poolName, poolType, "invalid subnet") } + gateway := net.ParseIP(poolCfg.Gateway) rangeSet := &allocator.RangeSet{allocator.Range{ RangeStart: rangeStart, RangeEnd: rangeEnd, Subnet: cniTypes.IPNet(*subnet), - Gateway: net.ParseIP(poolCfg.Gateway), + Gateway: gateway, }} if err := rangeSet.Canonicalize(); err != nil { return PoolAlloc{}, poolCfgError(poolLog, poolName, poolType, @@ -152,6 +153,16 @@ func (h *Handlers) allocateInPool(poolName string, reqLog logr.Logger, break } } + + if params.Features != nil && params.Features.AllocateDefaultGateway { + if gateway == nil { + return PoolAlloc{}, status.Errorf(codes.InvalidArgument, + "pool without gateway can't be used with allocate_default_gateway feature,"+ + "pool \"%s\", poolType \"%s\"", poolName, poolType) + } + selectedStaticIP = gateway + } + exclusionRangeSet := make(allocator.RangeSet, 0, len(poolCfg.Exclusions)) for _, e := range poolCfg.Exclusions { exclusionRangeSet = append(exclusionRangeSet, allocator.Range{ @@ -197,8 +208,13 @@ func (h *Handlers) allocateInPool(poolName string, reqLog logr.Logger, return PoolAlloc{}, status.Errorf(codes.Internal, "failed to allocate IP address in pool \"%s\", poolType \"%s\"", poolName, poolType) } - poolLog.Info("IP address allocated", "allocation", result.String()) + if params.Features != nil && params.Features.AllocateDefaultGateway { + // TODO (ykulazhenkov): do we want to keep gateway in this case? + // if we will return gateway here, the container will have same IP as interface address and as gateway + result.Gateway = nil + } + poolLog.Info("IP address allocated", "allocation", result.String()) return PoolAlloc{ Pool: poolName, IPConfig: result, diff --git a/pkg/ipam-node/handlers/handlers.go b/pkg/ipam-node/handlers/handlers.go index 98734fa..c3a421c 100644 --- a/pkg/ipam-node/handlers/handlers.go +++ b/pkg/ipam-node/handlers/handlers.go @@ -120,6 +120,10 @@ func validateReq(req paramsGetter) error { return fieldsIsInvalidError(fmt.Sprintf("parameters.requested_ips[%d]", i)) } } + if params.Features != nil && params.Features.AllocateDefaultGateway && len(params.RequestedIps) > 0 { + return status.Errorf(codes.InvalidArgument, + "parameters.features.allocate_default_gateway can't be used together with static IPs") + } return nil }