diff --git a/api/grpc/nvidia/ipam/node/v1/node.pb.go b/api/grpc/nvidia/ipam/node/v1/node.pb.go index e355b89..0ebada8 100644 --- a/api/grpc/nvidia/ipam/node/v1/node.pb.go +++ b/api/grpc/nvidia/ipam/node/v1/node.pb.go @@ -145,8 +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"` + // 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() { @@ -216,6 +220,20 @@ func (x *IPAMParameters) GetPoolType() PoolType { return PoolType_POOL_TYPE_UNSPECIFIED } +func (x *IPAMParameters) GetRequestedIps() []string { + if x != nil { + return x.RequestedIps + } + 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 @@ -292,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 @@ -305,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) } @@ -318,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 { @@ -331,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 { @@ -354,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) } @@ -367,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 { @@ -380,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 { @@ -403,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) } @@ -416,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 { @@ -429,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 { @@ -458,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) } @@ -471,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 { @@ -484,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 { @@ -525,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) } @@ -538,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 { @@ -551,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 @@ -564,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) } @@ -577,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 { @@ -590,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 @@ -604,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, 0xe9, 0x01, + 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, @@ -619,70 +686,81 @@ 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, 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, 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, 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, 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 ( @@ -698,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() } @@ -775,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: @@ -787,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: @@ -799,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: @@ -811,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: @@ -823,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: @@ -835,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 @@ -853,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 f824b9f..1c27a2a 100644 --- a/api/grpc/proto/nvidia/ipam/node/v1/node.proto +++ b/api/grpc/proto/nvidia/ipam/node/v1/node.proto @@ -71,8 +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; + // 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 @@ -87,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 fa791a4..01b7c8e 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,40 +190,38 @@ 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 } + + 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, 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") + RequestedIps: requestedIPs, + Features: &nodev1.IPAMFeatures{ + AllocateDefaultGateway: conf.IPAM.Features.AllocateDefaultGateway, + }, } 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 +232,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..6dc1091 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"}, @@ -91,6 +99,8 @@ var _ = Describe("plugin tests", func() { K8SPodName: "test", K8SPodNamespace: "test", }, + RequestedIps: []string{}, + Features: &nodev1.IPAMFeatures{}, }}).Return(&nodev1.AllocateResponse{ Allocations: []*nodev1.AllocationInfo{{ Pool: "my-pool", @@ -106,7 +116,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 +125,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..668b022 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,53 @@ 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:"-"` + // 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 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"` + PoolNames []string `json:"poolNames"` + PoolType string `json:"poolType"` + AllocateDefaultGateway bool `json:"allocateDefaultGateway"` +} + +// 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 +123,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,22 +160,91 @@ func (cl *confLoader) LoadConf(bytes []byte) (*NetConf, error) { } cl.overlayConf(defaultConf, n.IPAM) - n.IPAM.Pools, err = parsePoolName(n.IPAM.PoolName) - if err != nil { + // 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.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) + 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 + } + n.IPAM.PoolType, err = getPoolType(n) + if err != nil { + return nil, err + } return n, nil } -func parsePoolName(poolName string) ([]string, error) { - pools := strings.Split(poolName, ",") +// 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) +} + +// 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") } @@ -147,6 +256,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 2f44142..21b3a46 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,120 @@ 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), + ) + 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), + ) }) }) 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..19570ee 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) @@ -116,16 +135,34 @@ 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, fmt.Sprintf("invalid range config: %s", err.Error())) } + var selectedStaticIP net.IP + for _, ipAddr := range requestedIPs { + if subnet.Contains(ipAddr) { + selectedStaticIP = ipAddr + 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{ @@ -151,7 +188,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,11 +200,21 @@ 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) } - 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 3298e94..c3a421c 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,16 @@ 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)) + } + } + 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 } 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"),