diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index edf6b1d97..fc883d97f 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -393,7 +393,9 @@ func (in *ServerResources) DeepCopyInto(out *ServerResources) { if in.Ports != nil { in, out := &in.Ports, &out.Ports *out = make([]v1beta1.PortStatus, len(*in)) - copy(*out, *in) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } } diff --git a/api/v1alpha7/conversion_test.go b/api/v1alpha7/conversion_test.go index 00ee84274..c80701e19 100644 --- a/api/v1alpha7/conversion_test.go +++ b/api/v1alpha7/conversion_test.go @@ -96,6 +96,12 @@ func TestFuzzyConversion(t *testing.T) { // None of the following identityRef fields have ever been set in v1alpha7 identityRef.Region = "" }, + func(portOpts *infrav1.PortOpts, c fuzz.Continue) { + c.FuzzNoCustom(portOpts) + + // None of the following PortOpts fields have ever been set in v1alpha7 + portOpts.Subports = nil + }, } return slices.Concat(v1alpha7FuzzerFuncs, testhelpers.InfraV1FuzzerFuncs()) diff --git a/api/v1alpha7/types_conversion.go b/api/v1alpha7/types_conversion.go index e7f4b1f25..b7fad9a51 100644 --- a/api/v1alpha7/types_conversion.go +++ b/api/v1alpha7/types_conversion.go @@ -18,10 +18,12 @@ package v1alpha7 import ( "errors" + unsafe "unsafe" apiconversion "k8s.io/apimachinery/pkg/conversion" infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1" + v1beta1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1" "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/conversioncommon" "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/optional" ) @@ -426,6 +428,35 @@ func Convert_v1alpha7_PortOpts_To_v1beta1_PortOpts(in *PortOpts, out *infrav1.Po } } + if in.Network != nil { + in, out := &in.Network, &out.Network + *out = new(v1beta1.NetworkParam) + if err := Convert_v1alpha7_NetworkFilter_To_v1beta1_NetworkParam(*in, *out, s); err != nil { + return err + } + } else { + out.Network = nil + } + if err := optional.Convert_string_To_optional_String(&in.NameSuffix, &out.NameSuffix, s); err != nil { + return err + } + if err := optional.Convert_string_To_optional_String(&in.Description, &out.Description, s); err != nil { + return err + } + + if in.FixedIPs != nil { + in, out := &in.FixedIPs, &out.FixedIPs + *out = make([]v1beta1.FixedIP, len(*in)) + for i := range *in { + if err := Convert_v1alpha7_FixedIP_To_v1beta1_FixedIP(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.FixedIPs = nil + } + out.Tags = *(*[]string)(unsafe.Pointer(&in.Tags)) + return nil } @@ -482,6 +513,34 @@ func Convert_v1beta1_PortOpts_To_v1alpha7_PortOpts(in *infrav1.PortOpts, out *Po } } + if in.Network != nil { + in, out := &in.Network, &out.Network + *out = new(NetworkFilter) + if err := Convert_v1beta1_NetworkParam_To_v1alpha7_NetworkFilter(*in, *out, s); err != nil { + return err + } + } else { + out.Network = nil + } + if err := optional.Convert_optional_String_To_string(&in.Description, &out.Description, s); err != nil { + return err + } + if err := optional.Convert_optional_String_To_string(&in.NameSuffix, &out.NameSuffix, s); err != nil { + return err + } + if in.FixedIPs != nil { + in, out := &in.FixedIPs, &out.FixedIPs + *out = make([]FixedIP, len(*in)) + for i := range *in { + if err := Convert_v1beta1_FixedIP_To_v1alpha7_FixedIP(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.FixedIPs = nil + } + out.Tags = *(*[]string)(unsafe.Pointer(&in.Tags)) + return nil } diff --git a/api/v1alpha7/zz_generated.conversion.go b/api/v1alpha7/zz_generated.conversion.go index 109f11a15..abbfbace5 100644 --- a/api/v1alpha7/zz_generated.conversion.go +++ b/api/v1alpha7/zz_generated.conversion.go @@ -1668,34 +1668,12 @@ func Convert_v1beta1_OpenStackMachineTemplateSpec_To_v1alpha7_OpenStackMachineTe } func autoConvert_v1alpha7_PortOpts_To_v1beta1_PortOpts(in *PortOpts, out *v1beta1.PortOpts, s conversion.Scope) error { - if in.Network != nil { - in, out := &in.Network, &out.Network - *out = new(v1beta1.NetworkParam) - if err := Convert_v1alpha7_NetworkFilter_To_v1beta1_NetworkParam(*in, *out, s); err != nil { - return err - } - } else { - out.Network = nil - } - if err := optional.Convert_string_To_optional_String(&in.NameSuffix, &out.NameSuffix, s); err != nil { - return err - } - if err := optional.Convert_string_To_optional_String(&in.Description, &out.Description, s); err != nil { - return err - } + // WARNING: in.Network requires manual conversion: does not exist in peer-type + // WARNING: in.NameSuffix requires manual conversion: does not exist in peer-type + // WARNING: in.Description requires manual conversion: does not exist in peer-type // WARNING: in.AdminStateUp requires manual conversion: does not exist in peer-type // WARNING: in.MACAddress requires manual conversion: does not exist in peer-type - if in.FixedIPs != nil { - in, out := &in.FixedIPs, &out.FixedIPs - *out = make([]v1beta1.FixedIP, len(*in)) - for i := range *in { - if err := Convert_v1alpha7_FixedIP_To_v1beta1_FixedIP(&(*in)[i], &(*out)[i], s); err != nil { - return err - } - } - } else { - out.FixedIPs = nil - } + // WARNING: in.FixedIPs requires manual conversion: does not exist in peer-type // WARNING: in.SecurityGroupFilters requires manual conversion: does not exist in peer-type // WARNING: in.AllowedAddressPairs requires manual conversion: does not exist in peer-type out.Trunk = (*bool)(unsafe.Pointer(in.Trunk)) @@ -1704,42 +1682,15 @@ func autoConvert_v1alpha7_PortOpts_To_v1beta1_PortOpts(in *PortOpts, out *v1beta // WARNING: in.Profile requires manual conversion: does not exist in peer-type // WARNING: in.DisablePortSecurity requires manual conversion: does not exist in peer-type // WARNING: in.PropagateUplinkStatus requires manual conversion: does not exist in peer-type - out.Tags = *(*[]string)(unsafe.Pointer(&in.Tags)) + // WARNING: in.Tags requires manual conversion: does not exist in peer-type // WARNING: in.ValueSpecs requires manual conversion: does not exist in peer-type return nil } func autoConvert_v1beta1_PortOpts_To_v1alpha7_PortOpts(in *v1beta1.PortOpts, out *PortOpts, s conversion.Scope) error { - if in.Network != nil { - in, out := &in.Network, &out.Network - *out = new(NetworkFilter) - if err := Convert_v1beta1_NetworkParam_To_v1alpha7_NetworkFilter(*in, *out, s); err != nil { - return err - } - } else { - out.Network = nil - } - if err := optional.Convert_optional_String_To_string(&in.Description, &out.Description, s); err != nil { - return err - } - if err := optional.Convert_optional_String_To_string(&in.NameSuffix, &out.NameSuffix, s); err != nil { - return err - } - if in.FixedIPs != nil { - in, out := &in.FixedIPs, &out.FixedIPs - *out = make([]FixedIP, len(*in)) - for i := range *in { - if err := Convert_v1beta1_FixedIP_To_v1alpha7_FixedIP(&(*in)[i], &(*out)[i], s); err != nil { - return err - } - } - } else { - out.FixedIPs = nil - } - // WARNING: in.SecurityGroups requires manual conversion: does not exist in peer-type - out.Tags = *(*[]string)(unsafe.Pointer(&in.Tags)) out.Trunk = (*bool)(unsafe.Pointer(in.Trunk)) - // WARNING: in.ResolvedPortSpecFields requires manual conversion: does not exist in peer-type + // WARNING: in.Subports requires manual conversion: does not exist in peer-type + // WARNING: in.CommonPortOpts requires manual conversion: does not exist in peer-type return nil } diff --git a/api/v1beta1/types.go b/api/v1beta1/types.go index 99bc29d90..a7c9e8ed7 100644 --- a/api/v1beta1/types.go +++ b/api/v1beta1/types.go @@ -290,7 +290,7 @@ type AllocationPool struct { End string `json:"end"` } -type PortOpts struct { +type CommonPortOpts struct { // Network is a query for an openstack network that the port will be created or discovered on. // This will fail if the query returns more than one network. // +optional @@ -320,13 +320,41 @@ type PortOpts struct { // +optional Tags []string `json:"tags,omitempty"` + ResolvedPortSpecFields `json:",inline"` +} + +// PortOpts defines port parameters. +type PortOpts struct { // Trunk specifies whether trunking is enabled at the port level. If not // provided the value is inherited from the machine, or false for a // bastion host. // +optional Trunk *bool `json:"trunk,omitempty"` - ResolvedPortSpecFields `json:",inline"` + // Subports is a list of port specifications that will be created as + // subports of the trunk. + // +optional + // +listType=atomic + Subports []SubportOpts `json:"subports,omitempty"` + + CommonPortOpts `json:",inline"` +} + +// SubportOpts defines a trunk subport. +type SubportOpts struct { + // SegmentationID is the segmentation ID of the subport. E.g. VLAN ID. + // +required + // +kubebuilder:validation:Minimum:=1 + // +kubebuilder:validation:Maximum:=4094 + SegmentationID int `json:"segmentationID"` + + // SegmentationType is the segmentation type of the subport. E.g. "vlan". + // +required + // +kubebuilder:validation:Enum=vlan;flat + SegmentationType string `json:"segmentationType"` + + // Port contains parameters of the port associated with this subport + CommonPortOpts `json:",inline"` } // ResolvePortSpecFields is a convenience struct containing all fields of a @@ -393,7 +421,7 @@ type ResolvedPortSpecFields struct { } // ResolvedPortSpec is a PortOpts with all contained references fully resolved. -type ResolvedPortSpec struct { +type CommonResolvedPortSpec struct { // Name is the name of the port. Name string `json:"name"` @@ -408,10 +436,6 @@ type ResolvedPortSpec struct { // +optional Tags []string `json:"tags,omitempty"` - // Trunk specifies whether trunking is enabled at the port level. - // +optional - Trunk optional.Bool `json:"trunk,omitempty"` - // FixedIPs is a list of pairs of subnet and/or IP address to assign to the port. If specified, these must be subnets of the port's network. // +optional // +listType=atomic @@ -425,10 +449,47 @@ type ResolvedPortSpec struct { ResolvedPortSpecFields `json:",inline"` } +type ResolvedPortSpec struct { + // Trunk specifies whether trunking is enabled at the port level. + // +optional + Trunk optional.Bool `json:"trunk,omitempty"` + + // Subports is a list of resolved port specifications that will be created as + // subports of the trunk. + // +optional + // +listType=atomic + Subports []ResolvedSubportSpec `json:"subports,omitempty"` + + CommonResolvedPortSpec `json:",inline"` +} + +// ResolvedSubportSpec is a SubportOpts with all contained references fully resolved. +type ResolvedSubportSpec struct { + // SegmentationID is the segmentation ID of the subport. E.g. VLAN ID. + SegmentationID int `json:"segmentationID"` + + // SegmentationType is the segmentation type of the subport. E.g. "vlan". + SegmentationType string `json:"segmentationType"` + + // Port is a PortOpts with all contained references fully resolved. This is + // essentially port which is used as subport in trunk + CommonResolvedPortSpec `json:",inline"` +} + type PortStatus struct { // ID is the unique identifier of the port. // +required ID string `json:"id"` + + // Subports is the list of port IDs which intended to be trunk sub-ports + // +optional + Subports []SubPortStatus `json:"subports,omitempty"` +} + +type SubPortStatus struct { + // ID is the unique identifier of the trunk sub-port. + // +required + ID string `json:"id"` } type BindingProfile struct { diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index d10b48a05..8765b9a24 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -260,6 +260,89 @@ func (in *BlockDeviceVolume) DeepCopy() *BlockDeviceVolume { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CommonPortOpts) DeepCopyInto(out *CommonPortOpts) { + *out = *in + if in.Network != nil { + in, out := &in.Network, &out.Network + *out = new(NetworkParam) + (*in).DeepCopyInto(*out) + } + if in.Description != nil { + in, out := &in.Description, &out.Description + *out = new(string) + **out = **in + } + if in.NameSuffix != nil { + in, out := &in.NameSuffix, &out.NameSuffix + *out = new(string) + **out = **in + } + if in.FixedIPs != nil { + in, out := &in.FixedIPs, &out.FixedIPs + *out = make([]FixedIP, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.SecurityGroups != nil { + in, out := &in.SecurityGroups, &out.SecurityGroups + *out = make([]SecurityGroupParam, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.ResolvedPortSpecFields.DeepCopyInto(&out.ResolvedPortSpecFields) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommonPortOpts. +func (in *CommonPortOpts) DeepCopy() *CommonPortOpts { + if in == nil { + return nil + } + out := new(CommonPortOpts) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CommonResolvedPortSpec) DeepCopyInto(out *CommonResolvedPortSpec) { + *out = *in + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.FixedIPs != nil { + in, out := &in.FixedIPs, &out.FixedIPs + *out = make([]ResolvedFixedIP, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.SecurityGroups != nil { + in, out := &in.SecurityGroups, &out.SecurityGroups + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.ResolvedPortSpecFields.DeepCopyInto(&out.ResolvedPortSpecFields) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommonResolvedPortSpec. +func (in *CommonResolvedPortSpec) DeepCopy() *CommonResolvedPortSpec { + if in == nil { + return nil + } + out := new(CommonResolvedPortSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExternalRouterIPParam) DeepCopyInto(out *ExternalRouterIPParam) { *out = *in @@ -427,7 +510,9 @@ func (in *MachineResources) DeepCopyInto(out *MachineResources) { if in.Ports != nil { in, out := &in.Ports, &out.Ports *out = make([]PortStatus, len(*in)) - copy(*out, *in) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } } @@ -1222,46 +1307,19 @@ func (in *OpenStackMachineTemplateSpec) DeepCopy() *OpenStackMachineTemplateSpec // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PortOpts) DeepCopyInto(out *PortOpts) { *out = *in - if in.Network != nil { - in, out := &in.Network, &out.Network - *out = new(NetworkParam) - (*in).DeepCopyInto(*out) - } - if in.Description != nil { - in, out := &in.Description, &out.Description - *out = new(string) - **out = **in - } - if in.NameSuffix != nil { - in, out := &in.NameSuffix, &out.NameSuffix - *out = new(string) + if in.Trunk != nil { + in, out := &in.Trunk, &out.Trunk + *out = new(bool) **out = **in } - if in.FixedIPs != nil { - in, out := &in.FixedIPs, &out.FixedIPs - *out = make([]FixedIP, len(*in)) + if in.Subports != nil { + in, out := &in.Subports, &out.Subports + *out = make([]SubportOpts, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } - if in.SecurityGroups != nil { - in, out := &in.SecurityGroups, &out.SecurityGroups - *out = make([]SecurityGroupParam, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Tags != nil { - in, out := &in.Tags, &out.Tags - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Trunk != nil { - in, out := &in.Trunk, &out.Trunk - *out = new(bool) - **out = **in - } - in.ResolvedPortSpecFields.DeepCopyInto(&out.ResolvedPortSpecFields) + in.CommonPortOpts.DeepCopyInto(&out.CommonPortOpts) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PortOpts. @@ -1277,6 +1335,11 @@ func (in *PortOpts) DeepCopy() *PortOpts { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PortStatus) DeepCopyInto(out *PortStatus) { *out = *in + if in.Subports != nil { + in, out := &in.Subports, &out.Subports + *out = make([]SubPortStatus, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PortStatus. @@ -1339,29 +1402,19 @@ func (in *ResolvedMachineSpec) DeepCopy() *ResolvedMachineSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResolvedPortSpec) DeepCopyInto(out *ResolvedPortSpec) { *out = *in - if in.Tags != nil { - in, out := &in.Tags, &out.Tags - *out = make([]string, len(*in)) - copy(*out, *in) - } if in.Trunk != nil { in, out := &in.Trunk, &out.Trunk *out = new(bool) **out = **in } - if in.FixedIPs != nil { - in, out := &in.FixedIPs, &out.FixedIPs - *out = make([]ResolvedFixedIP, len(*in)) + if in.Subports != nil { + in, out := &in.Subports, &out.Subports + *out = make([]ResolvedSubportSpec, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } - if in.SecurityGroups != nil { - in, out := &in.SecurityGroups, &out.SecurityGroups - *out = make([]string, len(*in)) - copy(*out, *in) - } - in.ResolvedPortSpecFields.DeepCopyInto(&out.ResolvedPortSpecFields) + in.CommonResolvedPortSpec.DeepCopyInto(&out.CommonResolvedPortSpec) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResolvedPortSpec. @@ -1436,6 +1489,22 @@ func (in *ResolvedPortSpecFields) DeepCopy() *ResolvedPortSpecFields { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResolvedSubportSpec) DeepCopyInto(out *ResolvedSubportSpec) { + *out = *in + in.CommonResolvedPortSpec.DeepCopyInto(&out.CommonResolvedPortSpec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResolvedSubportSpec. +func (in *ResolvedSubportSpec) DeepCopy() *ResolvedSubportSpec { + if in == nil { + return nil + } + out := new(ResolvedSubportSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResourceReference) DeepCopyInto(out *ResourceReference) { *out = *in @@ -1750,6 +1819,21 @@ func (in *ServerMetadata) DeepCopy() *ServerMetadata { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SubPortStatus) DeepCopyInto(out *SubPortStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubPortStatus. +func (in *SubPortStatus) DeepCopy() *SubPortStatus { + if in == nil { + return nil + } + out := new(SubPortStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Subnet) DeepCopyInto(out *Subnet) { *out = *in @@ -1836,6 +1920,22 @@ func (in *SubnetSpec) DeepCopy() *SubnetSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SubportOpts) DeepCopyInto(out *SubportOpts) { + *out = *in + in.CommonPortOpts.DeepCopyInto(&out.CommonPortOpts) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubportOpts. +func (in *SubportOpts) DeepCopy() *SubportOpts { + if in == nil { + return nil + } + out := new(SubportOpts) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ValueSpec) DeepCopyInto(out *ValueSpec) { *out = *in diff --git a/api_violations.report b/api_violations.report index 837f09a14..e06cbfbbc 100644 --- a/api_violations.report +++ b/api_violations.report @@ -32,6 +32,7 @@ API rule violation: list_type_missing,sigs.k8s.io/cluster-api-provider-openstack API rule violation: list_type_missing,sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1,OpenStackMachineSpec,Ports API rule violation: list_type_missing,sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1,OpenStackMachineSpec,SecurityGroups API rule violation: list_type_missing,sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1,OpenStackMachineStatus,Addresses +API rule violation: list_type_missing,sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1,PortStatus,Subports API rule violation: list_type_missing,sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1,ResolvedMachineSpec,Ports API rule violation: list_type_missing,sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1,ResolvedPortSpecFields,AllowedAddressPairs API rule violation: list_type_missing,sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1,Router,IPs diff --git a/cmd/models-schema/zz_generated.openapi.go b/cmd/models-schema/zz_generated.openapi.go index 62c5fbfdc..d984aa17e 100644 --- a/cmd/models-schema/zz_generated.openapi.go +++ b/cmd/models-schema/zz_generated.openapi.go @@ -373,6 +373,8 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_BindingProfile(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BlockDeviceStorage": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_BlockDeviceStorage(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BlockDeviceVolume": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_BlockDeviceVolume(ref), + "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.CommonPortOpts": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_CommonPortOpts(ref), + "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.CommonResolvedPortSpec": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_CommonResolvedPortSpec(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ExternalRouterIPParam": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ExternalRouterIPParam(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.FilterByNeutronTags": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_FilterByNeutronTags(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.FixedIP": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_FixedIP(ref), @@ -408,6 +410,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ResolvedMachineSpec": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ResolvedMachineSpec(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ResolvedPortSpec": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ResolvedPortSpec(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ResolvedPortSpecFields": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ResolvedPortSpecFields(ref), + "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ResolvedSubportSpec": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ResolvedSubportSpec(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ResourceReference": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ResourceReference(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.RootVolume": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_RootVolume(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.Router": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_Router(ref), @@ -422,10 +425,12 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ServerGroupFilter": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ServerGroupFilter(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ServerGroupParam": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ServerGroupParam(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ServerMetadata": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ServerMetadata(ref), + "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SubPortStatus": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_SubPortStatus(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.Subnet": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_Subnet(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SubnetFilter": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_SubnetFilter(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SubnetParam": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_SubnetParam(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SubnetSpec": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_SubnetSpec(ref), + "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SubportOpts": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_SubportOpts(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ValueSpec": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ValueSpec(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.VolumeAvailabilityZone": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_VolumeAvailabilityZone(ref), "sigs.k8s.io/cluster-api/api/v1beta1.APIEndpoint": schema_sigsk8sio_cluster_api_api_v1beta1_APIEndpoint(ref), @@ -19952,6 +19957,365 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_BlockDeviceVolu } } +func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_CommonPortOpts(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "network": { + SchemaProps: spec.SchemaProps{ + Description: "Network is a query for an openstack network that the port will be created or discovered on. This will fail if the query returns more than one network.", + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkParam"), + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Description is a human-readable description for the port.", + Type: []string{"string"}, + Format: "", + }, + }, + "nameSuffix": { + SchemaProps: spec.SchemaProps{ + Description: "NameSuffix will be appended to the name of the port if specified. If unspecified, instead the 0-based index of the port in the list is used.", + Type: []string{"string"}, + Format: "", + }, + }, + "fixedIPs": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "FixedIPs is a list of pairs of subnet and/or IP address to assign to the port. If specified, these must be subnets of the port's network.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.FixedIP"), + }, + }, + }, + }, + }, + "securityGroups": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "SecurityGroups is a list of the names, uuids, filters or any combination these of the security groups to assign to the instance.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SecurityGroupParam"), + }, + }, + }, + }, + }, + "tags": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "set", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Tags applied to the port (and corresponding trunk, if a trunk is configured.) These tags are applied in addition to the instance's tags, which will also be applied to the port.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "adminStateUp": { + SchemaProps: spec.SchemaProps{ + Description: "AdminStateUp specifies whether the port should be created in the up (true) or down (false) state. The default is up.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "macAddress": { + SchemaProps: spec.SchemaProps{ + Description: "MACAddress specifies the MAC address of the port. If not specified, the MAC address will be generated.", + Type: []string{"string"}, + Format: "", + }, + }, + "allowedAddressPairs": { + SchemaProps: spec.SchemaProps{ + Description: "AllowedAddressPairs is a list of address pairs which Neutron will allow the port to send traffic from in addition to the port's addresses. If not specified, the MAC Address will be the MAC Address of the port. Depending on the configuration of Neutron, it may be supported to specify a CIDR instead of a specific IP address.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.AddressPair"), + }, + }, + }, + }, + }, + "hostID": { + SchemaProps: spec.SchemaProps{ + Description: "HostID specifies the ID of the host where the port resides.", + Type: []string{"string"}, + Format: "", + }, + }, + "vnicType": { + SchemaProps: spec.SchemaProps{ + Description: "VNICType specifies the type of vNIC which this port should be attached to. This is used to determine which mechanism driver(s) to be used to bind the port. The valid values are normal, macvtap, direct, baremetal, direct-physical, virtio-forwarder, smart-nic and remote-managed, although these values will not be validated in this API to ensure compatibility with future neutron changes or custom implementations. What type of vNIC is actually available depends on deployments. If not specified, the Neutron default value is used.", + Type: []string{"string"}, + Format: "", + }, + }, + "profile": { + SchemaProps: spec.SchemaProps{ + Description: "Profile is a set of key-value pairs that are used for binding details. We intentionally don't expose this as a map[string]string because we only want to enable the users to set the values of the keys that are known to work in OpenStack Networking API. See https://docs.openstack.org/api-ref/network/v2/index.html?expanded=create-port-detail#create-port To set profiles, your tenant needs permissions rule:create_port, and rule:create_port:binding:profile", + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile"), + }, + }, + "disablePortSecurity": { + SchemaProps: spec.SchemaProps{ + Description: "DisablePortSecurity enables or disables the port security when set. When not set, it takes the value of the corresponding field at the network level.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "propagateUplinkStatus": { + SchemaProps: spec.SchemaProps{ + Description: "PropageteUplinkStatus enables or disables the propagate uplink status on the port.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "valueSpecs": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "name", + }, + "x-kubernetes-list-type": "map", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Value specs are extra parameters to include in the API request with OpenStack. This is an extension point for the API, so what they do and if they are supported, depends on the specific OpenStack implementation.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ValueSpec"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.AddressPair", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.FixedIP", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkParam", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SecurityGroupParam", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ValueSpec"}, + } +} + +func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_CommonResolvedPortSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ResolvedPortSpec is a PortOpts with all contained references fully resolved.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of the port.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Description is a human-readable description for the port.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "networkID": { + SchemaProps: spec.SchemaProps{ + Description: "NetworkID is the ID of the network the port will be created in.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "tags": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "set", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Tags applied to the port (and corresponding trunk, if a trunk is configured.)", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "fixedIPs": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "FixedIPs is a list of pairs of subnet and/or IP address to assign to the port. If specified, these must be subnets of the port's network.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ResolvedFixedIP"), + }, + }, + }, + }, + }, + "securityGroups": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "SecurityGroups is a list of security group IDs to assign to the port.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "adminStateUp": { + SchemaProps: spec.SchemaProps{ + Description: "AdminStateUp specifies whether the port should be created in the up (true) or down (false) state. The default is up.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "macAddress": { + SchemaProps: spec.SchemaProps{ + Description: "MACAddress specifies the MAC address of the port. If not specified, the MAC address will be generated.", + Type: []string{"string"}, + Format: "", + }, + }, + "allowedAddressPairs": { + SchemaProps: spec.SchemaProps{ + Description: "AllowedAddressPairs is a list of address pairs which Neutron will allow the port to send traffic from in addition to the port's addresses. If not specified, the MAC Address will be the MAC Address of the port. Depending on the configuration of Neutron, it may be supported to specify a CIDR instead of a specific IP address.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.AddressPair"), + }, + }, + }, + }, + }, + "hostID": { + SchemaProps: spec.SchemaProps{ + Description: "HostID specifies the ID of the host where the port resides.", + Type: []string{"string"}, + Format: "", + }, + }, + "vnicType": { + SchemaProps: spec.SchemaProps{ + Description: "VNICType specifies the type of vNIC which this port should be attached to. This is used to determine which mechanism driver(s) to be used to bind the port. The valid values are normal, macvtap, direct, baremetal, direct-physical, virtio-forwarder, smart-nic and remote-managed, although these values will not be validated in this API to ensure compatibility with future neutron changes or custom implementations. What type of vNIC is actually available depends on deployments. If not specified, the Neutron default value is used.", + Type: []string{"string"}, + Format: "", + }, + }, + "profile": { + SchemaProps: spec.SchemaProps{ + Description: "Profile is a set of key-value pairs that are used for binding details. We intentionally don't expose this as a map[string]string because we only want to enable the users to set the values of the keys that are known to work in OpenStack Networking API. See https://docs.openstack.org/api-ref/network/v2/index.html?expanded=create-port-detail#create-port To set profiles, your tenant needs permissions rule:create_port, and rule:create_port:binding:profile", + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile"), + }, + }, + "disablePortSecurity": { + SchemaProps: spec.SchemaProps{ + Description: "DisablePortSecurity enables or disables the port security when set. When not set, it takes the value of the corresponding field at the network level.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "propagateUplinkStatus": { + SchemaProps: spec.SchemaProps{ + Description: "PropageteUplinkStatus enables or disables the propagate uplink status on the port.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "valueSpecs": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "name", + }, + "x-kubernetes-list-type": "map", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Value specs are extra parameters to include in the API request with OpenStack. This is an extension point for the API, so what they do and if they are supported, depends on the specific OpenStack implementation.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ValueSpec"), + }, + }, + }, + }, + }, + }, + Required: []string{"name", "description", "networkID"}, + }, + }, + Dependencies: []string{ + "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.AddressPair", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ResolvedFixedIP", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ValueSpec"}, + } +} + func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ExternalRouterIPParam(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -21728,8 +22092,35 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_PortOpts(ref co return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, + Description: "PortOpts defines port parameters.", + Type: []string{"object"}, Properties: map[string]spec.Schema{ + "trunk": { + SchemaProps: spec.SchemaProps{ + Description: "Trunk specifies whether trunking is enabled at the port level. If not provided the value is inherited from the machine, or false for a bastion host.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "subports": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Subports is a list of port specifications that will be created as subports of the trunk.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SubportOpts"), + }, + }, + }, + }, + }, "network": { SchemaProps: spec.SchemaProps{ Description: "Network is a query for an openstack network that the port will be created or discovered on. This will fail if the query returns more than one network.", @@ -21808,13 +22199,6 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_PortOpts(ref co }, }, }, - "trunk": { - SchemaProps: spec.SchemaProps{ - Description: "Trunk specifies whether trunking is enabled at the port level. If not provided the value is inherited from the machine, or false for a bastion host.", - Type: []string{"boolean"}, - Format: "", - }, - }, "adminStateUp": { SchemaProps: spec.SchemaProps{ Description: "AdminStateUp specifies whether the port should be created in the up (true) or down (false) state. The default is up.", @@ -21903,7 +22287,7 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_PortOpts(ref co }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.AddressPair", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.FixedIP", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkParam", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SecurityGroupParam", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ValueSpec"}, + "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.AddressPair", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.FixedIP", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkParam", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SecurityGroupParam", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SubportOpts", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ValueSpec"}, } } @@ -21921,10 +22305,26 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_PortStatus(ref Format: "", }, }, + "subports": { + SchemaProps: spec.SchemaProps{ + Description: "Subports is the list of port IDs which intended to be trunk sub-ports", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SubPortStatus"), + }, + }, + }, + }, + }, }, Required: []string{"id"}, }, }, + Dependencies: []string{ + "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SubPortStatus"}, } } @@ -22009,9 +22409,34 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ResolvedPortSpe return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "ResolvedPortSpec is a PortOpts with all contained references fully resolved.", - Type: []string{"object"}, + Type: []string{"object"}, Properties: map[string]spec.Schema{ + "trunk": { + SchemaProps: spec.SchemaProps{ + Description: "Trunk specifies whether trunking is enabled at the port level.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "subports": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Subports is a list of resolved port specifications that will be created as subports of the trunk.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ResolvedSubportSpec"), + }, + }, + }, + }, + }, "name": { SchemaProps: spec.SchemaProps{ Description: "Name is the name of the port.", @@ -22056,13 +22481,6 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ResolvedPortSpe }, }, }, - "trunk": { - SchemaProps: spec.SchemaProps{ - Description: "Trunk specifies whether trunking is enabled at the port level.", - Type: []string{"boolean"}, - Format: "", - }, - }, "fixedIPs": { VendorExtensible: spec.VendorExtensible{ Extensions: spec.Extensions{ @@ -22156,52 +22574,250 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ResolvedPortSpe Type: []string{"boolean"}, Format: "", }, - }, - "propagateUplinkStatus": { + }, + "propagateUplinkStatus": { + SchemaProps: spec.SchemaProps{ + Description: "PropageteUplinkStatus enables or disables the propagate uplink status on the port.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "valueSpecs": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "name", + }, + "x-kubernetes-list-type": "map", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Value specs are extra parameters to include in the API request with OpenStack. This is an extension point for the API, so what they do and if they are supported, depends on the specific OpenStack implementation.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ValueSpec"), + }, + }, + }, + }, + }, + }, + Required: []string{"name", "description", "networkID"}, + }, + }, + Dependencies: []string{ + "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.AddressPair", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ResolvedFixedIP", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ResolvedSubportSpec", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ValueSpec"}, + } +} + +func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ResolvedPortSpecFields(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ResolvePortSpecFields is a convenience struct containing all fields of a PortOpts which don't contain references which need to be resolved, and can therefore be shared with ResolvedPortSpec.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "adminStateUp": { + SchemaProps: spec.SchemaProps{ + Description: "AdminStateUp specifies whether the port should be created in the up (true) or down (false) state. The default is up.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "macAddress": { + SchemaProps: spec.SchemaProps{ + Description: "MACAddress specifies the MAC address of the port. If not specified, the MAC address will be generated.", + Type: []string{"string"}, + Format: "", + }, + }, + "allowedAddressPairs": { + SchemaProps: spec.SchemaProps{ + Description: "AllowedAddressPairs is a list of address pairs which Neutron will allow the port to send traffic from in addition to the port's addresses. If not specified, the MAC Address will be the MAC Address of the port. Depending on the configuration of Neutron, it may be supported to specify a CIDR instead of a specific IP address.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.AddressPair"), + }, + }, + }, + }, + }, + "hostID": { + SchemaProps: spec.SchemaProps{ + Description: "HostID specifies the ID of the host where the port resides.", + Type: []string{"string"}, + Format: "", + }, + }, + "vnicType": { + SchemaProps: spec.SchemaProps{ + Description: "VNICType specifies the type of vNIC which this port should be attached to. This is used to determine which mechanism driver(s) to be used to bind the port. The valid values are normal, macvtap, direct, baremetal, direct-physical, virtio-forwarder, smart-nic and remote-managed, although these values will not be validated in this API to ensure compatibility with future neutron changes or custom implementations. What type of vNIC is actually available depends on deployments. If not specified, the Neutron default value is used.", + Type: []string{"string"}, + Format: "", + }, + }, + "profile": { + SchemaProps: spec.SchemaProps{ + Description: "Profile is a set of key-value pairs that are used for binding details. We intentionally don't expose this as a map[string]string because we only want to enable the users to set the values of the keys that are known to work in OpenStack Networking API. See https://docs.openstack.org/api-ref/network/v2/index.html?expanded=create-port-detail#create-port To set profiles, your tenant needs permissions rule:create_port, and rule:create_port:binding:profile", + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile"), + }, + }, + "disablePortSecurity": { + SchemaProps: spec.SchemaProps{ + Description: "DisablePortSecurity enables or disables the port security when set. When not set, it takes the value of the corresponding field at the network level.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "propagateUplinkStatus": { + SchemaProps: spec.SchemaProps{ + Description: "PropageteUplinkStatus enables or disables the propagate uplink status on the port.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "valueSpecs": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "name", + }, + "x-kubernetes-list-type": "map", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Value specs are extra parameters to include in the API request with OpenStack. This is an extension point for the API, so what they do and if they are supported, depends on the specific OpenStack implementation.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ValueSpec"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.AddressPair", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ValueSpec"}, + } +} + +func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ResolvedSubportSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ResolvedSubportSpec is a SubportOpts with all contained references fully resolved.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "segmentationID": { + SchemaProps: spec.SchemaProps{ + Description: "SegmentationID is the segmentation ID of the subport. E.g. VLAN ID.", + Default: 0, + Type: []string{"integer"}, + Format: "int32", + }, + }, + "segmentationType": { + SchemaProps: spec.SchemaProps{ + Description: "SegmentationType is the segmentation type of the subport. E.g. \"vlan\".", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of the port.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Description is a human-readable description for the port.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "networkID": { + SchemaProps: spec.SchemaProps{ + Description: "NetworkID is the ID of the network the port will be created in.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "tags": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "set", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Tags applied to the port (and corresponding trunk, if a trunk is configured.)", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "fixedIPs": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, SchemaProps: spec.SchemaProps{ - Description: "PropageteUplinkStatus enables or disables the propagate uplink status on the port.", - Type: []string{"boolean"}, - Format: "", + Description: "FixedIPs is a list of pairs of subnet and/or IP address to assign to the port. If specified, these must be subnets of the port's network.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ResolvedFixedIP"), + }, + }, + }, }, }, - "valueSpecs": { + "securityGroups": { VendorExtensible: spec.VendorExtensible{ Extensions: spec.Extensions{ - "x-kubernetes-list-map-keys": []interface{}{ - "name", - }, - "x-kubernetes-list-type": "map", + "x-kubernetes-list-type": "atomic", }, }, SchemaProps: spec.SchemaProps{ - Description: "Value specs are extra parameters to include in the API request with OpenStack. This is an extension point for the API, so what they do and if they are supported, depends on the specific OpenStack implementation.", + Description: "SecurityGroups is a list of security group IDs to assign to the port.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ValueSpec"), + Default: "", + Type: []string{"string"}, + Format: "", }, }, }, }, }, - }, - Required: []string{"name", "description", "networkID"}, - }, - }, - Dependencies: []string{ - "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.AddressPair", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ResolvedFixedIP", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ValueSpec"}, - } -} - -func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ResolvedPortSpecFields(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ResolvePortSpecFields is a convenience struct containing all fields of a PortOpts which don't contain references which need to be resolved, and can therefore be shared with ResolvedPortSpec.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ "adminStateUp": { SchemaProps: spec.SchemaProps{ Description: "AdminStateUp specifies whether the port should be created in the up (true) or down (false) state. The default is up.", @@ -22287,10 +22903,11 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ResolvedPortSpe }, }, }, + Required: []string{"segmentationID", "segmentationType", "name", "description", "networkID"}, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.AddressPair", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ValueSpec"}, + "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.AddressPair", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ResolvedFixedIP", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ValueSpec"}, } } @@ -22960,6 +23577,27 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ServerMetadata( } } +func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_SubPortStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "id": { + SchemaProps: spec.SchemaProps{ + Description: "ID is the unique identifier of the trunk sub-port.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"id"}, + }, + }, + } +} + func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_Subnet(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -23230,6 +23868,200 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_SubnetSpec(ref } } +func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_SubportOpts(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SubportOpts defines a trunk subport.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "segmentationID": { + SchemaProps: spec.SchemaProps{ + Description: "SegmentationID is the segmentation ID of the subport. E.g. VLAN ID.", + Default: 0, + Type: []string{"integer"}, + Format: "int32", + }, + }, + "segmentationType": { + SchemaProps: spec.SchemaProps{ + Description: "SegmentationType is the segmentation type of the subport. E.g. \"vlan\".", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "network": { + SchemaProps: spec.SchemaProps{ + Description: "Network is a query for an openstack network that the port will be created or discovered on. This will fail if the query returns more than one network.", + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkParam"), + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Description is a human-readable description for the port.", + Type: []string{"string"}, + Format: "", + }, + }, + "nameSuffix": { + SchemaProps: spec.SchemaProps{ + Description: "NameSuffix will be appended to the name of the port if specified. If unspecified, instead the 0-based index of the port in the list is used.", + Type: []string{"string"}, + Format: "", + }, + }, + "fixedIPs": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "FixedIPs is a list of pairs of subnet and/or IP address to assign to the port. If specified, these must be subnets of the port's network.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.FixedIP"), + }, + }, + }, + }, + }, + "securityGroups": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "SecurityGroups is a list of the names, uuids, filters or any combination these of the security groups to assign to the instance.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SecurityGroupParam"), + }, + }, + }, + }, + }, + "tags": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "set", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Tags applied to the port (and corresponding trunk, if a trunk is configured.) These tags are applied in addition to the instance's tags, which will also be applied to the port.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "adminStateUp": { + SchemaProps: spec.SchemaProps{ + Description: "AdminStateUp specifies whether the port should be created in the up (true) or down (false) state. The default is up.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "macAddress": { + SchemaProps: spec.SchemaProps{ + Description: "MACAddress specifies the MAC address of the port. If not specified, the MAC address will be generated.", + Type: []string{"string"}, + Format: "", + }, + }, + "allowedAddressPairs": { + SchemaProps: spec.SchemaProps{ + Description: "AllowedAddressPairs is a list of address pairs which Neutron will allow the port to send traffic from in addition to the port's addresses. If not specified, the MAC Address will be the MAC Address of the port. Depending on the configuration of Neutron, it may be supported to specify a CIDR instead of a specific IP address.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.AddressPair"), + }, + }, + }, + }, + }, + "hostID": { + SchemaProps: spec.SchemaProps{ + Description: "HostID specifies the ID of the host where the port resides.", + Type: []string{"string"}, + Format: "", + }, + }, + "vnicType": { + SchemaProps: spec.SchemaProps{ + Description: "VNICType specifies the type of vNIC which this port should be attached to. This is used to determine which mechanism driver(s) to be used to bind the port. The valid values are normal, macvtap, direct, baremetal, direct-physical, virtio-forwarder, smart-nic and remote-managed, although these values will not be validated in this API to ensure compatibility with future neutron changes or custom implementations. What type of vNIC is actually available depends on deployments. If not specified, the Neutron default value is used.", + Type: []string{"string"}, + Format: "", + }, + }, + "profile": { + SchemaProps: spec.SchemaProps{ + Description: "Profile is a set of key-value pairs that are used for binding details. We intentionally don't expose this as a map[string]string because we only want to enable the users to set the values of the keys that are known to work in OpenStack Networking API. See https://docs.openstack.org/api-ref/network/v2/index.html?expanded=create-port-detail#create-port To set profiles, your tenant needs permissions rule:create_port, and rule:create_port:binding:profile", + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile"), + }, + }, + "disablePortSecurity": { + SchemaProps: spec.SchemaProps{ + Description: "DisablePortSecurity enables or disables the port security when set. When not set, it takes the value of the corresponding field at the network level.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "propagateUplinkStatus": { + SchemaProps: spec.SchemaProps{ + Description: "PropageteUplinkStatus enables or disables the propagate uplink status on the port.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "valueSpecs": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "name", + }, + "x-kubernetes-list-type": "map", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Value specs are extra parameters to include in the API request with OpenStack. This is an extension point for the API, so what they do and if they are supported, depends on the specific OpenStack implementation.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ValueSpec"), + }, + }, + }, + }, + }, + }, + Required: []string{"segmentationID", "segmentationType"}, + }, + }, + Dependencies: []string{ + "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.AddressPair", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.FixedIP", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkParam", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SecurityGroupParam", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ValueSpec"}, + } +} + func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ValueSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml index a2a2246aa..39fee873a 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml @@ -1625,6 +1625,7 @@ spec: Ports to be attached to the server instance. They are created if a port with the given name does not already exist. If not specified a default port will be added for the default cluster network. items: + description: PortOpts defines port parameters. properties: adminStateUp: description: AdminStateUp specifies whether the port @@ -1973,6 +1974,434 @@ spec: type: object type: array x-kubernetes-list-type: atomic + subports: + description: |- + Subports is a list of port specifications that will be created as + subports of the trunk. + items: + description: SubportOpts defines a trunk subport. + properties: + adminStateUp: + description: AdminStateUp specifies whether the + port should be created in the up (true) or down + (false) state. The default is up. + type: boolean + allowedAddressPairs: + description: |- + AllowedAddressPairs is a list of address pairs which Neutron will + allow the port to send traffic from in addition to the port's + addresses. If not specified, the MAC Address will be the MAC Address + of the port. Depending on the configuration of Neutron, it may be + supported to specify a CIDR instead of a specific IP address. + items: + properties: + ipAddress: + description: |- + IPAddress is the IP address of the allowed address pair. Depending on + the configuration of Neutron, it may be supported to specify a CIDR + instead of a specific IP address. + type: string + macAddress: + description: |- + MACAddress is the MAC address of the allowed address pair. If not + specified, the MAC address will be the MAC address of the port. + type: string + required: + - ipAddress + type: object + type: array + description: + description: Description is a human-readable description + for the port. + type: string + disablePortSecurity: + description: |- + DisablePortSecurity enables or disables the port security when set. + When not set, it takes the value of the corresponding field at the network level. + type: boolean + fixedIPs: + description: FixedIPs is a list of pairs of subnet + and/or IP address to assign to the port. If + specified, these must be subnets of the port's + network. + items: + properties: + ipAddress: + description: |- + IPAddress is a specific IP address to assign to the port. If Subnet + is also specified, IPAddress must be a valid IP address in the + subnet. If Subnet is not specified, IPAddress must be a valid IP + address in any subnet of the port's network. + type: string + subnet: + description: |- + Subnet is an openstack subnet query that will return the id of a subnet to create + the fixed IP of a port in. This query must not return more than one subnet. + maxProperties: 1 + minProperties: 1 + properties: + filter: + description: Filter specifies a filter + to select the subnet. It must match + exactly one subnet. + minProperties: 1 + properties: + cidr: + type: string + description: + type: string + gatewayIP: + type: string + ipVersion: + type: integer + ipv6AddressMode: + type: string + ipv6RAMode: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object + id: + description: ID is the uuid of the subnet. + It will not be validated. + format: uuid + type: string + type: object + type: object + type: array + x-kubernetes-list-type: atomic + hostID: + description: HostID specifies the ID of the host + where the port resides. + type: string + macAddress: + description: MACAddress specifies the MAC address + of the port. If not specified, the MAC address + will be generated. + type: string + nameSuffix: + description: NameSuffix will be appended to the + name of the port if specified. If unspecified, + instead the 0-based index of the port in the + list is used. + type: string + network: + description: |- + Network is a query for an openstack network that the port will be created or discovered on. + This will fail if the query returns more than one network. + maxProperties: 1 + minProperties: 1 + properties: + filter: + description: Filter specifies a filter to + select an OpenStack network. If provided, + cannot be empty. + minProperties: 1 + properties: + description: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object + id: + description: ID is the ID of the network to + use. If ID is provided, the other filters + cannot be provided. Must be in UUID format. + format: uuid + type: string + type: object + profile: + description: |- + Profile is a set of key-value pairs that are used for binding + details. We intentionally don't expose this as a map[string]string + because we only want to enable the users to set the values of the + keys that are known to work in OpenStack Networking API. See + https://docs.openstack.org/api-ref/network/v2/index.html?expanded=create-port-detail#create-port + To set profiles, your tenant needs permissions rule:create_port, and + rule:create_port:binding:profile + properties: + ovsHWOffload: + description: |- + OVSHWOffload enables or disables the OVS hardware offload feature. + This flag is not required on OpenStack clouds since Yoga as Nova will set it automatically when the port is attached. + See: https://bugs.launchpad.net/nova/+bug/2020813 + type: boolean + trustedVF: + description: TrustedVF enables or disables + the “trusted mode” for the VF. + type: boolean + type: object + propagateUplinkStatus: + description: PropageteUplinkStatus enables or + disables the propagate uplink status on the + port. + type: boolean + securityGroups: + description: SecurityGroups is a list of the names, + uuids, filters or any combination these of the + security groups to assign to the instance. + items: + description: SecurityGroupParam specifies an + OpenStack security group. It may be specified + by ID or filter, but not both. + maxProperties: 1 + minProperties: 1 + properties: + filter: + description: Filter specifies a query to + select an OpenStack security group. If + provided, cannot be empty. + minProperties: 1 + properties: + description: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object + id: + description: ID is the ID of the security + group to use. If ID is provided, the other + filters cannot be provided. Must be in + UUID format. + format: uuid + type: string + type: object + type: array + x-kubernetes-list-type: atomic + segmentationID: + description: SegmentationID is the segmentation + ID of the subport. E.g. VLAN ID. + maximum: 4094 + minimum: 1 + type: integer + segmentationType: + description: SegmentationType is the segmentation + type of the subport. E.g. "vlan". + enum: + - vlan + - flat + type: string + tags: + description: |- + Tags applied to the port (and corresponding trunk, if a trunk is configured.) + These tags are applied in addition to the instance's tags, which will also be applied to the port. + items: + type: string + type: array + x-kubernetes-list-type: set + valueSpecs: + description: |- + Value specs are extra parameters to include in the API request with OpenStack. + This is an extension point for the API, so what they do and if they are supported, + depends on the specific OpenStack implementation. + items: + description: ValueSpec represents a single value_spec + key-value pair. + properties: + key: + description: Key is the key in the key-value + pair. + type: string + name: + description: |- + Name is the name of the key-value pair. + This is just for identifying the pair and will not be sent to the OpenStack API. + type: string + value: + description: Value is the value in the key-value + pair. + type: string + required: + - key + - name + - value + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + vnicType: + description: |- + VNICType specifies the type of vNIC which this port should be + attached to. This is used to determine which mechanism driver(s) to + be used to bind the port. The valid values are normal, macvtap, + direct, baremetal, direct-physical, virtio-forwarder, smart-nic and + remote-managed, although these values will not be validated in this + API to ensure compatibility with future neutron changes or custom + implementations. What type of vNIC is actually available depends on + deployments. If not specified, the Neutron default value is used. + type: string + required: + - segmentationID + - segmentationType + type: object + type: array + x-kubernetes-list-type: atomic tags: description: |- Tags applied to the port (and corresponding trunk, if a trunk is configured.) @@ -3250,8 +3679,6 @@ spec: description: Ports is the fully resolved list of ports to create for the machine. items: - description: ResolvedPortSpec is a PortOpts with all contained - references fully resolved. properties: adminStateUp: description: AdminStateUp specifies whether the port @@ -3361,6 +3788,190 @@ spec: type: string type: array x-kubernetes-list-type: atomic + subports: + description: |- + Subports is a list of resolved port specifications that will be created as + subports of the trunk. + items: + description: ResolvedSubportSpec is a SubportOpts + with all contained references fully resolved. + properties: + adminStateUp: + description: AdminStateUp specifies whether the + port should be created in the up (true) or down + (false) state. The default is up. + type: boolean + allowedAddressPairs: + description: |- + AllowedAddressPairs is a list of address pairs which Neutron will + allow the port to send traffic from in addition to the port's + addresses. If not specified, the MAC Address will be the MAC Address + of the port. Depending on the configuration of Neutron, it may be + supported to specify a CIDR instead of a specific IP address. + items: + properties: + ipAddress: + description: |- + IPAddress is the IP address of the allowed address pair. Depending on + the configuration of Neutron, it may be supported to specify a CIDR + instead of a specific IP address. + type: string + macAddress: + description: |- + MACAddress is the MAC address of the allowed address pair. If not + specified, the MAC address will be the MAC address of the port. + type: string + required: + - ipAddress + type: object + type: array + description: + description: Description is a human-readable description + for the port. + type: string + disablePortSecurity: + description: |- + DisablePortSecurity enables or disables the port security when set. + When not set, it takes the value of the corresponding field at the network level. + type: boolean + fixedIPs: + description: FixedIPs is a list of pairs of subnet + and/or IP address to assign to the port. If + specified, these must be subnets of the port's + network. + items: + description: ResolvedFixedIP is a FixedIP with + the Subnet resolved to an ID. + properties: + ipAddress: + description: |- + IPAddress is a specific IP address to assign to the port. If SubnetID + is also specified, IPAddress must be a valid IP address in the + subnet. If Subnet is not specified, IPAddress must be a valid IP + address in any subnet of the port's network. + type: string + subnet: + description: SubnetID is the id of a subnet + to create the fixed IP of a port in. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + hostID: + description: HostID specifies the ID of the host + where the port resides. + type: string + macAddress: + description: MACAddress specifies the MAC address + of the port. If not specified, the MAC address + will be generated. + type: string + name: + description: Name is the name of the port. + type: string + networkID: + description: NetworkID is the ID of the network + the port will be created in. + type: string + profile: + description: |- + Profile is a set of key-value pairs that are used for binding + details. We intentionally don't expose this as a map[string]string + because we only want to enable the users to set the values of the + keys that are known to work in OpenStack Networking API. See + https://docs.openstack.org/api-ref/network/v2/index.html?expanded=create-port-detail#create-port + To set profiles, your tenant needs permissions rule:create_port, and + rule:create_port:binding:profile + properties: + ovsHWOffload: + description: |- + OVSHWOffload enables or disables the OVS hardware offload feature. + This flag is not required on OpenStack clouds since Yoga as Nova will set it automatically when the port is attached. + See: https://bugs.launchpad.net/nova/+bug/2020813 + type: boolean + trustedVF: + description: TrustedVF enables or disables + the “trusted mode” for the VF. + type: boolean + type: object + propagateUplinkStatus: + description: PropageteUplinkStatus enables or + disables the propagate uplink status on the + port. + type: boolean + securityGroups: + description: SecurityGroups is a list of security + group IDs to assign to the port. + items: + type: string + type: array + x-kubernetes-list-type: atomic + segmentationID: + description: SegmentationID is the segmentation + ID of the subport. E.g. VLAN ID. + type: integer + segmentationType: + description: SegmentationType is the segmentation + type of the subport. E.g. "vlan". + type: string + tags: + description: Tags applied to the port (and corresponding + trunk, if a trunk is configured.) + items: + type: string + type: array + x-kubernetes-list-type: set + valueSpecs: + description: |- + Value specs are extra parameters to include in the API request with OpenStack. + This is an extension point for the API, so what they do and if they are supported, + depends on the specific OpenStack implementation. + items: + description: ValueSpec represents a single value_spec + key-value pair. + properties: + key: + description: Key is the key in the key-value + pair. + type: string + name: + description: |- + Name is the name of the key-value pair. + This is just for identifying the pair and will not be sent to the OpenStack API. + type: string + value: + description: Value is the value in the key-value + pair. + type: string + required: + - key + - name + - value + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + vnicType: + description: |- + VNICType specifies the type of vNIC which this port should be + attached to. This is used to determine which mechanism driver(s) to + be used to bind the port. The valid values are normal, macvtap, + direct, baremetal, direct-physical, virtio-forwarder, smart-nic and + remote-managed, although these values will not be validated in this + API to ensure compatibility with future neutron changes or custom + implementations. What type of vNIC is actually available depends on + deployments. If not specified, the Neutron default value is used. + type: string + required: + - description + - name + - networkID + - segmentationID + - segmentationType + type: object + type: array + x-kubernetes-list-type: atomic tags: description: Tags applied to the port (and corresponding trunk, if a trunk is configured.) @@ -3436,6 +4047,19 @@ spec: id: description: ID is the unique identifier of the port. type: string + subports: + description: Subports is the list of port IDs which + intended to be trunk sub-ports + items: + properties: + id: + description: ID is the unique identifier of the + trunk sub-port. + type: string + required: + - id + type: object + type: array required: - id type: object diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml index 723019788..1d7e0a75a 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml @@ -1271,6 +1271,7 @@ spec: Ports to be attached to the server instance. They are created if a port with the given name does not already exist. If not specified a default port will be added for the default cluster network. items: + description: PortOpts defines port parameters. properties: adminStateUp: description: AdminStateUp specifies whether @@ -1624,6 +1625,442 @@ spec: type: object type: array x-kubernetes-list-type: atomic + subports: + description: |- + Subports is a list of port specifications that will be created as + subports of the trunk. + items: + description: SubportOpts defines a trunk subport. + properties: + adminStateUp: + description: AdminStateUp specifies whether + the port should be created in the up + (true) or down (false) state. The default + is up. + type: boolean + allowedAddressPairs: + description: |- + AllowedAddressPairs is a list of address pairs which Neutron will + allow the port to send traffic from in addition to the port's + addresses. If not specified, the MAC Address will be the MAC Address + of the port. Depending on the configuration of Neutron, it may be + supported to specify a CIDR instead of a specific IP address. + items: + properties: + ipAddress: + description: |- + IPAddress is the IP address of the allowed address pair. Depending on + the configuration of Neutron, it may be supported to specify a CIDR + instead of a specific IP address. + type: string + macAddress: + description: |- + MACAddress is the MAC address of the allowed address pair. If not + specified, the MAC address will be the MAC address of the port. + type: string + required: + - ipAddress + type: object + type: array + description: + description: Description is a human-readable + description for the port. + type: string + disablePortSecurity: + description: |- + DisablePortSecurity enables or disables the port security when set. + When not set, it takes the value of the corresponding field at the network level. + type: boolean + fixedIPs: + description: FixedIPs is a list of pairs + of subnet and/or IP address to assign + to the port. If specified, these must + be subnets of the port's network. + items: + properties: + ipAddress: + description: |- + IPAddress is a specific IP address to assign to the port. If Subnet + is also specified, IPAddress must be a valid IP address in the + subnet. If Subnet is not specified, IPAddress must be a valid IP + address in any subnet of the port's network. + type: string + subnet: + description: |- + Subnet is an openstack subnet query that will return the id of a subnet to create + the fixed IP of a port in. This query must not return more than one subnet. + maxProperties: 1 + minProperties: 1 + properties: + filter: + description: Filter specifies + a filter to select the subnet. + It must match exactly one + subnet. + minProperties: 1 + properties: + cidr: + type: string + description: + type: string + gatewayIP: + type: string + ipVersion: + type: integer + ipv6AddressMode: + type: string + ipv6RAMode: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object + id: + description: ID is the uuid + of the subnet. It will not + be validated. + format: uuid + type: string + type: object + type: object + type: array + x-kubernetes-list-type: atomic + hostID: + description: HostID specifies the ID of + the host where the port resides. + type: string + macAddress: + description: MACAddress specifies the + MAC address of the port. If not specified, + the MAC address will be generated. + type: string + nameSuffix: + description: NameSuffix will be appended + to the name of the port if specified. + If unspecified, instead the 0-based + index of the port in the list is used. + type: string + network: + description: |- + Network is a query for an openstack network that the port will be created or discovered on. + This will fail if the query returns more than one network. + maxProperties: 1 + minProperties: 1 + properties: + filter: + description: Filter specifies a filter + to select an OpenStack network. + If provided, cannot be empty. + minProperties: 1 + properties: + description: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object + id: + description: ID is the ID of the network + to use. If ID is provided, the other + filters cannot be provided. Must + be in UUID format. + format: uuid + type: string + type: object + profile: + description: |- + Profile is a set of key-value pairs that are used for binding + details. We intentionally don't expose this as a map[string]string + because we only want to enable the users to set the values of the + keys that are known to work in OpenStack Networking API. See + https://docs.openstack.org/api-ref/network/v2/index.html?expanded=create-port-detail#create-port + To set profiles, your tenant needs permissions rule:create_port, and + rule:create_port:binding:profile + properties: + ovsHWOffload: + description: |- + OVSHWOffload enables or disables the OVS hardware offload feature. + This flag is not required on OpenStack clouds since Yoga as Nova will set it automatically when the port is attached. + See: https://bugs.launchpad.net/nova/+bug/2020813 + type: boolean + trustedVF: + description: TrustedVF enables or + disables the “trusted mode” for + the VF. + type: boolean + type: object + propagateUplinkStatus: + description: PropageteUplinkStatus enables + or disables the propagate uplink status + on the port. + type: boolean + securityGroups: + description: SecurityGroups is a list + of the names, uuids, filters or any + combination these of the security groups + to assign to the instance. + items: + description: SecurityGroupParam specifies + an OpenStack security group. It may + be specified by ID or filter, but + not both. + maxProperties: 1 + minProperties: 1 + properties: + filter: + description: Filter specifies a + query to select an OpenStack security + group. If provided, cannot be + empty. + minProperties: 1 + properties: + description: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object + id: + description: ID is the ID of the + security group to use. If ID is + provided, the other filters cannot + be provided. Must be in UUID format. + format: uuid + type: string + type: object + type: array + x-kubernetes-list-type: atomic + segmentationID: + description: SegmentationID is the segmentation + ID of the subport. E.g. VLAN ID. + maximum: 4094 + minimum: 1 + type: integer + segmentationType: + description: SegmentationType is the segmentation + type of the subport. E.g. "vlan". + enum: + - vlan + - flat + type: string + tags: + description: |- + Tags applied to the port (and corresponding trunk, if a trunk is configured.) + These tags are applied in addition to the instance's tags, which will also be applied to the port. + items: + type: string + type: array + x-kubernetes-list-type: set + valueSpecs: + description: |- + Value specs are extra parameters to include in the API request with OpenStack. + This is an extension point for the API, so what they do and if they are supported, + depends on the specific OpenStack implementation. + items: + description: ValueSpec represents a + single value_spec key-value pair. + properties: + key: + description: Key is the key in the + key-value pair. + type: string + name: + description: |- + Name is the name of the key-value pair. + This is just for identifying the pair and will not be sent to the OpenStack API. + type: string + value: + description: Value is the value + in the key-value pair. + type: string + required: + - key + - name + - value + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + vnicType: + description: |- + VNICType specifies the type of vNIC which this port should be + attached to. This is used to determine which mechanism driver(s) to + be used to bind the port. The valid values are normal, macvtap, + direct, baremetal, direct-physical, virtio-forwarder, smart-nic and + remote-managed, although these values will not be validated in this + API to ensure compatibility with future neutron changes or custom + implementations. What type of vNIC is actually available depends on + deployments. If not specified, the Neutron default value is used. + type: string + required: + - segmentationID + - segmentationType + type: object + type: array + x-kubernetes-list-type: atomic tags: description: |- Tags applied to the port (and corresponding trunk, if a trunk is configured.) diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml index 96975431f..d618e9079 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml @@ -790,6 +790,7 @@ spec: Ports to be attached to the server instance. They are created if a port with the given name does not already exist. If not specified a default port will be added for the default cluster network. items: + description: PortOpts defines port parameters. properties: adminStateUp: description: AdminStateUp specifies whether the port should @@ -1134,6 +1135,426 @@ spec: type: object type: array x-kubernetes-list-type: atomic + subports: + description: |- + Subports is a list of port specifications that will be created as + subports of the trunk. + items: + description: SubportOpts defines a trunk subport. + properties: + adminStateUp: + description: AdminStateUp specifies whether the port should + be created in the up (true) or down (false) state. The + default is up. + type: boolean + allowedAddressPairs: + description: |- + AllowedAddressPairs is a list of address pairs which Neutron will + allow the port to send traffic from in addition to the port's + addresses. If not specified, the MAC Address will be the MAC Address + of the port. Depending on the configuration of Neutron, it may be + supported to specify a CIDR instead of a specific IP address. + items: + properties: + ipAddress: + description: |- + IPAddress is the IP address of the allowed address pair. Depending on + the configuration of Neutron, it may be supported to specify a CIDR + instead of a specific IP address. + type: string + macAddress: + description: |- + MACAddress is the MAC address of the allowed address pair. If not + specified, the MAC address will be the MAC address of the port. + type: string + required: + - ipAddress + type: object + type: array + description: + description: Description is a human-readable description + for the port. + type: string + disablePortSecurity: + description: |- + DisablePortSecurity enables or disables the port security when set. + When not set, it takes the value of the corresponding field at the network level. + type: boolean + fixedIPs: + description: FixedIPs is a list of pairs of subnet and/or + IP address to assign to the port. If specified, these + must be subnets of the port's network. + items: + properties: + ipAddress: + description: |- + IPAddress is a specific IP address to assign to the port. If Subnet + is also specified, IPAddress must be a valid IP address in the + subnet. If Subnet is not specified, IPAddress must be a valid IP + address in any subnet of the port's network. + type: string + subnet: + description: |- + Subnet is an openstack subnet query that will return the id of a subnet to create + the fixed IP of a port in. This query must not return more than one subnet. + maxProperties: 1 + minProperties: 1 + properties: + filter: + description: Filter specifies a filter to select + the subnet. It must match exactly one subnet. + minProperties: 1 + properties: + cidr: + type: string + description: + type: string + gatewayIP: + type: string + ipVersion: + type: integer + ipv6AddressMode: + type: string + ipv6RAMode: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object + id: + description: ID is the uuid of the subnet. It + will not be validated. + format: uuid + type: string + type: object + type: object + type: array + x-kubernetes-list-type: atomic + hostID: + description: HostID specifies the ID of the host where + the port resides. + type: string + macAddress: + description: MACAddress specifies the MAC address of the + port. If not specified, the MAC address will be generated. + type: string + nameSuffix: + description: NameSuffix will be appended to the name of + the port if specified. If unspecified, instead the 0-based + index of the port in the list is used. + type: string + network: + description: |- + Network is a query for an openstack network that the port will be created or discovered on. + This will fail if the query returns more than one network. + maxProperties: 1 + minProperties: 1 + properties: + filter: + description: Filter specifies a filter to select an + OpenStack network. If provided, cannot be empty. + minProperties: 1 + properties: + description: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object + id: + description: ID is the ID of the network to use. If + ID is provided, the other filters cannot be provided. + Must be in UUID format. + format: uuid + type: string + type: object + profile: + description: |- + Profile is a set of key-value pairs that are used for binding + details. We intentionally don't expose this as a map[string]string + because we only want to enable the users to set the values of the + keys that are known to work in OpenStack Networking API. See + https://docs.openstack.org/api-ref/network/v2/index.html?expanded=create-port-detail#create-port + To set profiles, your tenant needs permissions rule:create_port, and + rule:create_port:binding:profile + properties: + ovsHWOffload: + description: |- + OVSHWOffload enables or disables the OVS hardware offload feature. + This flag is not required on OpenStack clouds since Yoga as Nova will set it automatically when the port is attached. + See: https://bugs.launchpad.net/nova/+bug/2020813 + type: boolean + trustedVF: + description: TrustedVF enables or disables the “trusted + mode” for the VF. + type: boolean + type: object + propagateUplinkStatus: + description: PropageteUplinkStatus enables or disables + the propagate uplink status on the port. + type: boolean + securityGroups: + description: SecurityGroups is a list of the names, uuids, + filters or any combination these of the security groups + to assign to the instance. + items: + description: SecurityGroupParam specifies an OpenStack + security group. It may be specified by ID or filter, + but not both. + maxProperties: 1 + minProperties: 1 + properties: + filter: + description: Filter specifies a query to select + an OpenStack security group. If provided, cannot + be empty. + minProperties: 1 + properties: + description: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object + id: + description: ID is the ID of the security group + to use. If ID is provided, the other filters cannot + be provided. Must be in UUID format. + format: uuid + type: string + type: object + type: array + x-kubernetes-list-type: atomic + segmentationID: + description: SegmentationID is the segmentation ID of + the subport. E.g. VLAN ID. + maximum: 4094 + minimum: 1 + type: integer + segmentationType: + description: SegmentationType is the segmentation type + of the subport. E.g. "vlan". + enum: + - vlan + - flat + type: string + tags: + description: |- + Tags applied to the port (and corresponding trunk, if a trunk is configured.) + These tags are applied in addition to the instance's tags, which will also be applied to the port. + items: + type: string + type: array + x-kubernetes-list-type: set + valueSpecs: + description: |- + Value specs are extra parameters to include in the API request with OpenStack. + This is an extension point for the API, so what they do and if they are supported, + depends on the specific OpenStack implementation. + items: + description: ValueSpec represents a single value_spec + key-value pair. + properties: + key: + description: Key is the key in the key-value pair. + type: string + name: + description: |- + Name is the name of the key-value pair. + This is just for identifying the pair and will not be sent to the OpenStack API. + type: string + value: + description: Value is the value in the key-value + pair. + type: string + required: + - key + - name + - value + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + vnicType: + description: |- + VNICType specifies the type of vNIC which this port should be + attached to. This is used to determine which mechanism driver(s) to + be used to bind the port. The valid values are normal, macvtap, + direct, baremetal, direct-physical, virtio-forwarder, smart-nic and + remote-managed, although these values will not be validated in this + API to ensure compatibility with future neutron changes or custom + implementations. What type of vNIC is actually available depends on + deployments. If not specified, the Neutron default value is used. + type: string + required: + - segmentationID + - segmentationType + type: object + type: array + x-kubernetes-list-type: atomic tags: description: |- Tags applied to the port (and corresponding trunk, if a trunk is configured.) @@ -1575,8 +1996,6 @@ spec: description: Ports is the fully resolved list of ports to create for the machine. items: - description: ResolvedPortSpec is a PortOpts with all contained - references fully resolved. properties: adminStateUp: description: AdminStateUp specifies whether the port should @@ -1685,6 +2104,188 @@ spec: type: string type: array x-kubernetes-list-type: atomic + subports: + description: |- + Subports is a list of resolved port specifications that will be created as + subports of the trunk. + items: + description: ResolvedSubportSpec is a SubportOpts with + all contained references fully resolved. + properties: + adminStateUp: + description: AdminStateUp specifies whether the port + should be created in the up (true) or down (false) + state. The default is up. + type: boolean + allowedAddressPairs: + description: |- + AllowedAddressPairs is a list of address pairs which Neutron will + allow the port to send traffic from in addition to the port's + addresses. If not specified, the MAC Address will be the MAC Address + of the port. Depending on the configuration of Neutron, it may be + supported to specify a CIDR instead of a specific IP address. + items: + properties: + ipAddress: + description: |- + IPAddress is the IP address of the allowed address pair. Depending on + the configuration of Neutron, it may be supported to specify a CIDR + instead of a specific IP address. + type: string + macAddress: + description: |- + MACAddress is the MAC address of the allowed address pair. If not + specified, the MAC address will be the MAC address of the port. + type: string + required: + - ipAddress + type: object + type: array + description: + description: Description is a human-readable description + for the port. + type: string + disablePortSecurity: + description: |- + DisablePortSecurity enables or disables the port security when set. + When not set, it takes the value of the corresponding field at the network level. + type: boolean + fixedIPs: + description: FixedIPs is a list of pairs of subnet + and/or IP address to assign to the port. If specified, + these must be subnets of the port's network. + items: + description: ResolvedFixedIP is a FixedIP with the + Subnet resolved to an ID. + properties: + ipAddress: + description: |- + IPAddress is a specific IP address to assign to the port. If SubnetID + is also specified, IPAddress must be a valid IP address in the + subnet. If Subnet is not specified, IPAddress must be a valid IP + address in any subnet of the port's network. + type: string + subnet: + description: SubnetID is the id of a subnet + to create the fixed IP of a port in. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + hostID: + description: HostID specifies the ID of the host where + the port resides. + type: string + macAddress: + description: MACAddress specifies the MAC address + of the port. If not specified, the MAC address will + be generated. + type: string + name: + description: Name is the name of the port. + type: string + networkID: + description: NetworkID is the ID of the network the + port will be created in. + type: string + profile: + description: |- + Profile is a set of key-value pairs that are used for binding + details. We intentionally don't expose this as a map[string]string + because we only want to enable the users to set the values of the + keys that are known to work in OpenStack Networking API. See + https://docs.openstack.org/api-ref/network/v2/index.html?expanded=create-port-detail#create-port + To set profiles, your tenant needs permissions rule:create_port, and + rule:create_port:binding:profile + properties: + ovsHWOffload: + description: |- + OVSHWOffload enables or disables the OVS hardware offload feature. + This flag is not required on OpenStack clouds since Yoga as Nova will set it automatically when the port is attached. + See: https://bugs.launchpad.net/nova/+bug/2020813 + type: boolean + trustedVF: + description: TrustedVF enables or disables the + “trusted mode” for the VF. + type: boolean + type: object + propagateUplinkStatus: + description: PropageteUplinkStatus enables or disables + the propagate uplink status on the port. + type: boolean + securityGroups: + description: SecurityGroups is a list of security + group IDs to assign to the port. + items: + type: string + type: array + x-kubernetes-list-type: atomic + segmentationID: + description: SegmentationID is the segmentation ID + of the subport. E.g. VLAN ID. + type: integer + segmentationType: + description: SegmentationType is the segmentation + type of the subport. E.g. "vlan". + type: string + tags: + description: Tags applied to the port (and corresponding + trunk, if a trunk is configured.) + items: + type: string + type: array + x-kubernetes-list-type: set + valueSpecs: + description: |- + Value specs are extra parameters to include in the API request with OpenStack. + This is an extension point for the API, so what they do and if they are supported, + depends on the specific OpenStack implementation. + items: + description: ValueSpec represents a single value_spec + key-value pair. + properties: + key: + description: Key is the key in the key-value + pair. + type: string + name: + description: |- + Name is the name of the key-value pair. + This is just for identifying the pair and will not be sent to the OpenStack API. + type: string + value: + description: Value is the value in the key-value + pair. + type: string + required: + - key + - name + - value + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + vnicType: + description: |- + VNICType specifies the type of vNIC which this port should be + attached to. This is used to determine which mechanism driver(s) to + be used to bind the port. The valid values are normal, macvtap, + direct, baremetal, direct-physical, virtio-forwarder, smart-nic and + remote-managed, although these values will not be validated in this + API to ensure compatibility with future neutron changes or custom + implementations. What type of vNIC is actually available depends on + deployments. If not specified, the Neutron default value is used. + type: string + required: + - description + - name + - networkID + - segmentationID + - segmentationType + type: object + type: array + x-kubernetes-list-type: atomic tags: description: Tags applied to the port (and corresponding trunk, if a trunk is configured.) @@ -1759,6 +2360,19 @@ spec: id: description: ID is the unique identifier of the port. type: string + subports: + description: Subports is the list of port IDs which intended + to be trunk sub-ports + items: + properties: + id: + description: ID is the unique identifier of the trunk + sub-port. + type: string + required: + - id + type: object + type: array required: - id type: object diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml index df258200d..5e76ffca2 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml @@ -679,6 +679,7 @@ spec: Ports to be attached to the server instance. They are created if a port with the given name does not already exist. If not specified a default port will be added for the default cluster network. items: + description: PortOpts defines port parameters. properties: adminStateUp: description: AdminStateUp specifies whether the port @@ -1027,6 +1028,434 @@ spec: type: object type: array x-kubernetes-list-type: atomic + subports: + description: |- + Subports is a list of port specifications that will be created as + subports of the trunk. + items: + description: SubportOpts defines a trunk subport. + properties: + adminStateUp: + description: AdminStateUp specifies whether the + port should be created in the up (true) or down + (false) state. The default is up. + type: boolean + allowedAddressPairs: + description: |- + AllowedAddressPairs is a list of address pairs which Neutron will + allow the port to send traffic from in addition to the port's + addresses. If not specified, the MAC Address will be the MAC Address + of the port. Depending on the configuration of Neutron, it may be + supported to specify a CIDR instead of a specific IP address. + items: + properties: + ipAddress: + description: |- + IPAddress is the IP address of the allowed address pair. Depending on + the configuration of Neutron, it may be supported to specify a CIDR + instead of a specific IP address. + type: string + macAddress: + description: |- + MACAddress is the MAC address of the allowed address pair. If not + specified, the MAC address will be the MAC address of the port. + type: string + required: + - ipAddress + type: object + type: array + description: + description: Description is a human-readable description + for the port. + type: string + disablePortSecurity: + description: |- + DisablePortSecurity enables or disables the port security when set. + When not set, it takes the value of the corresponding field at the network level. + type: boolean + fixedIPs: + description: FixedIPs is a list of pairs of subnet + and/or IP address to assign to the port. If + specified, these must be subnets of the port's + network. + items: + properties: + ipAddress: + description: |- + IPAddress is a specific IP address to assign to the port. If Subnet + is also specified, IPAddress must be a valid IP address in the + subnet. If Subnet is not specified, IPAddress must be a valid IP + address in any subnet of the port's network. + type: string + subnet: + description: |- + Subnet is an openstack subnet query that will return the id of a subnet to create + the fixed IP of a port in. This query must not return more than one subnet. + maxProperties: 1 + minProperties: 1 + properties: + filter: + description: Filter specifies a filter + to select the subnet. It must match + exactly one subnet. + minProperties: 1 + properties: + cidr: + type: string + description: + type: string + gatewayIP: + type: string + ipVersion: + type: integer + ipv6AddressMode: + type: string + ipv6RAMode: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object + id: + description: ID is the uuid of the subnet. + It will not be validated. + format: uuid + type: string + type: object + type: object + type: array + x-kubernetes-list-type: atomic + hostID: + description: HostID specifies the ID of the host + where the port resides. + type: string + macAddress: + description: MACAddress specifies the MAC address + of the port. If not specified, the MAC address + will be generated. + type: string + nameSuffix: + description: NameSuffix will be appended to the + name of the port if specified. If unspecified, + instead the 0-based index of the port in the + list is used. + type: string + network: + description: |- + Network is a query for an openstack network that the port will be created or discovered on. + This will fail if the query returns more than one network. + maxProperties: 1 + minProperties: 1 + properties: + filter: + description: Filter specifies a filter to + select an OpenStack network. If provided, + cannot be empty. + minProperties: 1 + properties: + description: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object + id: + description: ID is the ID of the network to + use. If ID is provided, the other filters + cannot be provided. Must be in UUID format. + format: uuid + type: string + type: object + profile: + description: |- + Profile is a set of key-value pairs that are used for binding + details. We intentionally don't expose this as a map[string]string + because we only want to enable the users to set the values of the + keys that are known to work in OpenStack Networking API. See + https://docs.openstack.org/api-ref/network/v2/index.html?expanded=create-port-detail#create-port + To set profiles, your tenant needs permissions rule:create_port, and + rule:create_port:binding:profile + properties: + ovsHWOffload: + description: |- + OVSHWOffload enables or disables the OVS hardware offload feature. + This flag is not required on OpenStack clouds since Yoga as Nova will set it automatically when the port is attached. + See: https://bugs.launchpad.net/nova/+bug/2020813 + type: boolean + trustedVF: + description: TrustedVF enables or disables + the “trusted mode” for the VF. + type: boolean + type: object + propagateUplinkStatus: + description: PropageteUplinkStatus enables or + disables the propagate uplink status on the + port. + type: boolean + securityGroups: + description: SecurityGroups is a list of the names, + uuids, filters or any combination these of the + security groups to assign to the instance. + items: + description: SecurityGroupParam specifies an + OpenStack security group. It may be specified + by ID or filter, but not both. + maxProperties: 1 + minProperties: 1 + properties: + filter: + description: Filter specifies a query to + select an OpenStack security group. If + provided, cannot be empty. + minProperties: 1 + properties: + description: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object + id: + description: ID is the ID of the security + group to use. If ID is provided, the other + filters cannot be provided. Must be in + UUID format. + format: uuid + type: string + type: object + type: array + x-kubernetes-list-type: atomic + segmentationID: + description: SegmentationID is the segmentation + ID of the subport. E.g. VLAN ID. + maximum: 4094 + minimum: 1 + type: integer + segmentationType: + description: SegmentationType is the segmentation + type of the subport. E.g. "vlan". + enum: + - vlan + - flat + type: string + tags: + description: |- + Tags applied to the port (and corresponding trunk, if a trunk is configured.) + These tags are applied in addition to the instance's tags, which will also be applied to the port. + items: + type: string + type: array + x-kubernetes-list-type: set + valueSpecs: + description: |- + Value specs are extra parameters to include in the API request with OpenStack. + This is an extension point for the API, so what they do and if they are supported, + depends on the specific OpenStack implementation. + items: + description: ValueSpec represents a single value_spec + key-value pair. + properties: + key: + description: Key is the key in the key-value + pair. + type: string + name: + description: |- + Name is the name of the key-value pair. + This is just for identifying the pair and will not be sent to the OpenStack API. + type: string + value: + description: Value is the value in the key-value + pair. + type: string + required: + - key + - name + - value + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + vnicType: + description: |- + VNICType specifies the type of vNIC which this port should be + attached to. This is used to determine which mechanism driver(s) to + be used to bind the port. The valid values are normal, macvtap, + direct, baremetal, direct-physical, virtio-forwarder, smart-nic and + remote-managed, although these values will not be validated in this + API to ensure compatibility with future neutron changes or custom + implementations. What type of vNIC is actually available depends on + deployments. If not specified, the Neutron default value is used. + type: string + required: + - segmentationID + - segmentationType + type: object + type: array + x-kubernetes-list-type: atomic tags: description: |- Tags applied to the port (and corresponding trunk, if a trunk is configured.) diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackservers.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackservers.yaml index 84ce35b93..9602e70c8 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackservers.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackservers.yaml @@ -259,6 +259,7 @@ spec: ports: description: Ports to be attached to the server instance. items: + description: PortOpts defines port parameters. properties: adminStateUp: description: AdminStateUp specifies whether the port should @@ -603,6 +604,426 @@ spec: type: object type: array x-kubernetes-list-type: atomic + subports: + description: |- + Subports is a list of port specifications that will be created as + subports of the trunk. + items: + description: SubportOpts defines a trunk subport. + properties: + adminStateUp: + description: AdminStateUp specifies whether the port should + be created in the up (true) or down (false) state. The + default is up. + type: boolean + allowedAddressPairs: + description: |- + AllowedAddressPairs is a list of address pairs which Neutron will + allow the port to send traffic from in addition to the port's + addresses. If not specified, the MAC Address will be the MAC Address + of the port. Depending on the configuration of Neutron, it may be + supported to specify a CIDR instead of a specific IP address. + items: + properties: + ipAddress: + description: |- + IPAddress is the IP address of the allowed address pair. Depending on + the configuration of Neutron, it may be supported to specify a CIDR + instead of a specific IP address. + type: string + macAddress: + description: |- + MACAddress is the MAC address of the allowed address pair. If not + specified, the MAC address will be the MAC address of the port. + type: string + required: + - ipAddress + type: object + type: array + description: + description: Description is a human-readable description + for the port. + type: string + disablePortSecurity: + description: |- + DisablePortSecurity enables or disables the port security when set. + When not set, it takes the value of the corresponding field at the network level. + type: boolean + fixedIPs: + description: FixedIPs is a list of pairs of subnet and/or + IP address to assign to the port. If specified, these + must be subnets of the port's network. + items: + properties: + ipAddress: + description: |- + IPAddress is a specific IP address to assign to the port. If Subnet + is also specified, IPAddress must be a valid IP address in the + subnet. If Subnet is not specified, IPAddress must be a valid IP + address in any subnet of the port's network. + type: string + subnet: + description: |- + Subnet is an openstack subnet query that will return the id of a subnet to create + the fixed IP of a port in. This query must not return more than one subnet. + maxProperties: 1 + minProperties: 1 + properties: + filter: + description: Filter specifies a filter to select + the subnet. It must match exactly one subnet. + minProperties: 1 + properties: + cidr: + type: string + description: + type: string + gatewayIP: + type: string + ipVersion: + type: integer + ipv6AddressMode: + type: string + ipv6RAMode: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object + id: + description: ID is the uuid of the subnet. It + will not be validated. + format: uuid + type: string + type: object + type: object + type: array + x-kubernetes-list-type: atomic + hostID: + description: HostID specifies the ID of the host where + the port resides. + type: string + macAddress: + description: MACAddress specifies the MAC address of the + port. If not specified, the MAC address will be generated. + type: string + nameSuffix: + description: NameSuffix will be appended to the name of + the port if specified. If unspecified, instead the 0-based + index of the port in the list is used. + type: string + network: + description: |- + Network is a query for an openstack network that the port will be created or discovered on. + This will fail if the query returns more than one network. + maxProperties: 1 + minProperties: 1 + properties: + filter: + description: Filter specifies a filter to select an + OpenStack network. If provided, cannot be empty. + minProperties: 1 + properties: + description: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object + id: + description: ID is the ID of the network to use. If + ID is provided, the other filters cannot be provided. + Must be in UUID format. + format: uuid + type: string + type: object + profile: + description: |- + Profile is a set of key-value pairs that are used for binding + details. We intentionally don't expose this as a map[string]string + because we only want to enable the users to set the values of the + keys that are known to work in OpenStack Networking API. See + https://docs.openstack.org/api-ref/network/v2/index.html?expanded=create-port-detail#create-port + To set profiles, your tenant needs permissions rule:create_port, and + rule:create_port:binding:profile + properties: + ovsHWOffload: + description: |- + OVSHWOffload enables or disables the OVS hardware offload feature. + This flag is not required on OpenStack clouds since Yoga as Nova will set it automatically when the port is attached. + See: https://bugs.launchpad.net/nova/+bug/2020813 + type: boolean + trustedVF: + description: TrustedVF enables or disables the “trusted + mode” for the VF. + type: boolean + type: object + propagateUplinkStatus: + description: PropageteUplinkStatus enables or disables + the propagate uplink status on the port. + type: boolean + securityGroups: + description: SecurityGroups is a list of the names, uuids, + filters or any combination these of the security groups + to assign to the instance. + items: + description: SecurityGroupParam specifies an OpenStack + security group. It may be specified by ID or filter, + but not both. + maxProperties: 1 + minProperties: 1 + properties: + filter: + description: Filter specifies a query to select + an OpenStack security group. If provided, cannot + be empty. + minProperties: 1 + properties: + description: + type: string + name: + type: string + notTags: + description: |- + NotTags is a list of tags to filter by. If specified, resources which + contain all of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + notTagsAny: + description: |- + NotTagsAny is a list of tags to filter by. If specified, resources + which contain any of the given tags will be excluded from the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + projectID: + type: string + tags: + description: |- + Tags is a list of tags to filter by. If specified, the resource must + have all of the tags specified to be included in the result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + tagsAny: + description: |- + TagsAny is a list of tags to filter by. If specified, the resource + must have at least one of the tags specified to be included in the + result. + items: + description: |- + NeutronTag represents a tag on a Neutron resource. + It may not be empty and may not contain commas. + minLength: 1 + pattern: ^[^,]+$ + type: string + type: array + x-kubernetes-list-type: set + type: object + id: + description: ID is the ID of the security group + to use. If ID is provided, the other filters cannot + be provided. Must be in UUID format. + format: uuid + type: string + type: object + type: array + x-kubernetes-list-type: atomic + segmentationID: + description: SegmentationID is the segmentation ID of + the subport. E.g. VLAN ID. + maximum: 4094 + minimum: 1 + type: integer + segmentationType: + description: SegmentationType is the segmentation type + of the subport. E.g. "vlan". + enum: + - vlan + - flat + type: string + tags: + description: |- + Tags applied to the port (and corresponding trunk, if a trunk is configured.) + These tags are applied in addition to the instance's tags, which will also be applied to the port. + items: + type: string + type: array + x-kubernetes-list-type: set + valueSpecs: + description: |- + Value specs are extra parameters to include in the API request with OpenStack. + This is an extension point for the API, so what they do and if they are supported, + depends on the specific OpenStack implementation. + items: + description: ValueSpec represents a single value_spec + key-value pair. + properties: + key: + description: Key is the key in the key-value pair. + type: string + name: + description: |- + Name is the name of the key-value pair. + This is just for identifying the pair and will not be sent to the OpenStack API. + type: string + value: + description: Value is the value in the key-value + pair. + type: string + required: + - key + - name + - value + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + vnicType: + description: |- + VNICType specifies the type of vNIC which this port should be + attached to. This is used to determine which mechanism driver(s) to + be used to bind the port. The valid values are normal, macvtap, + direct, baremetal, direct-physical, virtio-forwarder, smart-nic and + remote-managed, although these values will not be validated in this + API to ensure compatibility with future neutron changes or custom + implementations. What type of vNIC is actually available depends on + deployments. If not specified, the Neutron default value is used. + type: string + required: + - segmentationID + - segmentationType + type: object + type: array + x-kubernetes-list-type: atomic tags: description: |- Tags applied to the port (and corresponding trunk, if a trunk is configured.) @@ -1036,8 +1457,6 @@ spec: description: Ports is the fully resolved list of ports to create for the server. items: - description: ResolvedPortSpec is a PortOpts with all contained - references fully resolved. properties: adminStateUp: description: AdminStateUp specifies whether the port should @@ -1146,6 +1565,188 @@ spec: type: string type: array x-kubernetes-list-type: atomic + subports: + description: |- + Subports is a list of resolved port specifications that will be created as + subports of the trunk. + items: + description: ResolvedSubportSpec is a SubportOpts with + all contained references fully resolved. + properties: + adminStateUp: + description: AdminStateUp specifies whether the port + should be created in the up (true) or down (false) + state. The default is up. + type: boolean + allowedAddressPairs: + description: |- + AllowedAddressPairs is a list of address pairs which Neutron will + allow the port to send traffic from in addition to the port's + addresses. If not specified, the MAC Address will be the MAC Address + of the port. Depending on the configuration of Neutron, it may be + supported to specify a CIDR instead of a specific IP address. + items: + properties: + ipAddress: + description: |- + IPAddress is the IP address of the allowed address pair. Depending on + the configuration of Neutron, it may be supported to specify a CIDR + instead of a specific IP address. + type: string + macAddress: + description: |- + MACAddress is the MAC address of the allowed address pair. If not + specified, the MAC address will be the MAC address of the port. + type: string + required: + - ipAddress + type: object + type: array + description: + description: Description is a human-readable description + for the port. + type: string + disablePortSecurity: + description: |- + DisablePortSecurity enables or disables the port security when set. + When not set, it takes the value of the corresponding field at the network level. + type: boolean + fixedIPs: + description: FixedIPs is a list of pairs of subnet + and/or IP address to assign to the port. If specified, + these must be subnets of the port's network. + items: + description: ResolvedFixedIP is a FixedIP with the + Subnet resolved to an ID. + properties: + ipAddress: + description: |- + IPAddress is a specific IP address to assign to the port. If SubnetID + is also specified, IPAddress must be a valid IP address in the + subnet. If Subnet is not specified, IPAddress must be a valid IP + address in any subnet of the port's network. + type: string + subnet: + description: SubnetID is the id of a subnet + to create the fixed IP of a port in. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + hostID: + description: HostID specifies the ID of the host where + the port resides. + type: string + macAddress: + description: MACAddress specifies the MAC address + of the port. If not specified, the MAC address will + be generated. + type: string + name: + description: Name is the name of the port. + type: string + networkID: + description: NetworkID is the ID of the network the + port will be created in. + type: string + profile: + description: |- + Profile is a set of key-value pairs that are used for binding + details. We intentionally don't expose this as a map[string]string + because we only want to enable the users to set the values of the + keys that are known to work in OpenStack Networking API. See + https://docs.openstack.org/api-ref/network/v2/index.html?expanded=create-port-detail#create-port + To set profiles, your tenant needs permissions rule:create_port, and + rule:create_port:binding:profile + properties: + ovsHWOffload: + description: |- + OVSHWOffload enables or disables the OVS hardware offload feature. + This flag is not required on OpenStack clouds since Yoga as Nova will set it automatically when the port is attached. + See: https://bugs.launchpad.net/nova/+bug/2020813 + type: boolean + trustedVF: + description: TrustedVF enables or disables the + “trusted mode” for the VF. + type: boolean + type: object + propagateUplinkStatus: + description: PropageteUplinkStatus enables or disables + the propagate uplink status on the port. + type: boolean + securityGroups: + description: SecurityGroups is a list of security + group IDs to assign to the port. + items: + type: string + type: array + x-kubernetes-list-type: atomic + segmentationID: + description: SegmentationID is the segmentation ID + of the subport. E.g. VLAN ID. + type: integer + segmentationType: + description: SegmentationType is the segmentation + type of the subport. E.g. "vlan". + type: string + tags: + description: Tags applied to the port (and corresponding + trunk, if a trunk is configured.) + items: + type: string + type: array + x-kubernetes-list-type: set + valueSpecs: + description: |- + Value specs are extra parameters to include in the API request with OpenStack. + This is an extension point for the API, so what they do and if they are supported, + depends on the specific OpenStack implementation. + items: + description: ValueSpec represents a single value_spec + key-value pair. + properties: + key: + description: Key is the key in the key-value + pair. + type: string + name: + description: |- + Name is the name of the key-value pair. + This is just for identifying the pair and will not be sent to the OpenStack API. + type: string + value: + description: Value is the value in the key-value + pair. + type: string + required: + - key + - name + - value + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + vnicType: + description: |- + VNICType specifies the type of vNIC which this port should be + attached to. This is used to determine which mechanism driver(s) to + be used to bind the port. The valid values are normal, macvtap, + direct, baremetal, direct-physical, virtio-forwarder, smart-nic and + remote-managed, although these values will not be validated in this + API to ensure compatibility with future neutron changes or custom + implementations. What type of vNIC is actually available depends on + deployments. If not specified, the Neutron default value is used. + type: string + required: + - description + - name + - networkID + - segmentationID + - segmentationType + type: object + type: array + x-kubernetes-list-type: atomic tags: description: Tags applied to the port (and corresponding trunk, if a trunk is configured.) @@ -1220,6 +1821,19 @@ spec: id: description: ID is the unique identifier of the port. type: string + subports: + description: Subports is the list of port IDs which intended + to be trunk sub-ports + items: + properties: + id: + description: ID is the unique identifier of the trunk + sub-port. + type: string + required: + - id + type: object + type: array required: - id type: object diff --git a/controllers/openstackmachine_controller_test.go b/controllers/openstackmachine_controller_test.go index e66e9dc2b..81c8d0630 100644 --- a/controllers/openstackmachine_controller_test.go +++ b/controllers/openstackmachine_controller_test.go @@ -66,27 +66,31 @@ func TestOpenStackMachineSpecToOpenStackServerSpec(t *testing.T) { } portOpts := []infrav1.PortOpts{ { - Network: &infrav1.NetworkParam{ - ID: ptr.To(openStackCluster.Status.Network.ID), - }, - SecurityGroups: []infrav1.SecurityGroupParam{ - { - ID: ptr.To(openStackCluster.Status.WorkerSecurityGroup.ID), + CommonPortOpts: infrav1.CommonPortOpts{ + Network: &infrav1.NetworkParam{ + ID: ptr.To(openStackCluster.Status.Network.ID), + }, + SecurityGroups: []infrav1.SecurityGroupParam{ + { + ID: ptr.To(openStackCluster.Status.WorkerSecurityGroup.ID), + }, }, }, }, } portOptsWithAdditionalSecurityGroup := []infrav1.PortOpts{ { - Network: &infrav1.NetworkParam{ - ID: ptr.To(openStackCluster.Status.Network.ID), - }, - SecurityGroups: []infrav1.SecurityGroupParam{ - { - ID: ptr.To(openStackCluster.Status.WorkerSecurityGroup.ID), + CommonPortOpts: infrav1.CommonPortOpts{ + Network: &infrav1.NetworkParam{ + ID: ptr.To(openStackCluster.Status.Network.ID), }, - { - ID: ptr.To(extraSecurityGroupUUID), + SecurityGroups: []infrav1.SecurityGroupParam{ + { + ID: ptr.To(openStackCluster.Status.WorkerSecurityGroup.ID), + }, + { + ID: ptr.To(extraSecurityGroupUUID), + }, }, }, }, diff --git a/controllers/openstackserver_controller.go b/controllers/openstackserver_controller.go index fa68ef7a4..348c324c5 100644 --- a/controllers/openstackserver_controller.go +++ b/controllers/openstackserver_controller.go @@ -430,6 +430,10 @@ func getOrCreateServerPorts(openStackServer *infrav1alpha1.OpenStackServer, netw return fmt.Errorf("creating ports: %w", err) } + if err := networkingService.EnsureTrunkSubPorts(openStackServer, desiredPorts, resources); err != nil { + return fmt.Errorf("creating subports: %w", err) + } + return nil } diff --git a/controllers/openstackserver_controller_test.go b/controllers/openstackserver_controller_test.go index 317527461..5936503ee 100644 --- a/controllers/openstackserver_controller_test.go +++ b/controllers/openstackserver_controller_test.go @@ -69,16 +69,20 @@ var defaultImage = infrav1.ImageParam{ var defaultPortOpts = []infrav1.PortOpts{ { - Network: &infrav1.NetworkParam{ - ID: ptr.To(networkUUID), + CommonPortOpts: infrav1.CommonPortOpts{ + Network: &infrav1.NetworkParam{ + ID: ptr.To(networkUUID), + }, }, }, } var defaultResolvedPorts = []infrav1.ResolvedPortSpec{ { - Name: openStackServerName + "-0", - NetworkID: networkUUID, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: openStackServerName + "-0", + NetworkID: networkUUID, + }, }, } diff --git a/docs/book/src/api/v1beta1/api.md b/docs/book/src/api/v1beta1/api.md index 00e763491..457c5fc26 100644 --- a/docs/book/src/api/v1beta1/api.md +++ b/docs/book/src/api/v1beta1/api.md @@ -1486,6 +1486,226 @@ availability zone.
++(Appears on: +PortOpts, +SubportOpts) +
++
+Field | +Description | +
---|---|
+network + + +NetworkParam + + + |
+
+(Optional)
+ Network is a query for an openstack network that the port will be created or discovered on. +This will fail if the query returns more than one network. + |
+
+description + +string + + |
+
+(Optional)
+ Description is a human-readable description for the port. + |
+
+nameSuffix + +string + + |
+
+(Optional)
+ NameSuffix will be appended to the name of the port if specified. If unspecified, instead the 0-based index of the port in the list is used. + |
+
+fixedIPs + + +[]FixedIP + + + |
+
+(Optional)
+ FixedIPs is a list of pairs of subnet and/or IP address to assign to the port. If specified, these must be subnets of the port’s network. + |
+
+securityGroups + + +[]SecurityGroupParam + + + |
+
+(Optional)
+ SecurityGroups is a list of the names, uuids, filters or any combination these of the security groups to assign to the instance. + |
+
+tags + +[]string + + |
+
+(Optional)
+ Tags applied to the port (and corresponding trunk, if a trunk is configured.) +These tags are applied in addition to the instance’s tags, which will also be applied to the port. + |
+
+ResolvedPortSpecFields + + +ResolvedPortSpecFields + + + |
+
+
+(Members of |
+
+(Appears on: +ResolvedPortSpec, +ResolvedSubportSpec) +
++
ResolvedPortSpec is a PortOpts with all contained references fully resolved.
+ +Field | +Description | +
---|---|
+name + +string + + |
+
+ Name is the name of the port. + |
+
+description + +string + + |
+
+ Description is a human-readable description for the port. + |
+
+networkID + +string + + |
+
+ NetworkID is the ID of the network the port will be created in. + |
+
+tags + +[]string + + |
+
+(Optional)
+ Tags applied to the port (and corresponding trunk, if a trunk is configured.) + |
+
+fixedIPs + + +[]ResolvedFixedIP + + + |
+
+(Optional)
+ FixedIPs is a list of pairs of subnet and/or IP address to assign to the port. If specified, these must be subnets of the port’s network. + |
+
+securityGroups + +[]string + + |
+
+(Optional)
+ SecurityGroups is a list of security group IDs to assign to the port. + |
+
+ResolvedPortSpecFields + + +ResolvedPortSpecFields + + + |
+
+
+(Members of |
+
@@ -1614,7 +1834,7 @@ which contain any of the given tags will be excluded from the result.
(Appears on: -PortOpts) +CommonPortOpts)
@@ -2059,8 +2279,8 @@ FilterByNeutronTags
(Appears on: APIServerLoadBalancer, -OpenStackClusterSpec, -PortOpts) +CommonPortOpts, +OpenStackClusterSpec)
NetworkParam specifies an OpenStack network. It may be specified by either ID or Filter, but not both.
@@ -3927,6 +4147,7 @@ OpenStackMachineTemplateResource OpenStackMachineSpec)+
PortOpts defines port parameters.
-network - - -NetworkParam - - - |
-
-(Optional)
- Network is a query for an openstack network that the port will be created or discovered on. -This will fail if the query returns more than one network. - |
-
-description - -string - - |
-
-(Optional)
- Description is a human-readable description for the port. - |
-
-nameSuffix - -string - - |
-
-(Optional)
- NameSuffix will be appended to the name of the port if specified. If unspecified, instead the 0-based index of the port in the list is used. - |
-
-fixedIPs + trunk - -[]FixedIP - +bool |
(Optional)
- FixedIPs is a list of pairs of subnet and/or IP address to assign to the port. If specified, these must be subnets of the port’s network. +Trunk specifies whether trunking is enabled at the port level. If not +provided the value is inherited from the machine, or false for a +bastion host. |
-securityGroups + subports - -[]SecurityGroupParam + +[]SubportOpts |
(Optional)
- SecurityGroups is a list of the names, uuids, filters or any combination these of the security groups to assign to the instance. +Subports is a list of port specifications that will be created as +subports of the trunk. |
-tags + CommonPortOpts -[]string - - |
-
-(Optional)
- Tags applied to the port (and corresponding trunk, if a trunk is configured.) -These tags are applied in addition to the instance’s tags, which will also be applied to the port. - |
-
-trunk - -bool - - |
-
-(Optional)
- Trunk specifies whether trunking is enabled at the port level. If not -provided the value is inherited from the machine, or false for a -bastion host. - |
-
-ResolvedPortSpecFields - - -ResolvedPortSpecFields + +CommonPortOpts |
-(Members of |
+subports + + +[]SubPortStatus + + + |
+
+(Optional)
+ Subports is the list of port IDs which intended to be trunk sub-ports + |
+
(Appears on: -ResolvedPortSpec) +CommonResolvedPortSpec)
ResolvedFixedIP is a FixedIP with the Subnet resolved to an ID.
@@ -4199,7 +4369,6 @@ string ResolvedMachineSpec)-
ResolvedPortSpec is a PortOpts with all contained references fully resolved.
-name - -string - - |
-
- Name is the name of the port. - |
-
-description - -string - - |
-
- Description is a human-readable description for the port. - |
-
-networkID - -string - - |
-
- NetworkID is the ID of the network the port will be created in. - |
-
-tags - -[]string - - |
-
-(Optional)
- Tags applied to the port (and corresponding trunk, if a trunk is configured.) - |
-
trunk bool @@ -4268,42 +4392,31 @@ bool | |
-fixedIPs + subports - -[]ResolvedFixedIP + +[]ResolvedSubportSpec |
(Optional)
- FixedIPs is a list of pairs of subnet and/or IP address to assign to the port. If specified, these must be subnets of the port’s network. +Subports is a list of resolved port specifications that will be created as +subports of the trunk. |
-securityGroups - -[]string - - |
-
-(Optional)
- SecurityGroups is a list of security group IDs to assign to the port. - |
-
-ResolvedPortSpecFields + CommonResolvedPortSpec - -ResolvedPortSpecFields + +CommonResolvedPortSpec |
-(Members of |
+(Appears on: +ResolvedPortSpec) +
++
ResolvedSubportSpec is a SubportOpts with all contained references fully resolved.
+ +Field | +Description | +
---|---|
+segmentationID + +int + + |
+
+ SegmentationID is the segmentation ID of the subport. E.g. VLAN ID. + |
+
+segmentationType + +string + + |
+
+ SegmentationType is the segmentation type of the subport. E.g. “vlan”. + |
+
+CommonResolvedPortSpec + + +CommonResolvedPortSpec + + + |
+
+
+(Members of Port is a PortOpts with all contained references fully resolved. This is +essentially port which is used as subport in trunk + |
+
@@ -4916,8 +5087,8 @@ FilterByNeutronTags
(Appears on: -OpenStackMachineSpec, -PortOpts) +CommonPortOpts, +OpenStackMachineSpec)
SecurityGroupParam specifies an OpenStack security group. It may be specified by ID or filter, but not both.
@@ -5267,6 +5438,35 @@ string ++(Appears on: +PortStatus) +
++
+Field | +Description | +
---|---|
+id + +string + + |
+
+ ID is the unique identifier of the trunk sub-port. + |
+
@@ -5546,6 +5746,63 @@ outside of these ranges manually.
++(Appears on: +PortOpts) +
++
SubportOpts defines a trunk subport.
+ +Field | +Description | +
---|---|
+segmentationID + +int + + |
+
+ SegmentationID is the segmentation ID of the subport. E.g. VLAN ID. + |
+
+segmentationType + +string + + |
+
+ SegmentationType is the segmentation type of the subport. E.g. “vlan”. + |
+
+CommonPortOpts + + +CommonPortOpts + + + |
+
+
+(Members of Port contains parameters of the port associated with this subport + |
+
diff --git a/docs/book/src/clusteropenstack/configuration.md b/docs/book/src/clusteropenstack/configuration.md index 60892c17e..2aab4164c 100644 --- a/docs/book/src/clusteropenstack/configuration.md +++ b/docs/book/src/clusteropenstack/configuration.md @@ -486,6 +486,38 @@ spec: Any such ports are created in addition to ports used for connections to networks or subnets. +### Creating Trunks and Subports +You can create trunk ports and attach subports to them for advanced networking scenarios such as VLAN segmentation. To create a trunk port, set the trunk: true key on a port entry. Subports can be specified using the subports key, which is a list of subport configurations. + +The configuration structure for each subport follows the same format as a regular port configuration, except that the trunk and subports keys are not used within subport definitions. + +**Example: Creating a trunk port with subports** +```yaml +ports: + - network: + filter: + name: trunk-parent-net + trunk: true + subports: + - network: + filter: + name: vlan-100 + segmentationID: 100 + segmentationType: vlan + - network: + filter: + name: vlan-200 + segmentationID: 200 + segmentationType: vlan +``` + +* trunk: true marks the port as a trunk port. +* subports is a list of subport definitions to attach to the trunk. +* Each subport can specify its own network, segmentationID, and segmentationType. +* The subport configuration supports the same fields as a regular port, except for trunk and subports. + +Note: Subports are only valid when attached to a trunk port. Attempting to configure subports without trunk: true will result in an error. + ### Port network and IP addresses Together, `network` and `fixedIPs` define the network a port will be created on, and the addresses which will be assigned to the port on that network. diff --git a/pkg/clients/mock/network.go b/pkg/clients/mock/network.go index 1dfafc4d0..cf3238592 100644 --- a/pkg/clients/mock/network.go +++ b/pkg/clients/mock/network.go @@ -79,6 +79,21 @@ func (mr *MockNetworkClientMockRecorder) AddRouterInterface(id, opts any) *gomoc return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRouterInterface", reflect.TypeOf((*MockNetworkClient)(nil).AddRouterInterface), id, opts) } +// AddSubports mocks base method. +func (m *MockNetworkClient) AddSubports(id string, opts trunks.AddSubportsOpts) (*trunks.Trunk, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AddSubports", id, opts) + ret0, _ := ret[0].(*trunks.Trunk) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AddSubports indicates an expected call of AddSubports. +func (mr *MockNetworkClientMockRecorder) AddSubports(id, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSubports", reflect.TypeOf((*MockNetworkClient)(nil).AddSubports), id, opts) +} + // CreateFloatingIP mocks base method. func (m *MockNetworkClient) CreateFloatingIP(opts floatingips.CreateOptsBuilder) (*floatingips.FloatingIP, error) { m.ctrl.T.Helper() diff --git a/pkg/clients/networking.go b/pkg/clients/networking.go index c9e111b3a..7ddd7b0fb 100644 --- a/pkg/clients/networking.go +++ b/pkg/clients/networking.go @@ -55,6 +55,7 @@ type NetworkClient interface { DeleteTrunk(id string) error ListTrunkSubports(trunkID string) ([]trunks.Subport, error) + AddSubports(id string, opts trunks.AddSubportsOpts) (*trunks.Trunk, error) RemoveSubports(id string, opts trunks.RemoveSubportsOpts) error ListRouter(opts routers.ListOpts) ([]routers.Router, error) @@ -251,6 +252,15 @@ func (c networkClient) ListTrunkSubports(trunkID string) ([]trunks.Subport, erro return subports, nil } +func (c networkClient) AddSubports(id string, opts trunks.AddSubportsOpts) (*trunks.Trunk, error) { + mc := metrics.NewMetricPrometheusContext("trunk", "addsubports") + trunk, err := trunks.AddSubports(context.TODO(), c.serviceClient, id, opts).Extract() + if mc.ObserveRequest(err) != nil { + return nil, err + } + return trunk, nil +} + func (c networkClient) RemoveSubports(id string, opts trunks.RemoveSubportsOpts) error { mc := metrics.NewMetricPrometheusContext("trunk", "deletesubports") _, err := trunks.RemoveSubports(context.TODO(), c.serviceClient, id, opts).Extract() diff --git a/pkg/cloud/services/compute/referenced_resources_test.go b/pkg/cloud/services/compute/referenced_resources_test.go index 4f04c217d..4bf7ab886 100644 --- a/pkg/cloud/services/compute/referenced_resources_test.go +++ b/pkg/cloud/services/compute/referenced_resources_test.go @@ -52,16 +52,20 @@ func Test_ResolveServerSpec(t *testing.T) { defaultPortSpec := []infrav1.ResolvedPortSpec{ { - Name: "test-instance-0", - Description: "Created by cluster-api-provider-openstack cluster test-cluster", - NetworkID: networkID1, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "test-instance-0", + Description: "Created by cluster-api-provider-openstack cluster test-cluster", + NetworkID: networkID1, + }, }, } defaultPortOpts := []infrav1.PortOpts{ { - Network: &infrav1.NetworkParam{ - ID: ptr.To(networkID1), + CommonPortOpts: infrav1.CommonPortOpts{ + Network: &infrav1.NetworkParam{ + ID: ptr.To(networkID1), + }, }, }, } diff --git a/pkg/cloud/services/networking/port.go b/pkg/cloud/services/networking/port.go index 59e1e5a8b..ff97cab30 100644 --- a/pkg/cloud/services/networking/port.go +++ b/pkg/cloud/services/networking/port.go @@ -498,6 +498,34 @@ func (s *Service) normalizePorts(ports []infrav1.PortOpts, clusterResourceName, } } } + + // Resolve subports if trunk is enabled + if ptr.Deref(normalizedPort.Trunk, false) && len(port.Subports) > 0 { + normalizedPort.Subports = make([]infrav1.ResolvedSubportSpec, len(port.Subports)) + // Get all ports associated with trunk and normalize them first + trunkSubPorts := make([]infrav1.PortOpts, len(port.Subports)) + for j, p := range port.Subports { + // Trunk port must have network + if p.Network == nil && len(p.FixedIPs) == 0 { + return nil, fmt.Errorf("subport %d of port must specify a network", j) + } + trunkSubPorts[j] = infrav1.PortOpts{CommonPortOpts: p.CommonPortOpts} + } + subportBaseName := fmt.Sprintf("%s-subport", baseName) + normalizedTrunkPorts, err := s.normalizePorts(trunkSubPorts, clusterResourceName, subportBaseName, false, defaultSecurityGroupIDs, nil, baseTags) + if err != nil { + return nil, err + } + + // Normalise tunk port itself + for j, subport := range port.Subports { + normalizedPort.Subports[j] = infrav1.ResolvedSubportSpec{ + SegmentationID: subport.SegmentationID, + SegmentationType: subport.SegmentationType, + CommonResolvedPortSpec: normalizedTrunkPorts[j].CommonResolvedPortSpec, + } + } + } } return normalizedPorts, nil } diff --git a/pkg/cloud/services/networking/port_test.go b/pkg/cloud/services/networking/port_test.go index a215e2efe..8a03dd012 100644 --- a/pkg/cloud/services/networking/port_test.go +++ b/pkg/cloud/services/networking/port_test.go @@ -68,46 +68,48 @@ func Test_EnsurePort(t *testing.T) { { name: "creates port correctly with all options specified except tags, trunk and disablePortSecurity", port: infrav1.ResolvedPortSpec{ - Name: "foo-port-1", - Description: "Created by cluster-api-provider-openstack cluster test-cluster", - NetworkID: netID, - FixedIPs: []infrav1.ResolvedFixedIP{ - { - SubnetID: ptr.To(subnetID1), - IPAddress: ptr.To(ipAddress1), - }, - { - IPAddress: ptr.To(ipAddress2), - }, - { - SubnetID: ptr.To(subnetID2), - }, - }, - SecurityGroups: []string{portSecurityGroupID}, - ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{ - AdminStateUp: ptr.To(true), - MACAddress: ptr.To(macAddress), - AllowedAddressPairs: []infrav1.AddressPair{ + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "foo-port-1", + Description: "Created by cluster-api-provider-openstack cluster test-cluster", + NetworkID: netID, + FixedIPs: []infrav1.ResolvedFixedIP{ { - IPAddress: ipAddress1, - MACAddress: ptr.To(macAddress), + SubnetID: ptr.To(subnetID1), + IPAddress: ptr.To(ipAddress1), }, { - IPAddress: ipAddress2, + IPAddress: ptr.To(ipAddress2), }, - }, - HostID: ptr.To(hostID), - VNICType: ptr.To("normal"), - Profile: &infrav1.BindingProfile{ - OVSHWOffload: ptr.To(true), - TrustedVF: ptr.To(true), - }, - PropagateUplinkStatus: ptr.To(true), - ValueSpecs: []infrav1.ValueSpec{ { - Name: "test-valuespec", - Key: "test-key", - Value: "test-value", + SubnetID: ptr.To(subnetID2), + }, + }, + SecurityGroups: []string{portSecurityGroupID}, + ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{ + AdminStateUp: ptr.To(true), + MACAddress: ptr.To(macAddress), + AllowedAddressPairs: []infrav1.AddressPair{ + { + IPAddress: ipAddress1, + MACAddress: ptr.To(macAddress), + }, + { + IPAddress: ipAddress2, + }, + }, + HostID: ptr.To(hostID), + VNICType: ptr.To("normal"), + Profile: &infrav1.BindingProfile{ + OVSHWOffload: ptr.To(true), + TrustedVF: ptr.To(true), + }, + PropagateUplinkStatus: ptr.To(true), + ValueSpecs: []infrav1.ValueSpec{ + { + Name: "test-valuespec", + Key: "test-key", + Value: "test-value", + }, }, }, }, @@ -176,8 +178,10 @@ func Test_EnsurePort(t *testing.T) { { name: "creates minimum port correctly", port: infrav1.ResolvedPortSpec{ - Name: "test-port", - NetworkID: netID, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "test-port", + NetworkID: netID, + }, }, expect: func(m *mock.MockNetworkClientMockRecorder, g Gomega) { var expectedCreateOpts ports.CreateOptsBuilder @@ -203,12 +207,14 @@ func Test_EnsurePort(t *testing.T) { { name: "disable port security with security groups produces an error", port: infrav1.ResolvedPortSpec{ - Name: "test-port", - NetworkID: netID, - ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{ - DisablePortSecurity: ptr.To(true), + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "test-port", + NetworkID: netID, + ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{ + DisablePortSecurity: ptr.To(true), + }, + SecurityGroups: []string{portSecurityGroupID}, }, - SecurityGroups: []string{portSecurityGroupID}, }, expect: func(m *mock.MockNetworkClientMockRecorder, _ Gomega) { m.ListPort(ports.ListOpts{ @@ -222,14 +228,16 @@ func Test_EnsurePort(t *testing.T) { { name: "disable port security also ignores allowed address pairs", port: infrav1.ResolvedPortSpec{ - Name: "test-port", - NetworkID: netID, - ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{ - DisablePortSecurity: ptr.To(true), - AllowedAddressPairs: []infrav1.AddressPair{ - { - IPAddress: ipAddress1, - MACAddress: ptr.To(macAddress), + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "test-port", + NetworkID: netID, + ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{ + DisablePortSecurity: ptr.To(true), + AllowedAddressPairs: []infrav1.AddressPair{ + { + IPAddress: ipAddress1, + MACAddress: ptr.To(macAddress), + }, }, }, }, @@ -262,14 +270,16 @@ func Test_EnsurePort(t *testing.T) { { name: "disable port security explicitly false includes allowed address pairs", port: infrav1.ResolvedPortSpec{ - Name: "test-port", - NetworkID: netID, - ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{ - DisablePortSecurity: ptr.To(false), - AllowedAddressPairs: []infrav1.AddressPair{ - { - IPAddress: ipAddress1, - MACAddress: ptr.To(macAddress), + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "test-port", + NetworkID: netID, + ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{ + DisablePortSecurity: ptr.To(false), + AllowedAddressPairs: []infrav1.AddressPair{ + { + IPAddress: ipAddress1, + MACAddress: ptr.To(macAddress), + }, }, }, }, @@ -308,10 +318,12 @@ func Test_EnsurePort(t *testing.T) { { name: "create port with tags and trunk", port: infrav1.ResolvedPortSpec{ - Name: "test-port", - NetworkID: netID, - Tags: []string{"tag1", "tag2"}, - Trunk: ptr.To(true), + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "test-port", + NetworkID: netID, + Tags: []string{"tag1", "tag2"}, + }, + Trunk: ptr.To(true), }, expect: func(m *mock.MockNetworkClientMockRecorder, g types.Gomega) { var expectedCreateOpts ports.CreateOptsBuilder @@ -361,10 +373,12 @@ func Test_EnsurePort(t *testing.T) { { name: "port with tags and trunk already exists", port: infrav1.ResolvedPortSpec{ - Name: "test-port", - NetworkID: netID, - Tags: []string{"tag1", "tag2"}, - Trunk: ptr.To(true), + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "test-port", + NetworkID: netID, + Tags: []string{"tag1", "tag2"}, + }, + Trunk: ptr.To(true), }, expect: func(m *mock.MockNetworkClientMockRecorder, _ types.Gomega) { m.ListPort(ports.ListOpts{ @@ -396,10 +410,12 @@ func Test_EnsurePort(t *testing.T) { { name: "partial port missing tags and trunk", port: infrav1.ResolvedPortSpec{ - Name: "test-port", - NetworkID: netID, - Tags: []string{"tag1", "tag2"}, - Trunk: ptr.To(true), + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "test-port", + NetworkID: netID, + Tags: []string{"tag1", "tag2"}, + }, + Trunk: ptr.To(true), }, expect: func(m *mock.MockNetworkClientMockRecorder, _ types.Gomega) { m.ListPort(ports.ListOpts{ @@ -475,6 +491,7 @@ func TestService_ConstructPorts(t *testing.T) { defaultSubnetID = "d8dbba89-8c39-4192-a571-e702fca35bac" networkID = "afa54944-1443-4132-9ef5-ce37eb4d6ab6" + subportNetID = "a4e891a1-0fee-4a87-acbe-0e21e7af78d5" subnetID1 = "d786e715-c299-4a97-911d-640c10fc0392" subnetID2 = "41ad8201-5b2f-4e0e-b29d-3d82fad6ef10" securityGroupID1 = "044f6d31-3938-4f09-ad45-47b661e2ba1c" @@ -502,12 +519,14 @@ func TestService_ConstructPorts(t *testing.T) { spec: infrav1.OpenStackMachineSpec{}, want: []infrav1.ResolvedPortSpec{ { - Name: "test-instance-0", - Description: defaultDescription, - Tags: []string{"test-tag"}, - NetworkID: defaultNetworkID, - FixedIPs: []infrav1.ResolvedFixedIP{ - {SubnetID: ptr.To(defaultSubnetID)}, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "test-instance-0", + Description: defaultDescription, + Tags: []string{"test-tag"}, + NetworkID: defaultNetworkID, + FixedIPs: []infrav1.ResolvedFixedIP{ + {SubnetID: ptr.To(defaultSubnetID)}, + }, }, }, }, @@ -517,23 +536,27 @@ func TestService_ConstructPorts(t *testing.T) { spec: infrav1.OpenStackMachineSpec{ Ports: []infrav1.PortOpts{ { - NameSuffix: ptr.To("custom"), - Network: nil, - FixedIPs: nil, + CommonPortOpts: infrav1.CommonPortOpts{ + NameSuffix: ptr.To("custom"), + Network: nil, + FixedIPs: nil, + }, }, }, }, want: []infrav1.ResolvedPortSpec{ { - Name: "test-instance-custom", - Description: defaultDescription, - NetworkID: defaultNetworkID, - FixedIPs: []infrav1.ResolvedFixedIP{ - { - SubnetID: ptr.To(defaultSubnetID), + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "test-instance-custom", + Description: defaultDescription, + NetworkID: defaultNetworkID, + FixedIPs: []infrav1.ResolvedFixedIP{ + { + SubnetID: ptr.To(defaultSubnetID), + }, }, + Tags: []string{"test-tag"}, }, - Tags: []string{"test-tag"}, }, }, }, @@ -542,9 +565,11 @@ func TestService_ConstructPorts(t *testing.T) { spec: infrav1.OpenStackMachineSpec{ Ports: []infrav1.PortOpts{ { - NameSuffix: ptr.To("custom"), - Network: nil, - FixedIPs: nil, + CommonPortOpts: infrav1.CommonPortOpts{ + NameSuffix: ptr.To("custom"), + Network: nil, + FixedIPs: nil, + }, }, }, Trunk: true, @@ -554,13 +579,15 @@ func TestService_ConstructPorts(t *testing.T) { }, want: []infrav1.ResolvedPortSpec{ { - Name: "test-instance-custom", - Description: defaultDescription, - NetworkID: defaultNetworkID, - FixedIPs: []infrav1.ResolvedFixedIP{ - {SubnetID: ptr.To(defaultSubnetID)}, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "test-instance-custom", + Description: defaultDescription, + NetworkID: defaultNetworkID, + FixedIPs: []infrav1.ResolvedFixedIP{ + {SubnetID: ptr.To(defaultSubnetID)}, + }, + Tags: []string{"test-tag"}, }, - Tags: []string{"test-tag"}, Trunk: ptr.To(true), }, }, @@ -581,13 +608,15 @@ func TestService_ConstructPorts(t *testing.T) { }, want: []infrav1.ResolvedPortSpec{ { - Name: "test-instance-0", - Description: defaultDescription, - NetworkID: defaultNetworkID, - FixedIPs: []infrav1.ResolvedFixedIP{ - {SubnetID: ptr.To(defaultSubnetID)}, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "test-instance-0", + Description: defaultDescription, + NetworkID: defaultNetworkID, + FixedIPs: []infrav1.ResolvedFixedIP{ + {SubnetID: ptr.To(defaultSubnetID)}, + }, + Tags: []string{"test-tag"}, }, - Tags: []string{"test-tag"}, Trunk: ptr.To(true), }, }, @@ -597,20 +626,24 @@ func TestService_ConstructPorts(t *testing.T) { spec: infrav1.OpenStackMachineSpec{ Ports: []infrav1.PortOpts{ { - Network: &infrav1.NetworkParam{ - ID: ptr.To(networkID), + CommonPortOpts: infrav1.CommonPortOpts{ + Network: &infrav1.NetworkParam{ + ID: ptr.To(networkID), + }, }, }, }, }, want: []infrav1.ResolvedPortSpec{ { - NetworkID: networkID, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + NetworkID: networkID, - // Defaults - Name: "test-instance-0", - Description: defaultDescription, - Tags: []string{"test-tag"}, + // Defaults + Name: "test-instance-0", + Description: defaultDescription, + Tags: []string{"test-tag"}, + }, }, }, }, @@ -619,8 +652,10 @@ func TestService_ConstructPorts(t *testing.T) { spec: infrav1.OpenStackMachineSpec{ Ports: []infrav1.PortOpts{ { - Network: &infrav1.NetworkParam{ - Filter: &infrav1.NetworkFilter{Name: "test-network"}, + CommonPortOpts: infrav1.CommonPortOpts{ + Network: &infrav1.NetworkParam{ + Filter: &infrav1.NetworkFilter{Name: "test-network"}, + }, }, }, }, @@ -632,12 +667,14 @@ func TestService_ConstructPorts(t *testing.T) { }, want: []infrav1.ResolvedPortSpec{ { - NetworkID: networkID, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + NetworkID: networkID, - // Defaults - Name: "test-instance-0", - Description: defaultDescription, - Tags: []string{"test-tag"}, + // Defaults + Name: "test-instance-0", + Description: defaultDescription, + Tags: []string{"test-tag"}, + }, }, }, }, @@ -646,10 +683,12 @@ func TestService_ConstructPorts(t *testing.T) { spec: infrav1.OpenStackMachineSpec{ Ports: []infrav1.PortOpts{ { - FixedIPs: []infrav1.FixedIP{ - { - Subnet: &infrav1.SubnetParam{ - ID: ptr.To(subnetID1), + CommonPortOpts: infrav1.CommonPortOpts{ + FixedIPs: []infrav1.FixedIP{ + { + Subnet: &infrav1.SubnetParam{ + ID: ptr.To(subnetID1), + }, }, }, }, @@ -661,15 +700,17 @@ func TestService_ConstructPorts(t *testing.T) { }, want: []infrav1.ResolvedPortSpec{ { - NetworkID: networkID, - FixedIPs: []infrav1.ResolvedFixedIP{ - {SubnetID: ptr.To(subnetID1)}, - }, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + NetworkID: networkID, + FixedIPs: []infrav1.ResolvedFixedIP{ + {SubnetID: ptr.To(subnetID1)}, + }, - // Defaults - Name: "test-instance-0", - Description: defaultDescription, - Tags: []string{"test-tag"}, + // Defaults + Name: "test-instance-0", + Description: defaultDescription, + Tags: []string{"test-tag"}, + }, }, }, }, @@ -678,10 +719,12 @@ func TestService_ConstructPorts(t *testing.T) { spec: infrav1.OpenStackMachineSpec{ Ports: []infrav1.PortOpts{ { - FixedIPs: []infrav1.FixedIP{ - { - Subnet: &infrav1.SubnetParam{ - Filter: &infrav1.SubnetFilter{Name: "test-subnet"}, + CommonPortOpts: infrav1.CommonPortOpts{ + FixedIPs: []infrav1.FixedIP{ + { + Subnet: &infrav1.SubnetParam{ + Filter: &infrav1.SubnetFilter{Name: "test-subnet"}, + }, }, }, }, @@ -695,17 +738,19 @@ func TestService_ConstructPorts(t *testing.T) { }, want: []infrav1.ResolvedPortSpec{ { - NetworkID: networkID, - FixedIPs: []infrav1.ResolvedFixedIP{ - { - SubnetID: ptr.To(subnetID1), + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + NetworkID: networkID, + FixedIPs: []infrav1.ResolvedFixedIP{ + { + SubnetID: ptr.To(subnetID1), + }, }, - }, - // Defaults - Name: "test-instance-0", - Description: defaultDescription, - Tags: []string{"test-tag"}, + // Defaults + Name: "test-instance-0", + Description: defaultDescription, + Tags: []string{"test-tag"}, + }, }, }, }, @@ -714,10 +759,12 @@ func TestService_ConstructPorts(t *testing.T) { spec: infrav1.OpenStackMachineSpec{ Ports: []infrav1.PortOpts{ { - FixedIPs: []infrav1.FixedIP{ - { - Subnet: &infrav1.SubnetParam{ - Filter: &infrav1.SubnetFilter{Name: "test-subnet"}, + CommonPortOpts: infrav1.CommonPortOpts{ + FixedIPs: []infrav1.FixedIP{ + { + Subnet: &infrav1.SubnetParam{ + Filter: &infrav1.SubnetFilter{Name: "test-subnet"}, + }, }, }, }, @@ -734,10 +781,12 @@ func TestService_ConstructPorts(t *testing.T) { spec: infrav1.OpenStackMachineSpec{ Ports: []infrav1.PortOpts{ { - FixedIPs: []infrav1.FixedIP{ - { - Subnet: &infrav1.SubnetParam{ - Filter: &infrav1.SubnetFilter{Name: "test-subnet"}, + CommonPortOpts: infrav1.CommonPortOpts{ + FixedIPs: []infrav1.FixedIP{ + { + Subnet: &infrav1.SubnetParam{ + Filter: &infrav1.SubnetFilter{Name: "test-subnet"}, + }, }, }, }, @@ -757,15 +806,17 @@ func TestService_ConstructPorts(t *testing.T) { spec: infrav1.OpenStackMachineSpec{ Ports: []infrav1.PortOpts{ { - FixedIPs: []infrav1.FixedIP{ - { - Subnet: &infrav1.SubnetParam{ - Filter: &infrav1.SubnetFilter{Name: "test-subnet1"}, + CommonPortOpts: infrav1.CommonPortOpts{ + FixedIPs: []infrav1.FixedIP{ + { + Subnet: &infrav1.SubnetParam{ + Filter: &infrav1.SubnetFilter{Name: "test-subnet1"}, + }, }, - }, - { - Subnet: &infrav1.SubnetParam{ - Filter: &infrav1.SubnetFilter{Name: "test-subnet2"}, + { + Subnet: &infrav1.SubnetParam{ + Filter: &infrav1.SubnetFilter{Name: "test-subnet2"}, + }, }, }, }, @@ -787,20 +838,22 @@ func TestService_ConstructPorts(t *testing.T) { }, want: []infrav1.ResolvedPortSpec{ { - NetworkID: networkID, - FixedIPs: []infrav1.ResolvedFixedIP{ - { - SubnetID: ptr.To(subnetID1), - }, - { - SubnetID: ptr.To(subnetID2), + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + NetworkID: networkID, + FixedIPs: []infrav1.ResolvedFixedIP{ + { + SubnetID: ptr.To(subnetID1), + }, + { + SubnetID: ptr.To(subnetID2), + }, }, - }, - // Defaults - Name: "test-instance-0", - Description: defaultDescription, - Tags: []string{"test-tag"}, + // Defaults + Name: "test-instance-0", + Description: defaultDescription, + Tags: []string{"test-tag"}, + }, }, }, }, @@ -818,14 +871,16 @@ func TestService_ConstructPorts(t *testing.T) { }, want: []infrav1.ResolvedPortSpec{ { - Name: "test-instance-0", - NetworkID: defaultNetworkID, - FixedIPs: []infrav1.ResolvedFixedIP{ - {SubnetID: ptr.To(defaultSubnetID)}, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "test-instance-0", + NetworkID: defaultNetworkID, + FixedIPs: []infrav1.ResolvedFixedIP{ + {SubnetID: ptr.To(defaultSubnetID)}, + }, + Description: defaultDescription, + Tags: []string{"test-tag"}, + SecurityGroups: []string{securityGroupID1}, }, - Description: defaultDescription, - Tags: []string{"test-tag"}, - SecurityGroups: []string{securityGroupID1}, }, }, }, @@ -836,7 +891,11 @@ func TestService_ConstructPorts(t *testing.T) { {Filter: &infrav1.SecurityGroupFilter{Name: "machine-security-group"}}, }, Ports: []infrav1.PortOpts{ - {SecurityGroups: []infrav1.SecurityGroupParam{{Filter: &infrav1.SecurityGroupFilter{Name: "port-security-group"}}}}, + { + CommonPortOpts: infrav1.CommonPortOpts{ + SecurityGroups: []infrav1.SecurityGroupParam{{Filter: &infrav1.SecurityGroupFilter{Name: "port-security-group"}}}, + }, + }, }, }, expectNetwork: func(m *mock.MockNetworkClientMockRecorder) { @@ -849,14 +908,16 @@ func TestService_ConstructPorts(t *testing.T) { }, want: []infrav1.ResolvedPortSpec{ { - Name: "test-instance-0", - NetworkID: defaultNetworkID, - FixedIPs: []infrav1.ResolvedFixedIP{ - {SubnetID: ptr.To(defaultSubnetID)}, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "test-instance-0", + NetworkID: defaultNetworkID, + FixedIPs: []infrav1.ResolvedFixedIP{ + {SubnetID: ptr.To(defaultSubnetID)}, + }, + Description: defaultDescription, + Tags: []string{"test-tag"}, + SecurityGroups: []string{securityGroupID2}, }, - Description: defaultDescription, - Tags: []string{"test-tag"}, - SecurityGroups: []string{securityGroupID2}, }, }, }, @@ -866,14 +927,16 @@ func TestService_ConstructPorts(t *testing.T) { managedSecurityGroup: ptr.To(securityGroupID1), want: []infrav1.ResolvedPortSpec{ { - Name: "test-instance-0", - NetworkID: defaultNetworkID, - FixedIPs: []infrav1.ResolvedFixedIP{ - {SubnetID: ptr.To(defaultSubnetID)}, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "test-instance-0", + NetworkID: defaultNetworkID, + FixedIPs: []infrav1.ResolvedFixedIP{ + {SubnetID: ptr.To(defaultSubnetID)}, + }, + Description: defaultDescription, + Tags: []string{"test-tag"}, + SecurityGroups: []string{securityGroupID1}, }, - Description: defaultDescription, - Tags: []string{"test-tag"}, - SecurityGroups: []string{securityGroupID1}, }, }, }, @@ -890,14 +953,16 @@ func TestService_ConstructPorts(t *testing.T) { }, want: []infrav1.ResolvedPortSpec{ { - Name: "test-instance-0", - NetworkID: defaultNetworkID, - FixedIPs: []infrav1.ResolvedFixedIP{ - {SubnetID: ptr.To(defaultSubnetID)}, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "test-instance-0", + NetworkID: defaultNetworkID, + FixedIPs: []infrav1.ResolvedFixedIP{ + {SubnetID: ptr.To(defaultSubnetID)}, + }, + Description: defaultDescription, + Tags: []string{"test-tag"}, + SecurityGroups: []string{securityGroupID2, securityGroupID1}, }, - Description: defaultDescription, - Tags: []string{"test-tag"}, - SecurityGroups: []string{securityGroupID2, securityGroupID1}, }, }, }, @@ -909,8 +974,10 @@ func TestService_ConstructPorts(t *testing.T) { }, Ports: []infrav1.PortOpts{ { - ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{ - DisablePortSecurity: ptr.To(true), + CommonPortOpts: infrav1.CommonPortOpts{ + ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{ + DisablePortSecurity: ptr.To(true), + }, }, }, }, @@ -922,18 +989,107 @@ func TestService_ConstructPorts(t *testing.T) { }, want: []infrav1.ResolvedPortSpec{ { - Name: "test-instance-0", - NetworkID: defaultNetworkID, - FixedIPs: []infrav1.ResolvedFixedIP{ - {SubnetID: ptr.To(defaultSubnetID)}, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "test-instance-0", + NetworkID: defaultNetworkID, + FixedIPs: []infrav1.ResolvedFixedIP{ + {SubnetID: ptr.To(defaultSubnetID)}, + }, + Description: defaultDescription, + Tags: []string{"test-tag"}, + ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{ + DisablePortSecurity: ptr.To(true), + }, }, - Description: defaultDescription, - Tags: []string{"test-tag"}, - ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{ - DisablePortSecurity: ptr.To(true), + }, + }, + }, + { + name: "subports with trunk", + spec: infrav1.OpenStackMachineSpec{ + Ports: []infrav1.PortOpts{ + { + CommonPortOpts: infrav1.CommonPortOpts{ + Network: &infrav1.NetworkParam{ + Filter: &infrav1.NetworkFilter{Name: "test-network"}, + }, + }, + Trunk: ptr.To(true), + Subports: []infrav1.SubportOpts{ + { + CommonPortOpts: infrav1.CommonPortOpts{ + Network: &infrav1.NetworkParam{ + Filter: &infrav1.NetworkFilter{Name: "subport-network"}, + }, + }, + SegmentationID: 300, + SegmentationType: "vlan", + }, + }, + }, + }, + Trunk: false, + }, + expectNetwork: func(m *mock.MockNetworkClientMockRecorder) { + expectListExtensions(m) + m.ListNetwork(networks.ListOpts{Name: "test-network"}).Return([]networks.Network{ + {ID: networkID}, + }, nil) + m.ListNetwork(networks.ListOpts{Name: "subport-network"}).Return([]networks.Network{ + {ID: subportNetID}, + }, nil) + }, + want: []infrav1.ResolvedPortSpec{ + { + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "test-instance-0", + Description: defaultDescription, + NetworkID: networkID, + Tags: []string{"test-tag"}, + }, + Subports: []infrav1.ResolvedSubportSpec{ + { + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "test-instance-subport-0", + Description: defaultDescription, + NetworkID: subportNetID, + Tags: []string{"test-tag"}, + }, + SegmentationID: 300, + SegmentationType: "vlan", + }, + }, + Trunk: ptr.To(true), + }, + }, + }, + { + name: "subports error without network", + spec: infrav1.OpenStackMachineSpec{ + Ports: []infrav1.PortOpts{ + { + CommonPortOpts: infrav1.CommonPortOpts{ + Network: &infrav1.NetworkParam{ + Filter: &infrav1.NetworkFilter{Name: "test-network"}, + }, + }, + Trunk: ptr.To(true), + Subports: []infrav1.SubportOpts{ + { + SegmentationID: 300, + SegmentationType: "vlan", + }, + }, }, }, + Trunk: false, }, + expectNetwork: func(m *mock.MockNetworkClientMockRecorder) { + m.ListNetwork(networks.ListOpts{Name: "test-network"}).Return([]networks.Network{ + {ID: networkID}, + }, nil) + }, + wantErr: true, }, } for i := range tests { @@ -997,7 +1153,9 @@ func Test_getPortName(t *testing.T) { name: "with PortOpts name suffix", instanceName: "test-1-instance", spec: &infrav1.PortOpts{ - NameSuffix: ptr.To("foo"), + CommonPortOpts: infrav1.CommonPortOpts{ + NameSuffix: ptr.To("foo"), + }, }, netIndex: 4, want: "test-1-instance-foo", @@ -1013,10 +1171,12 @@ func Test_getPortName(t *testing.T) { name: "with PortOpts name suffix", instanceName: "test-1-instance", spec: &infrav1.PortOpts{ - NameSuffix: ptr.To("foo2"), - Network: &infrav1.NetworkParam{ID: ptr.To("bar")}, - ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{ - DisablePortSecurity: ptr.To(true), + CommonPortOpts: infrav1.CommonPortOpts{ + NameSuffix: ptr.To("foo2"), + Network: &infrav1.NetworkParam{ID: ptr.To("bar")}, + ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{ + DisablePortSecurity: ptr.To(true), + }, }, }, netIndex: 4, @@ -1055,7 +1215,11 @@ func Test_AdoptPortsServer(t *testing.T) { { testName: "desired port already in status: no-op", desiredPorts: []infrav1.ResolvedPortSpec{ - {NetworkID: networkID1}, + { + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + NetworkID: networkID1, + }, + }, }, resources: infrav1alpha1.ServerResources{ Ports: []infrav1.PortStatus{ diff --git a/pkg/cloud/services/networking/trunk.go b/pkg/cloud/services/networking/trunk.go index ffe393de7..b23d356a2 100644 --- a/pkg/cloud/services/networking/trunk.go +++ b/pkg/cloud/services/networking/trunk.go @@ -25,7 +25,10 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/utils/ptr" + infrav1alpha1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha1" + infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1" "sigs.k8s.io/cluster-api-provider-openstack/pkg/record" capoerrors "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/errors" ) @@ -114,6 +117,79 @@ func (s *Service) RemoveTrunkSubports(trunkID string) error { return nil } +// EnsurePorts ensures that every one of desiredPorts is created and has +// expected trunk and tags. +func (s *Service) EnsureTrunkSubPorts(eventObject runtime.Object, desiredPorts []infrav1.ResolvedPortSpec, resources *infrav1alpha1.ServerResources) error { + for i := range desiredPorts { + // Fail here if trunk port has not been created + if i >= len(resources.Ports) { + return fmt.Errorf("ports have not been created") + } + + var newSubports []trunks.Subport + if ptr.Deref(desiredPorts[i].Trunk, false) { + // Retrieve trunk subports and transform it to map for faster validation + trnks, err := s.client.ListTrunk(trunks.ListOpts{PortID: resources.Ports[i].ID}) + if err != nil { + return fmt.Errorf("searching trunk: %v", err) + } + + if len(trnks) > 1 { + return fmt.Errorf("multiple trunks found for port \"%s\"", resources.Ports[i].ID) + } + + if len(trnks) == 0 { + return fmt.Errorf("trunks not found for port \"%s\"", resources.Ports[i].ID) + } + + existingSubports, err := s.client.ListTrunkSubports(trnks[0].ID) + if err != nil { + return fmt.Errorf("searching for existing port for server: %v", err) + } + subportMap := make(map[string]trunks.Subport) + for _, existringSubport := range existingSubports { + subportMap[existringSubport.PortID] = existringSubport + } + + // Ensure trunk subports are created as ports + for j, desiredSubport := range desiredPorts[i].Subports { + subportStatus := infrav1.PortStatus{} + if j < len(resources.Ports[i].Subports) { + subportStatus.ID = resources.Ports[i].Subports[j].ID + } + subport, err := s.EnsurePort(eventObject, &infrav1.ResolvedPortSpec{CommonResolvedPortSpec: desiredSubport.CommonResolvedPortSpec}, subportStatus) + if err != nil { + return err + } + + if _, found := subportMap[subport.ID]; !found { + newSubports = append(newSubports, trunks.Subport{ + SegmentationID: desiredSubport.SegmentationID, + SegmentationType: desiredSubport.SegmentationType, + PortID: subport.ID, + }) + } + + if j < len(resources.Ports[i].Subports) { + resources.Ports[i].Subports[j].ID = subportStatus.ID + } else { + resources.Ports[i].Subports = append(resources.Ports[i].Subports, infrav1.SubPortStatus{ID: subport.ID}) + } + } + + // Append subports to trunk + if len(newSubports) > 0 { + _, err = s.client.AddSubports(trnks[0].ID, trunks.AddSubportsOpts{Subports: newSubports}) + if err != nil { + return fmt.Errorf("failed to add subports to trunk %s: %v", trnks[0].ID, err) + } + } + } + } + + return nil +} + func (s *Service) DeleteTrunk(eventObject runtime.Object, portID string) error { listOpts := trunks.ListOpts{ PortID: portID, diff --git a/pkg/cloud/services/networking/trunk_test.go b/pkg/cloud/services/networking/trunk_test.go index 8989e8988..22e1ecfb6 100644 --- a/pkg/cloud/services/networking/trunk_test.go +++ b/pkg/cloud/services/networking/trunk_test.go @@ -17,13 +17,18 @@ limitations under the License. package networking import ( + "fmt" "testing" + "github.com/google/go-cmp/cmp" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/portsbinding" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" . "github.com/onsi/gomega" //nolint:revive "go.uber.org/mock/gomock" + "k8s.io/utils/ptr" + "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha1" infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1" "sigs.k8s.io/cluster-api-provider-openstack/pkg/clients/mock" ) @@ -111,3 +116,413 @@ func Test_GetOrCreateTrunk(t *testing.T) { }) } } + +func Test_EnsureTrunkSubPorts(t *testing.T) { + // Arbitrary values used in the tests + const ( + netID = "7fd24ceb-788a-441f-ad0a-d8e2f5d31a1d" + portID = "50214c48-c09e-4a54-914f-97b40fd22802" + trunkPortID = "54fe7034-8e72-4e8d-92af-a9f02aed7f6f" + trunkID = "2efdd5e5-c85b-419f-8c59-678b5fccbdba" + subportID = "d002cdd7-7343-4376-997b-7f15cf97b89c" + + segmentationID = 300 + segmentationType = "vlan" + ) + + tests := []struct { + name string + ports []infrav1.ResolvedPortSpec + serverResources *v1alpha1.ServerResources + expect func(m *mock.MockNetworkClientMockRecorder, g Gomega) + // Note the 'wanted' port isn't so important, since it will be whatever we tell ListPort or EnsurePort to return. + // Mostly in this test suite, we're checking that EnsurePort is called with the expected port opts. + wantResources *v1alpha1.ServerResources + wantErr bool + }{ + { + name: "creates subports and assign to trunk with tag", + ports: []infrav1.ResolvedPortSpec{ + { + Trunk: ptr.To(true), + Subports: []infrav1.ResolvedSubportSpec{ + { + SegmentationID: segmentationID, + SegmentationType: segmentationType, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "foo-port-1", + Description: "Created by cluster-api-provider-openstack cluster test-cluster", + NetworkID: netID, + }, + }, + }, + }, + }, + serverResources: &v1alpha1.ServerResources{ + Ports: []infrav1.PortStatus{ + {ID: trunkPortID}, + }, + }, + expect: func(m *mock.MockNetworkClientMockRecorder, g Gomega) { + var expectedCreateOpts ports.CreateOptsBuilder + expectedCreateOpts = ports.CreateOpts{ + Name: "foo-port-1", + Description: "Created by cluster-api-provider-openstack cluster test-cluster", + NetworkID: netID, + } + expectedCreateOpts = portsbinding.CreateOptsExt{ + CreateOptsBuilder: expectedCreateOpts, + } + + m.ListTrunk(trunks.ListOpts{PortID: trunkPortID}).Return([]trunks.Trunk{{ID: trunkID}}, nil) + m.ListTrunkSubports(trunkID).Return(nil, nil) + m.ListPort(ports.ListOpts{ + Name: "foo-port-1", + NetworkID: netID, + }).Return(nil, nil) + // The following allows us to use gomega to + // compare the argument instead of gomock. + // Gomock's output in the case of a mismatch is + // not usable for this struct. + m.CreatePort(gomock.Any()).DoAndReturn(func(builder ports.CreateOptsBuilder) (*ports.Port, error) { + gotCreateOpts := builder.(portsbinding.CreateOptsExt) + g.Expect(gotCreateOpts).To(Equal(expectedCreateOpts), cmp.Diff(gotCreateOpts, expectedCreateOpts)) + return &ports.Port{ID: subportID}, nil + }) + m.AddSubports( + trunkID, + trunks.AddSubportsOpts{ + Subports: []trunks.Subport{ + { + SegmentationID: segmentationID, + SegmentationType: segmentationType, + PortID: subportID, + }, + }, + }, + ) + }, + wantResources: &v1alpha1.ServerResources{ + Ports: []infrav1.PortStatus{ + { + ID: trunkPortID, + Subports: []infrav1.SubPortStatus{{ID: subportID}}, + }, + }, + }, + }, + { + name: "error if resources.Ports is not defined", + ports: []infrav1.ResolvedPortSpec{ + { + Trunk: ptr.To(true), + Subports: []infrav1.ResolvedSubportSpec{ + { + SegmentationID: segmentationID, + SegmentationType: segmentationType, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "foo-port-1", + Description: "Created by cluster-api-provider-openstack cluster test-cluster", + NetworkID: netID, + }, + }, + }, + }, + }, + serverResources: &v1alpha1.ServerResources{ + Ports: nil, + }, + expect: func(m *mock.MockNetworkClientMockRecorder, g Gomega) { + // No calls expected + }, + wantResources: &v1alpha1.ServerResources{ + Ports: nil, + }, + wantErr: true, + }, + { + name: "error if ListTrunk returns error", + ports: []infrav1.ResolvedPortSpec{ + { + Trunk: ptr.To(true), + Subports: []infrav1.ResolvedSubportSpec{ + { + SegmentationID: segmentationID, + SegmentationType: segmentationType, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "foo-port-1", + Description: "Created by cluster-api-provider-openstack cluster test-cluster", + NetworkID: netID, + }, + }, + }, + }, + }, + serverResources: &v1alpha1.ServerResources{ + Ports: []infrav1.PortStatus{ + {ID: trunkPortID}, + }, + }, + expect: func(m *mock.MockNetworkClientMockRecorder, g Gomega) { + m.ListTrunk(trunks.ListOpts{PortID: trunkPortID}).Return(nil, fmt.Errorf("list trunk error")) + }, + wantResources: &v1alpha1.ServerResources{ + Ports: []infrav1.PortStatus{ + {ID: trunkPortID}, + }, + }, + wantErr: true, + }, + { + name: "error if multiple trunks found", + ports: []infrav1.ResolvedPortSpec{ + { + Trunk: ptr.To(true), + Subports: []infrav1.ResolvedSubportSpec{ + { + SegmentationID: segmentationID, + SegmentationType: segmentationType, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "foo-port-1", + Description: "Created by cluster-api-provider-openstack cluster test-cluster", + NetworkID: netID, + }, + }, + }, + }, + }, + serverResources: &v1alpha1.ServerResources{ + Ports: []infrav1.PortStatus{ + {ID: trunkPortID}, + }, + }, + expect: func(m *mock.MockNetworkClientMockRecorder, g Gomega) { + m.ListTrunk(trunks.ListOpts{PortID: trunkPortID}).Return([]trunks.Trunk{{ID: trunkID}, {ID: "another-trunk"}}, nil) + }, + wantResources: &v1alpha1.ServerResources{ + Ports: []infrav1.PortStatus{ + {ID: trunkPortID}, + }, + }, + wantErr: true, + }, + { + name: "error if no trunks found", + ports: []infrav1.ResolvedPortSpec{ + { + Trunk: ptr.To(true), + Subports: []infrav1.ResolvedSubportSpec{ + { + SegmentationID: segmentationID, + SegmentationType: segmentationType, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "foo-port-1", + Description: "Created by cluster-api-provider-openstack cluster test-cluster", + NetworkID: netID, + }, + }, + }, + }, + }, + serverResources: &v1alpha1.ServerResources{ + Ports: []infrav1.PortStatus{ + {ID: trunkPortID}, + }, + }, + expect: func(m *mock.MockNetworkClientMockRecorder, g Gomega) { + m.ListTrunk(trunks.ListOpts{PortID: trunkPortID}).Return([]trunks.Trunk{}, nil) + }, + wantResources: &v1alpha1.ServerResources{ + Ports: []infrav1.PortStatus{ + {ID: trunkPortID}, + }, + }, + wantErr: true, + }, + { + name: "error from ListTrunkSubports", + ports: []infrav1.ResolvedPortSpec{ + { + Trunk: ptr.To(true), + Subports: []infrav1.ResolvedSubportSpec{ + { + SegmentationID: segmentationID, + SegmentationType: segmentationType, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "foo-port-1", + Description: "Created by cluster-api-provider-openstack cluster test-cluster", + NetworkID: netID, + }, + }, + }, + }, + }, + serverResources: &v1alpha1.ServerResources{ + Ports: []infrav1.PortStatus{ + {ID: trunkPortID}, + }, + }, + expect: func(m *mock.MockNetworkClientMockRecorder, g Gomega) { + m.ListTrunk(trunks.ListOpts{PortID: trunkPortID}).Return([]trunks.Trunk{{ID: trunkID}}, nil) + m.ListTrunkSubports(trunkID).Return(nil, fmt.Errorf("subports error")) + }, + wantResources: &v1alpha1.ServerResources{ + Ports: []infrav1.PortStatus{ + {ID: trunkPortID}, + }, + }, + wantErr: true, + }, + { + name: "error from AddSubports", + ports: []infrav1.ResolvedPortSpec{ + { + Trunk: ptr.To(true), + Subports: []infrav1.ResolvedSubportSpec{ + { + SegmentationID: segmentationID, + SegmentationType: segmentationType, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "foo-port-1", + Description: "Created by cluster-api-provider-openstack cluster test-cluster", + NetworkID: netID, + }, + }, + }, + }, + }, + serverResources: &v1alpha1.ServerResources{ + Ports: []infrav1.PortStatus{ + {ID: trunkPortID}, + }, + }, + expect: func(m *mock.MockNetworkClientMockRecorder, g Gomega) { + var expectedCreateOpts ports.CreateOptsBuilder + expectedCreateOpts = ports.CreateOpts{ + Name: "foo-port-1", + Description: "Created by cluster-api-provider-openstack cluster test-cluster", + NetworkID: netID, + } + expectedCreateOpts = portsbinding.CreateOptsExt{ + CreateOptsBuilder: expectedCreateOpts, + } + + m.ListTrunk(trunks.ListOpts{PortID: trunkPortID}).Return([]trunks.Trunk{{ID: trunkID}}, nil) + m.ListTrunkSubports(trunkID).Return(nil, nil) + m.ListPort(ports.ListOpts{ + Name: "foo-port-1", + NetworkID: netID, + }).Return(nil, nil) + m.CreatePort(gomock.Any()).DoAndReturn(func(builder ports.CreateOptsBuilder) (*ports.Port, error) { + gotCreateOpts := builder.(portsbinding.CreateOptsExt) + g.Expect(gotCreateOpts).To(Equal(expectedCreateOpts), cmp.Diff(gotCreateOpts, expectedCreateOpts)) + return &ports.Port{ID: subportID}, nil + }) + m.AddSubports( + trunkID, + trunks.AddSubportsOpts{ + Subports: []trunks.Subport{ + { + SegmentationID: segmentationID, + SegmentationType: segmentationType, + PortID: subportID, + }, + }, + }, + ).Return(nil, fmt.Errorf("add subports error")) + }, + wantResources: &v1alpha1.ServerResources{ + Ports: []infrav1.PortStatus{ + {ID: trunkPortID, Subports: []infrav1.SubPortStatus{{ID: subportID}}}, + }, + }, + wantErr: true, + }, + { + name: "error creating port for subport", + ports: []infrav1.ResolvedPortSpec{ + { + Trunk: ptr.To(true), + Subports: []infrav1.ResolvedSubportSpec{ + { + SegmentationID: segmentationID, + SegmentationType: segmentationType, + CommonResolvedPortSpec: infrav1.CommonResolvedPortSpec{ + Name: "foo-port-1", + Description: "Created by cluster-api-provider-openstack cluster test-cluster", + NetworkID: netID, + }, + }, + }, + }, + }, + serverResources: &v1alpha1.ServerResources{ + Ports: []infrav1.PortStatus{ + {ID: trunkPortID}, + }, + }, + expect: func(m *mock.MockNetworkClientMockRecorder, g Gomega) { + var expectedCreateOpts ports.CreateOptsBuilder + expectedCreateOpts = ports.CreateOpts{ + Name: "foo-port-1", + Description: "Created by cluster-api-provider-openstack cluster test-cluster", + NetworkID: netID, + } + expectedCreateOpts = portsbinding.CreateOptsExt{ + CreateOptsBuilder: expectedCreateOpts, + } + + m.ListTrunk(trunks.ListOpts{PortID: trunkPortID}).Return([]trunks.Trunk{{ID: trunkID}}, nil) + m.ListTrunkSubports(trunkID).Return(nil, nil) + m.ListPort(ports.ListOpts{ + Name: "foo-port-1", + NetworkID: netID, + }).Return(nil, nil) + // The following allows us to use gomega to + // compare the argument instead of gomock. + // Gomock's output in the case of a mismatch is + // not usable for this struct. + m.CreatePort(gomock.Any()).DoAndReturn(func(builder ports.CreateOptsBuilder) (*ports.Port, error) { + gotCreateOpts := builder.(portsbinding.CreateOptsExt) + g.Expect(gotCreateOpts).To(Equal(expectedCreateOpts), cmp.Diff(gotCreateOpts, expectedCreateOpts)) + return nil, fmt.Errorf("ensure port error") + }) + }, + wantResources: &v1alpha1.ServerResources{ + Ports: []infrav1.PortStatus{ + { + ID: trunkPortID, + }, + }, + }, + wantErr: true, + }, + } + + eventObject := &infrav1.OpenStackMachine{} + for i := range tests { + tt := tests[i] + t.Run(tt.name, func(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + g := NewWithT(t) + mockClient := mock.NewMockNetworkClient(mockCtrl) + tt.expect(mockClient.EXPECT(), g) + s := Service{ + client: mockClient, + } + err := s.EnsureTrunkSubPorts( + eventObject, + tt.ports, + tt.serverResources, + ) + if tt.wantErr { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).NotTo(HaveOccurred()) + } + g.Expect(tt.serverResources).To(Equal(tt.wantResources)) + }) + } +} diff --git a/pkg/generated/applyconfiguration/api/v1beta1/commonportopts.go b/pkg/generated/applyconfiguration/api/v1beta1/commonportopts.go new file mode 100644 index 000000000..52d71cac6 --- /dev/null +++ b/pkg/generated/applyconfiguration/api/v1beta1/commonportopts.go @@ -0,0 +1,179 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1beta1 + +// CommonPortOptsApplyConfiguration represents a declarative configuration of the CommonPortOpts type for use +// with apply. +type CommonPortOptsApplyConfiguration struct { + Network *NetworkParamApplyConfiguration `json:"network,omitempty"` + Description *string `json:"description,omitempty"` + NameSuffix *string `json:"nameSuffix,omitempty"` + FixedIPs []FixedIPApplyConfiguration `json:"fixedIPs,omitempty"` + SecurityGroups []SecurityGroupParamApplyConfiguration `json:"securityGroups,omitempty"` + Tags []string `json:"tags,omitempty"` + ResolvedPortSpecFieldsApplyConfiguration `json:",inline"` +} + +// CommonPortOptsApplyConfiguration constructs a declarative configuration of the CommonPortOpts type for use with +// apply. +func CommonPortOpts() *CommonPortOptsApplyConfiguration { + return &CommonPortOptsApplyConfiguration{} +} + +// WithNetwork sets the Network field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Network field is set to the value of the last call. +func (b *CommonPortOptsApplyConfiguration) WithNetwork(value *NetworkParamApplyConfiguration) *CommonPortOptsApplyConfiguration { + b.Network = value + return b +} + +// WithDescription sets the Description field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Description field is set to the value of the last call. +func (b *CommonPortOptsApplyConfiguration) WithDescription(value string) *CommonPortOptsApplyConfiguration { + b.Description = &value + return b +} + +// WithNameSuffix sets the NameSuffix field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the NameSuffix field is set to the value of the last call. +func (b *CommonPortOptsApplyConfiguration) WithNameSuffix(value string) *CommonPortOptsApplyConfiguration { + b.NameSuffix = &value + return b +} + +// WithFixedIPs adds the given value to the FixedIPs field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the FixedIPs field. +func (b *CommonPortOptsApplyConfiguration) WithFixedIPs(values ...*FixedIPApplyConfiguration) *CommonPortOptsApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithFixedIPs") + } + b.FixedIPs = append(b.FixedIPs, *values[i]) + } + return b +} + +// WithSecurityGroups adds the given value to the SecurityGroups field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the SecurityGroups field. +func (b *CommonPortOptsApplyConfiguration) WithSecurityGroups(values ...*SecurityGroupParamApplyConfiguration) *CommonPortOptsApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithSecurityGroups") + } + b.SecurityGroups = append(b.SecurityGroups, *values[i]) + } + return b +} + +// WithTags adds the given value to the Tags field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Tags field. +func (b *CommonPortOptsApplyConfiguration) WithTags(values ...string) *CommonPortOptsApplyConfiguration { + for i := range values { + b.Tags = append(b.Tags, values[i]) + } + return b +} + +// WithAdminStateUp sets the AdminStateUp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the AdminStateUp field is set to the value of the last call. +func (b *CommonPortOptsApplyConfiguration) WithAdminStateUp(value bool) *CommonPortOptsApplyConfiguration { + b.AdminStateUp = &value + return b +} + +// WithMACAddress sets the MACAddress field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the MACAddress field is set to the value of the last call. +func (b *CommonPortOptsApplyConfiguration) WithMACAddress(value string) *CommonPortOptsApplyConfiguration { + b.MACAddress = &value + return b +} + +// WithAllowedAddressPairs adds the given value to the AllowedAddressPairs field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the AllowedAddressPairs field. +func (b *CommonPortOptsApplyConfiguration) WithAllowedAddressPairs(values ...*AddressPairApplyConfiguration) *CommonPortOptsApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithAllowedAddressPairs") + } + b.AllowedAddressPairs = append(b.AllowedAddressPairs, *values[i]) + } + return b +} + +// WithHostID sets the HostID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the HostID field is set to the value of the last call. +func (b *CommonPortOptsApplyConfiguration) WithHostID(value string) *CommonPortOptsApplyConfiguration { + b.HostID = &value + return b +} + +// WithVNICType sets the VNICType field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the VNICType field is set to the value of the last call. +func (b *CommonPortOptsApplyConfiguration) WithVNICType(value string) *CommonPortOptsApplyConfiguration { + b.VNICType = &value + return b +} + +// WithProfile sets the Profile field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Profile field is set to the value of the last call. +func (b *CommonPortOptsApplyConfiguration) WithProfile(value *BindingProfileApplyConfiguration) *CommonPortOptsApplyConfiguration { + b.Profile = value + return b +} + +// WithDisablePortSecurity sets the DisablePortSecurity field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DisablePortSecurity field is set to the value of the last call. +func (b *CommonPortOptsApplyConfiguration) WithDisablePortSecurity(value bool) *CommonPortOptsApplyConfiguration { + b.DisablePortSecurity = &value + return b +} + +// WithPropagateUplinkStatus sets the PropagateUplinkStatus field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PropagateUplinkStatus field is set to the value of the last call. +func (b *CommonPortOptsApplyConfiguration) WithPropagateUplinkStatus(value bool) *CommonPortOptsApplyConfiguration { + b.PropagateUplinkStatus = &value + return b +} + +// WithValueSpecs adds the given value to the ValueSpecs field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the ValueSpecs field. +func (b *CommonPortOptsApplyConfiguration) WithValueSpecs(values ...*ValueSpecApplyConfiguration) *CommonPortOptsApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithValueSpecs") + } + b.ValueSpecs = append(b.ValueSpecs, *values[i]) + } + return b +} diff --git a/pkg/generated/applyconfiguration/api/v1beta1/commonresolvedportspec.go b/pkg/generated/applyconfiguration/api/v1beta1/commonresolvedportspec.go new file mode 100644 index 000000000..ddb3b4274 --- /dev/null +++ b/pkg/generated/applyconfiguration/api/v1beta1/commonresolvedportspec.go @@ -0,0 +1,176 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1beta1 + +// CommonResolvedPortSpecApplyConfiguration represents a declarative configuration of the CommonResolvedPortSpec type for use +// with apply. +type CommonResolvedPortSpecApplyConfiguration struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + NetworkID *string `json:"networkID,omitempty"` + Tags []string `json:"tags,omitempty"` + FixedIPs []ResolvedFixedIPApplyConfiguration `json:"fixedIPs,omitempty"` + SecurityGroups []string `json:"securityGroups,omitempty"` + ResolvedPortSpecFieldsApplyConfiguration `json:",inline"` +} + +// CommonResolvedPortSpecApplyConfiguration constructs a declarative configuration of the CommonResolvedPortSpec type for use with +// apply. +func CommonResolvedPortSpec() *CommonResolvedPortSpecApplyConfiguration { + return &CommonResolvedPortSpecApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *CommonResolvedPortSpecApplyConfiguration) WithName(value string) *CommonResolvedPortSpecApplyConfiguration { + b.Name = &value + return b +} + +// WithDescription sets the Description field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Description field is set to the value of the last call. +func (b *CommonResolvedPortSpecApplyConfiguration) WithDescription(value string) *CommonResolvedPortSpecApplyConfiguration { + b.Description = &value + return b +} + +// WithNetworkID sets the NetworkID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the NetworkID field is set to the value of the last call. +func (b *CommonResolvedPortSpecApplyConfiguration) WithNetworkID(value string) *CommonResolvedPortSpecApplyConfiguration { + b.NetworkID = &value + return b +} + +// WithTags adds the given value to the Tags field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Tags field. +func (b *CommonResolvedPortSpecApplyConfiguration) WithTags(values ...string) *CommonResolvedPortSpecApplyConfiguration { + for i := range values { + b.Tags = append(b.Tags, values[i]) + } + return b +} + +// WithFixedIPs adds the given value to the FixedIPs field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the FixedIPs field. +func (b *CommonResolvedPortSpecApplyConfiguration) WithFixedIPs(values ...*ResolvedFixedIPApplyConfiguration) *CommonResolvedPortSpecApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithFixedIPs") + } + b.FixedIPs = append(b.FixedIPs, *values[i]) + } + return b +} + +// WithSecurityGroups adds the given value to the SecurityGroups field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the SecurityGroups field. +func (b *CommonResolvedPortSpecApplyConfiguration) WithSecurityGroups(values ...string) *CommonResolvedPortSpecApplyConfiguration { + for i := range values { + b.SecurityGroups = append(b.SecurityGroups, values[i]) + } + return b +} + +// WithAdminStateUp sets the AdminStateUp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the AdminStateUp field is set to the value of the last call. +func (b *CommonResolvedPortSpecApplyConfiguration) WithAdminStateUp(value bool) *CommonResolvedPortSpecApplyConfiguration { + b.AdminStateUp = &value + return b +} + +// WithMACAddress sets the MACAddress field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the MACAddress field is set to the value of the last call. +func (b *CommonResolvedPortSpecApplyConfiguration) WithMACAddress(value string) *CommonResolvedPortSpecApplyConfiguration { + b.MACAddress = &value + return b +} + +// WithAllowedAddressPairs adds the given value to the AllowedAddressPairs field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the AllowedAddressPairs field. +func (b *CommonResolvedPortSpecApplyConfiguration) WithAllowedAddressPairs(values ...*AddressPairApplyConfiguration) *CommonResolvedPortSpecApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithAllowedAddressPairs") + } + b.AllowedAddressPairs = append(b.AllowedAddressPairs, *values[i]) + } + return b +} + +// WithHostID sets the HostID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the HostID field is set to the value of the last call. +func (b *CommonResolvedPortSpecApplyConfiguration) WithHostID(value string) *CommonResolvedPortSpecApplyConfiguration { + b.HostID = &value + return b +} + +// WithVNICType sets the VNICType field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the VNICType field is set to the value of the last call. +func (b *CommonResolvedPortSpecApplyConfiguration) WithVNICType(value string) *CommonResolvedPortSpecApplyConfiguration { + b.VNICType = &value + return b +} + +// WithProfile sets the Profile field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Profile field is set to the value of the last call. +func (b *CommonResolvedPortSpecApplyConfiguration) WithProfile(value *BindingProfileApplyConfiguration) *CommonResolvedPortSpecApplyConfiguration { + b.Profile = value + return b +} + +// WithDisablePortSecurity sets the DisablePortSecurity field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DisablePortSecurity field is set to the value of the last call. +func (b *CommonResolvedPortSpecApplyConfiguration) WithDisablePortSecurity(value bool) *CommonResolvedPortSpecApplyConfiguration { + b.DisablePortSecurity = &value + return b +} + +// WithPropagateUplinkStatus sets the PropagateUplinkStatus field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PropagateUplinkStatus field is set to the value of the last call. +func (b *CommonResolvedPortSpecApplyConfiguration) WithPropagateUplinkStatus(value bool) *CommonResolvedPortSpecApplyConfiguration { + b.PropagateUplinkStatus = &value + return b +} + +// WithValueSpecs adds the given value to the ValueSpecs field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the ValueSpecs field. +func (b *CommonResolvedPortSpecApplyConfiguration) WithValueSpecs(values ...*ValueSpecApplyConfiguration) *CommonResolvedPortSpecApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithValueSpecs") + } + b.ValueSpecs = append(b.ValueSpecs, *values[i]) + } + return b +} diff --git a/pkg/generated/applyconfiguration/api/v1beta1/portopts.go b/pkg/generated/applyconfiguration/api/v1beta1/portopts.go index b66fab862..e678fd162 100644 --- a/pkg/generated/applyconfiguration/api/v1beta1/portopts.go +++ b/pkg/generated/applyconfiguration/api/v1beta1/portopts.go @@ -21,14 +21,9 @@ package v1beta1 // PortOptsApplyConfiguration represents a declarative configuration of the PortOpts type for use // with apply. type PortOptsApplyConfiguration struct { - Network *NetworkParamApplyConfiguration `json:"network,omitempty"` - Description *string `json:"description,omitempty"` - NameSuffix *string `json:"nameSuffix,omitempty"` - FixedIPs []FixedIPApplyConfiguration `json:"fixedIPs,omitempty"` - SecurityGroups []SecurityGroupParamApplyConfiguration `json:"securityGroups,omitempty"` - Tags []string `json:"tags,omitempty"` - Trunk *bool `json:"trunk,omitempty"` - ResolvedPortSpecFieldsApplyConfiguration `json:",inline"` + Trunk *bool `json:"trunk,omitempty"` + Subports []SubportOptsApplyConfiguration `json:"subports,omitempty"` + CommonPortOptsApplyConfiguration `json:",inline"` } // PortOptsApplyConfiguration constructs a declarative configuration of the PortOpts type for use with @@ -37,6 +32,27 @@ func PortOpts() *PortOptsApplyConfiguration { return &PortOptsApplyConfiguration{} } +// WithTrunk sets the Trunk field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Trunk field is set to the value of the last call. +func (b *PortOptsApplyConfiguration) WithTrunk(value bool) *PortOptsApplyConfiguration { + b.Trunk = &value + return b +} + +// WithSubports adds the given value to the Subports field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Subports field. +func (b *PortOptsApplyConfiguration) WithSubports(values ...*SubportOptsApplyConfiguration) *PortOptsApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithSubports") + } + b.Subports = append(b.Subports, *values[i]) + } + return b +} + // WithNetwork sets the Network field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Network field is set to the value of the last call. @@ -97,14 +113,6 @@ func (b *PortOptsApplyConfiguration) WithTags(values ...string) *PortOptsApplyCo return b } -// WithTrunk sets the Trunk field in the declarative configuration to the given value -// and returns the receiver, so that objects can be built by chaining "With" function invocations. -// If called multiple times, the Trunk field is set to the value of the last call. -func (b *PortOptsApplyConfiguration) WithTrunk(value bool) *PortOptsApplyConfiguration { - b.Trunk = &value - return b -} - // WithAdminStateUp sets the AdminStateUp field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the AdminStateUp field is set to the value of the last call. diff --git a/pkg/generated/applyconfiguration/api/v1beta1/portstatus.go b/pkg/generated/applyconfiguration/api/v1beta1/portstatus.go index 847b2a26a..f2c4954ea 100644 --- a/pkg/generated/applyconfiguration/api/v1beta1/portstatus.go +++ b/pkg/generated/applyconfiguration/api/v1beta1/portstatus.go @@ -21,7 +21,8 @@ package v1beta1 // PortStatusApplyConfiguration represents a declarative configuration of the PortStatus type for use // with apply. type PortStatusApplyConfiguration struct { - ID *string `json:"id,omitempty"` + ID *string `json:"id,omitempty"` + Subports []SubPortStatusApplyConfiguration `json:"subports,omitempty"` } // PortStatusApplyConfiguration constructs a declarative configuration of the PortStatus type for use with @@ -37,3 +38,16 @@ func (b *PortStatusApplyConfiguration) WithID(value string) *PortStatusApplyConf b.ID = &value return b } + +// WithSubports adds the given value to the Subports field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Subports field. +func (b *PortStatusApplyConfiguration) WithSubports(values ...*SubPortStatusApplyConfiguration) *PortStatusApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithSubports") + } + b.Subports = append(b.Subports, *values[i]) + } + return b +} diff --git a/pkg/generated/applyconfiguration/api/v1beta1/resolvedportspec.go b/pkg/generated/applyconfiguration/api/v1beta1/resolvedportspec.go index fb84a3123..b7b919443 100644 --- a/pkg/generated/applyconfiguration/api/v1beta1/resolvedportspec.go +++ b/pkg/generated/applyconfiguration/api/v1beta1/resolvedportspec.go @@ -21,14 +21,9 @@ package v1beta1 // ResolvedPortSpecApplyConfiguration represents a declarative configuration of the ResolvedPortSpec type for use // with apply. type ResolvedPortSpecApplyConfiguration struct { - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - NetworkID *string `json:"networkID,omitempty"` - Tags []string `json:"tags,omitempty"` - Trunk *bool `json:"trunk,omitempty"` - FixedIPs []ResolvedFixedIPApplyConfiguration `json:"fixedIPs,omitempty"` - SecurityGroups []string `json:"securityGroups,omitempty"` - ResolvedPortSpecFieldsApplyConfiguration `json:",inline"` + Trunk *bool `json:"trunk,omitempty"` + Subports []ResolvedSubportSpecApplyConfiguration `json:"subports,omitempty"` + CommonResolvedPortSpecApplyConfiguration `json:",inline"` } // ResolvedPortSpecApplyConfiguration constructs a declarative configuration of the ResolvedPortSpec type for use with @@ -37,6 +32,27 @@ func ResolvedPortSpec() *ResolvedPortSpecApplyConfiguration { return &ResolvedPortSpecApplyConfiguration{} } +// WithTrunk sets the Trunk field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Trunk field is set to the value of the last call. +func (b *ResolvedPortSpecApplyConfiguration) WithTrunk(value bool) *ResolvedPortSpecApplyConfiguration { + b.Trunk = &value + return b +} + +// WithSubports adds the given value to the Subports field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Subports field. +func (b *ResolvedPortSpecApplyConfiguration) WithSubports(values ...*ResolvedSubportSpecApplyConfiguration) *ResolvedPortSpecApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithSubports") + } + b.Subports = append(b.Subports, *values[i]) + } + return b +} + // WithName sets the Name field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Name field is set to the value of the last call. @@ -71,14 +87,6 @@ func (b *ResolvedPortSpecApplyConfiguration) WithTags(values ...string) *Resolve return b } -// WithTrunk sets the Trunk field in the declarative configuration to the given value -// and returns the receiver, so that objects can be built by chaining "With" function invocations. -// If called multiple times, the Trunk field is set to the value of the last call. -func (b *ResolvedPortSpecApplyConfiguration) WithTrunk(value bool) *ResolvedPortSpecApplyConfiguration { - b.Trunk = &value - return b -} - // WithFixedIPs adds the given value to the FixedIPs field in the declarative configuration // and returns the receiver, so that objects can be build by chaining "With" function invocations. // If called multiple times, values provided by each call will be appended to the FixedIPs field. diff --git a/pkg/generated/applyconfiguration/api/v1beta1/resolvedsubportspec.go b/pkg/generated/applyconfiguration/api/v1beta1/resolvedsubportspec.go new file mode 100644 index 000000000..559f793e6 --- /dev/null +++ b/pkg/generated/applyconfiguration/api/v1beta1/resolvedsubportspec.go @@ -0,0 +1,188 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1beta1 + +// ResolvedSubportSpecApplyConfiguration represents a declarative configuration of the ResolvedSubportSpec type for use +// with apply. +type ResolvedSubportSpecApplyConfiguration struct { + SegmentationID *int `json:"segmentationID,omitempty"` + SegmentationType *string `json:"segmentationType,omitempty"` + CommonResolvedPortSpecApplyConfiguration `json:",inline"` +} + +// ResolvedSubportSpecApplyConfiguration constructs a declarative configuration of the ResolvedSubportSpec type for use with +// apply. +func ResolvedSubportSpec() *ResolvedSubportSpecApplyConfiguration { + return &ResolvedSubportSpecApplyConfiguration{} +} + +// WithSegmentationID sets the SegmentationID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SegmentationID field is set to the value of the last call. +func (b *ResolvedSubportSpecApplyConfiguration) WithSegmentationID(value int) *ResolvedSubportSpecApplyConfiguration { + b.SegmentationID = &value + return b +} + +// WithSegmentationType sets the SegmentationType field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SegmentationType field is set to the value of the last call. +func (b *ResolvedSubportSpecApplyConfiguration) WithSegmentationType(value string) *ResolvedSubportSpecApplyConfiguration { + b.SegmentationType = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *ResolvedSubportSpecApplyConfiguration) WithName(value string) *ResolvedSubportSpecApplyConfiguration { + b.Name = &value + return b +} + +// WithDescription sets the Description field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Description field is set to the value of the last call. +func (b *ResolvedSubportSpecApplyConfiguration) WithDescription(value string) *ResolvedSubportSpecApplyConfiguration { + b.Description = &value + return b +} + +// WithNetworkID sets the NetworkID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the NetworkID field is set to the value of the last call. +func (b *ResolvedSubportSpecApplyConfiguration) WithNetworkID(value string) *ResolvedSubportSpecApplyConfiguration { + b.NetworkID = &value + return b +} + +// WithTags adds the given value to the Tags field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Tags field. +func (b *ResolvedSubportSpecApplyConfiguration) WithTags(values ...string) *ResolvedSubportSpecApplyConfiguration { + for i := range values { + b.Tags = append(b.Tags, values[i]) + } + return b +} + +// WithFixedIPs adds the given value to the FixedIPs field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the FixedIPs field. +func (b *ResolvedSubportSpecApplyConfiguration) WithFixedIPs(values ...*ResolvedFixedIPApplyConfiguration) *ResolvedSubportSpecApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithFixedIPs") + } + b.FixedIPs = append(b.FixedIPs, *values[i]) + } + return b +} + +// WithSecurityGroups adds the given value to the SecurityGroups field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the SecurityGroups field. +func (b *ResolvedSubportSpecApplyConfiguration) WithSecurityGroups(values ...string) *ResolvedSubportSpecApplyConfiguration { + for i := range values { + b.SecurityGroups = append(b.SecurityGroups, values[i]) + } + return b +} + +// WithAdminStateUp sets the AdminStateUp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the AdminStateUp field is set to the value of the last call. +func (b *ResolvedSubportSpecApplyConfiguration) WithAdminStateUp(value bool) *ResolvedSubportSpecApplyConfiguration { + b.AdminStateUp = &value + return b +} + +// WithMACAddress sets the MACAddress field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the MACAddress field is set to the value of the last call. +func (b *ResolvedSubportSpecApplyConfiguration) WithMACAddress(value string) *ResolvedSubportSpecApplyConfiguration { + b.MACAddress = &value + return b +} + +// WithAllowedAddressPairs adds the given value to the AllowedAddressPairs field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the AllowedAddressPairs field. +func (b *ResolvedSubportSpecApplyConfiguration) WithAllowedAddressPairs(values ...*AddressPairApplyConfiguration) *ResolvedSubportSpecApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithAllowedAddressPairs") + } + b.AllowedAddressPairs = append(b.AllowedAddressPairs, *values[i]) + } + return b +} + +// WithHostID sets the HostID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the HostID field is set to the value of the last call. +func (b *ResolvedSubportSpecApplyConfiguration) WithHostID(value string) *ResolvedSubportSpecApplyConfiguration { + b.HostID = &value + return b +} + +// WithVNICType sets the VNICType field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the VNICType field is set to the value of the last call. +func (b *ResolvedSubportSpecApplyConfiguration) WithVNICType(value string) *ResolvedSubportSpecApplyConfiguration { + b.VNICType = &value + return b +} + +// WithProfile sets the Profile field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Profile field is set to the value of the last call. +func (b *ResolvedSubportSpecApplyConfiguration) WithProfile(value *BindingProfileApplyConfiguration) *ResolvedSubportSpecApplyConfiguration { + b.Profile = value + return b +} + +// WithDisablePortSecurity sets the DisablePortSecurity field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DisablePortSecurity field is set to the value of the last call. +func (b *ResolvedSubportSpecApplyConfiguration) WithDisablePortSecurity(value bool) *ResolvedSubportSpecApplyConfiguration { + b.DisablePortSecurity = &value + return b +} + +// WithPropagateUplinkStatus sets the PropagateUplinkStatus field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PropagateUplinkStatus field is set to the value of the last call. +func (b *ResolvedSubportSpecApplyConfiguration) WithPropagateUplinkStatus(value bool) *ResolvedSubportSpecApplyConfiguration { + b.PropagateUplinkStatus = &value + return b +} + +// WithValueSpecs adds the given value to the ValueSpecs field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the ValueSpecs field. +func (b *ResolvedSubportSpecApplyConfiguration) WithValueSpecs(values ...*ValueSpecApplyConfiguration) *ResolvedSubportSpecApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithValueSpecs") + } + b.ValueSpecs = append(b.ValueSpecs, *values[i]) + } + return b +} diff --git a/pkg/generated/applyconfiguration/api/v1beta1/subportopts.go b/pkg/generated/applyconfiguration/api/v1beta1/subportopts.go new file mode 100644 index 000000000..646903072 --- /dev/null +++ b/pkg/generated/applyconfiguration/api/v1beta1/subportopts.go @@ -0,0 +1,191 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1beta1 + +// SubportOptsApplyConfiguration represents a declarative configuration of the SubportOpts type for use +// with apply. +type SubportOptsApplyConfiguration struct { + SegmentationID *int `json:"segmentationID,omitempty"` + SegmentationType *string `json:"segmentationType,omitempty"` + CommonPortOptsApplyConfiguration `json:",inline"` +} + +// SubportOptsApplyConfiguration constructs a declarative configuration of the SubportOpts type for use with +// apply. +func SubportOpts() *SubportOptsApplyConfiguration { + return &SubportOptsApplyConfiguration{} +} + +// WithSegmentationID sets the SegmentationID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SegmentationID field is set to the value of the last call. +func (b *SubportOptsApplyConfiguration) WithSegmentationID(value int) *SubportOptsApplyConfiguration { + b.SegmentationID = &value + return b +} + +// WithSegmentationType sets the SegmentationType field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SegmentationType field is set to the value of the last call. +func (b *SubportOptsApplyConfiguration) WithSegmentationType(value string) *SubportOptsApplyConfiguration { + b.SegmentationType = &value + return b +} + +// WithNetwork sets the Network field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Network field is set to the value of the last call. +func (b *SubportOptsApplyConfiguration) WithNetwork(value *NetworkParamApplyConfiguration) *SubportOptsApplyConfiguration { + b.Network = value + return b +} + +// WithDescription sets the Description field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Description field is set to the value of the last call. +func (b *SubportOptsApplyConfiguration) WithDescription(value string) *SubportOptsApplyConfiguration { + b.Description = &value + return b +} + +// WithNameSuffix sets the NameSuffix field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the NameSuffix field is set to the value of the last call. +func (b *SubportOptsApplyConfiguration) WithNameSuffix(value string) *SubportOptsApplyConfiguration { + b.NameSuffix = &value + return b +} + +// WithFixedIPs adds the given value to the FixedIPs field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the FixedIPs field. +func (b *SubportOptsApplyConfiguration) WithFixedIPs(values ...*FixedIPApplyConfiguration) *SubportOptsApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithFixedIPs") + } + b.FixedIPs = append(b.FixedIPs, *values[i]) + } + return b +} + +// WithSecurityGroups adds the given value to the SecurityGroups field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the SecurityGroups field. +func (b *SubportOptsApplyConfiguration) WithSecurityGroups(values ...*SecurityGroupParamApplyConfiguration) *SubportOptsApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithSecurityGroups") + } + b.SecurityGroups = append(b.SecurityGroups, *values[i]) + } + return b +} + +// WithTags adds the given value to the Tags field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Tags field. +func (b *SubportOptsApplyConfiguration) WithTags(values ...string) *SubportOptsApplyConfiguration { + for i := range values { + b.Tags = append(b.Tags, values[i]) + } + return b +} + +// WithAdminStateUp sets the AdminStateUp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the AdminStateUp field is set to the value of the last call. +func (b *SubportOptsApplyConfiguration) WithAdminStateUp(value bool) *SubportOptsApplyConfiguration { + b.AdminStateUp = &value + return b +} + +// WithMACAddress sets the MACAddress field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the MACAddress field is set to the value of the last call. +func (b *SubportOptsApplyConfiguration) WithMACAddress(value string) *SubportOptsApplyConfiguration { + b.MACAddress = &value + return b +} + +// WithAllowedAddressPairs adds the given value to the AllowedAddressPairs field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the AllowedAddressPairs field. +func (b *SubportOptsApplyConfiguration) WithAllowedAddressPairs(values ...*AddressPairApplyConfiguration) *SubportOptsApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithAllowedAddressPairs") + } + b.AllowedAddressPairs = append(b.AllowedAddressPairs, *values[i]) + } + return b +} + +// WithHostID sets the HostID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the HostID field is set to the value of the last call. +func (b *SubportOptsApplyConfiguration) WithHostID(value string) *SubportOptsApplyConfiguration { + b.HostID = &value + return b +} + +// WithVNICType sets the VNICType field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the VNICType field is set to the value of the last call. +func (b *SubportOptsApplyConfiguration) WithVNICType(value string) *SubportOptsApplyConfiguration { + b.VNICType = &value + return b +} + +// WithProfile sets the Profile field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Profile field is set to the value of the last call. +func (b *SubportOptsApplyConfiguration) WithProfile(value *BindingProfileApplyConfiguration) *SubportOptsApplyConfiguration { + b.Profile = value + return b +} + +// WithDisablePortSecurity sets the DisablePortSecurity field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DisablePortSecurity field is set to the value of the last call. +func (b *SubportOptsApplyConfiguration) WithDisablePortSecurity(value bool) *SubportOptsApplyConfiguration { + b.DisablePortSecurity = &value + return b +} + +// WithPropagateUplinkStatus sets the PropagateUplinkStatus field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PropagateUplinkStatus field is set to the value of the last call. +func (b *SubportOptsApplyConfiguration) WithPropagateUplinkStatus(value bool) *SubportOptsApplyConfiguration { + b.PropagateUplinkStatus = &value + return b +} + +// WithValueSpecs adds the given value to the ValueSpecs field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the ValueSpecs field. +func (b *SubportOptsApplyConfiguration) WithValueSpecs(values ...*ValueSpecApplyConfiguration) *SubportOptsApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithValueSpecs") + } + b.ValueSpecs = append(b.ValueSpecs, *values[i]) + } + return b +} diff --git a/pkg/generated/applyconfiguration/api/v1beta1/subportstatus.go b/pkg/generated/applyconfiguration/api/v1beta1/subportstatus.go new file mode 100644 index 000000000..bd0dc479d --- /dev/null +++ b/pkg/generated/applyconfiguration/api/v1beta1/subportstatus.go @@ -0,0 +1,39 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1beta1 + +// SubPortStatusApplyConfiguration represents a declarative configuration of the SubPortStatus type for use +// with apply. +type SubPortStatusApplyConfiguration struct { + ID *string `json:"id,omitempty"` +} + +// SubPortStatusApplyConfiguration constructs a declarative configuration of the SubPortStatus type for use with +// apply. +func SubPortStatus() *SubPortStatusApplyConfiguration { + return &SubPortStatusApplyConfiguration{} +} + +// WithID sets the ID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ID field is set to the value of the last call. +func (b *SubPortStatusApplyConfiguration) WithID(value string) *SubPortStatusApplyConfiguration { + b.ID = &value + return b +} diff --git a/pkg/generated/applyconfiguration/internal/internal.go b/pkg/generated/applyconfiguration/internal/internal.go index 6f50cce73..03d51f7e8 100644 --- a/pkg/generated/applyconfiguration/internal/internal.go +++ b/pkg/generated/applyconfiguration/internal/internal.go @@ -1998,6 +1998,12 @@ var schemaYAML = typed.YAMLObject(`types: elementType: namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.SecurityGroupParam elementRelationship: atomic + - name: subports + type: + list: + elementType: + namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.SubportOpts + elementRelationship: atomic - name: tags type: list: @@ -2025,6 +2031,12 @@ var schemaYAML = typed.YAMLObject(`types: type: scalar: string default: "" + - name: subports + type: + list: + elementType: + namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.SubPortStatus + elementRelationship: atomic - name: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.ResolvedFixedIP map: fields: @@ -2103,6 +2115,12 @@ var schemaYAML = typed.YAMLObject(`types: elementType: scalar: string elementRelationship: atomic + - name: subports + type: + list: + elementType: + namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.ResolvedSubportSpec + elementRelationship: atomic - name: tags type: list: @@ -2123,6 +2141,82 @@ var schemaYAML = typed.YAMLObject(`types: - name: vnicType type: scalar: string +- name: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.ResolvedSubportSpec + map: + fields: + - name: adminStateUp + type: + scalar: boolean + - name: allowedAddressPairs + type: + list: + elementType: + namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.AddressPair + elementRelationship: atomic + - name: description + type: + scalar: string + default: "" + - name: disablePortSecurity + type: + scalar: boolean + - name: fixedIPs + type: + list: + elementType: + namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.ResolvedFixedIP + elementRelationship: atomic + - name: hostID + type: + scalar: string + - name: macAddress + type: + scalar: string + - name: name + type: + scalar: string + default: "" + - name: networkID + type: + scalar: string + default: "" + - name: profile + type: + namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.BindingProfile + - name: propagateUplinkStatus + type: + scalar: boolean + - name: securityGroups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: segmentationID + type: + scalar: numeric + default: 0 + - name: segmentationType + type: + scalar: string + default: "" + - name: tags + type: + list: + elementType: + scalar: string + elementRelationship: associative + - name: valueSpecs + type: + list: + elementType: + namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.ValueSpec + elementRelationship: associative + keys: + - name + - name: vnicType + type: + scalar: string - name: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.ResourceReference map: fields: @@ -2358,6 +2452,13 @@ var schemaYAML = typed.YAMLObject(`types: type: scalar: string default: "" +- name: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.SubPortStatus + map: + fields: + - name: id + type: + scalar: string + default: "" - name: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.Subnet map: fields: @@ -2458,6 +2559,79 @@ var schemaYAML = typed.YAMLObject(`types: elementType: scalar: string elementRelationship: atomic +- name: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.SubportOpts + map: + fields: + - name: adminStateUp + type: + scalar: boolean + - name: allowedAddressPairs + type: + list: + elementType: + namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.AddressPair + elementRelationship: atomic + - name: description + type: + scalar: string + - name: disablePortSecurity + type: + scalar: boolean + - name: fixedIPs + type: + list: + elementType: + namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.FixedIP + elementRelationship: atomic + - name: hostID + type: + scalar: string + - name: macAddress + type: + scalar: string + - name: nameSuffix + type: + scalar: string + - name: network + type: + namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.NetworkParam + - name: profile + type: + namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.BindingProfile + - name: propagateUplinkStatus + type: + scalar: boolean + - name: securityGroups + type: + list: + elementType: + namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.SecurityGroupParam + elementRelationship: atomic + - name: segmentationID + type: + scalar: numeric + default: 0 + - name: segmentationType + type: + scalar: string + default: "" + - name: tags + type: + list: + elementType: + scalar: string + elementRelationship: associative + - name: valueSpecs + type: + list: + elementType: + namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.ValueSpec + elementRelationship: associative + keys: + - name + - name: vnicType + type: + scalar: string - name: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.ValueSpec map: fields: diff --git a/pkg/generated/applyconfiguration/utils.go b/pkg/generated/applyconfiguration/utils.go index 1be3d3403..53f6a8ce3 100644 --- a/pkg/generated/applyconfiguration/utils.go +++ b/pkg/generated/applyconfiguration/utils.go @@ -142,6 +142,10 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &apiv1beta1.BlockDeviceStorageApplyConfiguration{} case v1beta1.SchemeGroupVersion.WithKind("BlockDeviceVolume"): return &apiv1beta1.BlockDeviceVolumeApplyConfiguration{} + case v1beta1.SchemeGroupVersion.WithKind("CommonPortOpts"): + return &apiv1beta1.CommonPortOptsApplyConfiguration{} + case v1beta1.SchemeGroupVersion.WithKind("CommonResolvedPortSpec"): + return &apiv1beta1.CommonResolvedPortSpecApplyConfiguration{} case v1beta1.SchemeGroupVersion.WithKind("ExternalRouterIPParam"): return &apiv1beta1.ExternalRouterIPParamApplyConfiguration{} case v1beta1.SchemeGroupVersion.WithKind("FilterByNeutronTags"): @@ -204,6 +208,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &apiv1beta1.ResolvedPortSpecApplyConfiguration{} case v1beta1.SchemeGroupVersion.WithKind("ResolvedPortSpecFields"): return &apiv1beta1.ResolvedPortSpecFieldsApplyConfiguration{} + case v1beta1.SchemeGroupVersion.WithKind("ResolvedSubportSpec"): + return &apiv1beta1.ResolvedSubportSpecApplyConfiguration{} case v1beta1.SchemeGroupVersion.WithKind("ResourceReference"): return &apiv1beta1.ResourceReferenceApplyConfiguration{} case v1beta1.SchemeGroupVersion.WithKind("RootVolume"): @@ -240,6 +246,10 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &apiv1beta1.SubnetParamApplyConfiguration{} case v1beta1.SchemeGroupVersion.WithKind("SubnetSpec"): return &apiv1beta1.SubnetSpecApplyConfiguration{} + case v1beta1.SchemeGroupVersion.WithKind("SubportOpts"): + return &apiv1beta1.SubportOptsApplyConfiguration{} + case v1beta1.SchemeGroupVersion.WithKind("SubPortStatus"): + return &apiv1beta1.SubPortStatusApplyConfiguration{} case v1beta1.SchemeGroupVersion.WithKind("ValueSpec"): return &apiv1beta1.ValueSpecApplyConfiguration{} case v1beta1.SchemeGroupVersion.WithKind("VolumeAvailabilityZone"): diff --git a/pkg/webhooks/openstackmachine_webhook.go b/pkg/webhooks/openstackmachine_webhook.go index 601fef647..22ef1aa33 100644 --- a/pkg/webhooks/openstackmachine_webhook.go +++ b/pkg/webhooks/openstackmachine_webhook.go @@ -63,9 +63,30 @@ func (*openStackMachineWebhook) ValidateCreate(_ context.Context, objRaw runtime } } - for _, port := range newObj.Spec.Ports { + for i, port := range newObj.Spec.Ports { + portPath := field.NewPath("spec", "ports").Index(i) + if ptr.Deref(port.DisablePortSecurity, false) && len(port.SecurityGroups) > 0 { - allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "ports"), "cannot have security groups when DisablePortSecurity is set to true")) + allErrs = append(allErrs, field.Forbidden(portPath, "cannot have security groups when DisablePortSecurity is set to true")) + } + + // Validate subports + if len(port.Subports) > 0 { + if !ptr.Deref(port.Trunk, false) { + allErrs = append(allErrs, field.Forbidden(portPath.Child("subports"), "subports can only be specified when trunk is enabled")) + } + + for j, subport := range port.Subports { + subportPath := portPath.Child("subports").Index(j) + + if subport.SegmentationID == 0 { + allErrs = append(allErrs, field.Invalid(subportPath.Child("segmentationID"), subport.SegmentationID, "segmentationID must be greater than 0")) + } + + if subport.SegmentationType == "" { + allErrs = append(allErrs, field.Required(subportPath.Child("segmentationType"), "segmentationType must be specified")) + } + } } } diff --git a/test/e2e/data/kustomize/multi-network/patch-machine-template-networks.yaml b/test/e2e/data/kustomize/multi-network/patch-machine-template-networks.yaml index 4ad2116cf..e03869de8 100644 --- a/test/e2e/data/kustomize/multi-network/patch-machine-template-networks.yaml +++ b/test/e2e/data/kustomize/multi-network/patch-machine-template-networks.yaml @@ -9,3 +9,13 @@ - description: "Extra Network 2" network: id: "${CLUSTER_EXTRA_NET_2}" + - description: "Trunk Network" + network: + id: "${CLUSTER_TRUNK_NET}" + trunk: true + subports: + - description: "Subport network" + segmentationID: 300 + segmentationType: vlan + network: + id: "${CLUSTER_SUBPORT_NET}" diff --git a/test/e2e/suites/apivalidations/filters_test.go b/test/e2e/suites/apivalidations/filters_test.go index 9814b5f84..66ffc4085 100644 --- a/test/e2e/suites/apivalidations/filters_test.go +++ b/test/e2e/suites/apivalidations/filters_test.go @@ -114,23 +114,39 @@ var _ = Describe("Filter API validations", func() { { machine := machine.DeepCopy() machine.Spec.Ports = []infrav1.PortOpts{ - {Network: &infrav1.NetworkParam{Filter: &infrav1.NetworkFilter{FilterByNeutronTags: tags[i]}}}, + { + CommonPortOpts: infrav1.CommonPortOpts{ + Network: &infrav1.NetworkParam{Filter: &infrav1.NetworkFilter{FilterByNeutronTags: tags[i]}}, + }, + }, } Expect(k8sClient.Create(ctx, machine)).NotTo(Succeed(), "OpenStackMachine creation should fail with invalid port network neutron tags") } { machine := machine.DeepCopy() machine.Spec.Ports = []infrav1.PortOpts{ - {FixedIPs: []infrav1.FixedIP{{Subnet: &infrav1.SubnetParam{ - Filter: &infrav1.SubnetFilter{FilterByNeutronTags: tags[i]}, - }}}}, + { + CommonPortOpts: infrav1.CommonPortOpts{ + FixedIPs: []infrav1.FixedIP{ + { + Subnet: &infrav1.SubnetParam{ + Filter: &infrav1.SubnetFilter{FilterByNeutronTags: tags[i]}, + }, + }, + }, + }, + }, } Expect(k8sClient.Create(ctx, machine)).NotTo(Succeed(), "OpenStackMachine creation should fail with invalid port subnet neutron tags") } { machine := machine.DeepCopy() machine.Spec.Ports = []infrav1.PortOpts{ - {SecurityGroups: []infrav1.SecurityGroupParam{{Filter: &infrav1.SecurityGroupFilter{FilterByNeutronTags: tags[i]}}}}, + { + CommonPortOpts: infrav1.CommonPortOpts{ + SecurityGroups: []infrav1.SecurityGroupParam{{Filter: &infrav1.SecurityGroupFilter{FilterByNeutronTags: tags[i]}}}, + }, + }, } Expect(k8sClient.Create(ctx, machine)).NotTo(Succeed(), "OpenStackMachine creation should fail with invalid port security group neutron tags") } diff --git a/test/e2e/suites/apivalidations/openstackmachine_test.go b/test/e2e/suites/apivalidations/openstackmachine_test.go index 28af1cf80..5f0ee5a87 100644 --- a/test/e2e/suites/apivalidations/openstackmachine_test.go +++ b/test/e2e/suites/apivalidations/openstackmachine_test.go @@ -215,11 +215,13 @@ var _ = Describe("OpenStackMachine API validations", func() { machine := defaultMachine() machine.Spec.Ports = []infrav1.PortOpts{ { - SecurityGroups: []infrav1.SecurityGroupParam{{ - Filter: &infrav1.SecurityGroupFilter{Name: "test-security-group"}, - }}, - ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{ - DisablePortSecurity: ptr.To(true), + CommonPortOpts: infrav1.CommonPortOpts{ + SecurityGroups: []infrav1.SecurityGroupParam{{ + Filter: &infrav1.SecurityGroupFilter{Name: "test-security-group"}, + }}, + ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{ + DisablePortSecurity: ptr.To(true), + }, }, }, } diff --git a/test/e2e/suites/apivalidations/openstackserver_test.go b/test/e2e/suites/apivalidations/openstackserver_test.go index ccc72f8cb..b3bb30ded 100644 --- a/test/e2e/suites/apivalidations/openstackserver_test.go +++ b/test/e2e/suites/apivalidations/openstackserver_test.go @@ -43,9 +43,11 @@ var _ = Describe("OpenStackServer API validations", func() { Image: infrav1.ImageParam{Filter: &infrav1.ImageFilter{Name: ptr.To("test-image")}}, Ports: []infrav1.PortOpts{ { - Network: &infrav1.NetworkParam{ - Filter: &infrav1.NetworkFilter{ - Name: "test-network", + CommonPortOpts: infrav1.CommonPortOpts{ + Network: &infrav1.NetworkParam{ + Filter: &infrav1.NetworkFilter{ + Name: "test-network", + }, }, }, }, @@ -168,11 +170,13 @@ var _ = Describe("OpenStackServer API validations", func() { server := defaultServer() server.Spec.Ports = []infrav1.PortOpts{ { - SecurityGroups: []infrav1.SecurityGroupParam{{ - Filter: &infrav1.SecurityGroupFilter{Name: "test-security-group"}, - }}, - ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{ - DisablePortSecurity: ptr.To(true), + CommonPortOpts: infrav1.CommonPortOpts{ + SecurityGroups: []infrav1.SecurityGroupParam{{ + Filter: &infrav1.SecurityGroupFilter{Name: "test-security-group"}, + }}, + ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{ + DisablePortSecurity: ptr.To(true), + }, }, }, } diff --git a/test/e2e/suites/e2e/e2e_test.go b/test/e2e/suites/e2e/e2e_test.go index a444e89b0..92735577d 100644 --- a/test/e2e/suites/e2e/e2e_test.go +++ b/test/e2e/suites/e2e/e2e_test.go @@ -521,14 +521,20 @@ var _ = Describe("e2e tests [PR-Blocking]", func() { customPortOptions := &[]infrav1.PortOpts{ { - Description: ptr.To("primary"), + CommonPortOpts: infrav1.CommonPortOpts{ + Description: ptr.To("primary"), + }, }, { - Description: ptr.To("trunked"), - Trunk: ptr.To(true), + CommonPortOpts: infrav1.CommonPortOpts{ + Description: ptr.To("trunked"), + }, + Trunk: ptr.To(true), }, { - SecurityGroups: []infrav1.SecurityGroupParam{{Filter: &infrav1.SecurityGroupFilter{Name: testSecurityGroupName}}}, + CommonPortOpts: infrav1.CommonPortOpts{ + SecurityGroups: []infrav1.SecurityGroupParam{{Filter: &infrav1.SecurityGroupFilter{Name: testSecurityGroupName}}}, + }, }, } @@ -687,7 +693,7 @@ var _ = Describe("e2e tests [PR-Blocking]", func() { configCluster clusterctl.ConfigClusterInput md []*clusterv1.MachineDeployment - extraNet1, extraNet2 *networks.Network + extraNet1, extraNet2, trunkNet, subportNet *networks.Network ) BeforeEach(func(ctx context.Context) { @@ -715,8 +721,26 @@ var _ = Describe("e2e tests [PR-Blocking]", func() { Expect(err).NotTo(HaveOccurred()) }) + trunkNet, err = shared.CreateOpenStackNetwork(e2eCtx, fmt.Sprintf("%s-trunk", namespace.Name), "10.14.2.0/24") + Expect(err).NotTo(HaveOccurred()) + postClusterCleanup = append(postClusterCleanup, func(ctx context.Context) { + shared.Logf("Deleting trunk network %s", trunkNet.Name) + err := shared.DeleteOpenStackNetwork(ctx, e2eCtx, trunkNet.ID) + Expect(err).NotTo(HaveOccurred()) + }) + + subportNet, err = shared.CreateOpenStackNetwork(e2eCtx, fmt.Sprintf("%s-trunk", namespace.Name), "10.14.3.0/24") + Expect(err).NotTo(HaveOccurred()) + postClusterCleanup = append(postClusterCleanup, func(ctx context.Context) { + shared.Logf("Deleting subport network %s", subportNet.Name) + err := shared.DeleteOpenStackNetwork(ctx, e2eCtx, subportNet.ID) + Expect(err).NotTo(HaveOccurred()) + }) + os.Setenv("CLUSTER_EXTRA_NET_1", extraNet1.ID) os.Setenv("CLUSTER_EXTRA_NET_2", extraNet2.ID) + os.Setenv("CLUSTER_TRUNK_NET", trunkNet.ID) + os.Setenv("CLUSTER_SUBPORT_NET", subportNet.ID) shared.Logf("Creating a cluster") clusterName = fmt.Sprintf("cluster-%s", namespace.Name) @@ -755,24 +779,32 @@ var _ = Describe("e2e tests [PR-Blocking]", func() { openStackCluster.Status.Network.ID: "primary", extraNet1.ID: "Extra Network 1", extraNet2.ID: "Extra Network 2", + trunkNet.ID: "Trunk Network", } + providerClient, clientOpts, _, err := shared.GetTenantProviderClient(e2eCtx) + Expect(err).To(BeNil(), "Cannot create providerClient") + networkClient, err := openstack.NewNetworkV2(providerClient, gophercloud.EndpointOpts{ + Region: clientOpts.RegionName, + }) + Expect(err).To(BeNil(), "Cannot create network client") + for i := range allMachines { machine := &allMachines[i] shared.Logf("Checking ports for machine %s", machine.Name) instanceID := getInstanceIDForMachine(machine) shared.Logf("Fetching ports for instance %s", instanceID) - ports, err := shared.DumpOpenStackPorts(e2eCtx, ports.ListOpts{ + osPorts, err := shared.DumpOpenStackPorts(e2eCtx, ports.ListOpts{ DeviceID: instanceID, }) Expect(err).NotTo(HaveOccurred()) - Expect(ports).To(HaveLen(len(expectedPorts))) + Expect(osPorts).To(HaveLen(len(expectedPorts))) var seenNetworks []string var seenAddresses clusterv1.MachineAddresses - for j := range ports { - port := &ports[j] + for j := range osPorts { + port := &osPorts[j] // Check that the port has an expected network ID and description Expect(expectedPorts).To(HaveKeyWithValue(port.NetworkID, port.Description)) @@ -787,6 +819,24 @@ var _ = Describe("e2e tests [PR-Blocking]", func() { Address: port.FixedIPs[k].IPAddress, }) } + if port.NetworkID == trunkNet.ID { + var trunk *trunks.Trunk + Eventually(func() int { + trunk, err = shared.DumpOpenStackTrunks(e2eCtx, port.ID) + Expect(err).To(BeNil()) + Expect(trunk).NotTo(BeNil()) + return 1 + }, e2eCtx.E2EConfig.GetIntervals(specName, "wait-worker-nodes")...).Should(Equal(1)) + Expect(trunk.PortID).To(Equal(port.ID)) + + trunkSubports, err := trunks.GetSubports(ctx, networkClient, trunk.ID).Extract() + Expect(trunkSubports).To(HaveLen(1)) + + portInfo, err := shared.DumpOpenStackPorts(e2eCtx, ports.ListOpts{ID: trunkSubports[0].PortID}) + Expect(err).To(BeNil()) + Expect(portInfo).To(HaveLen(1)) + Expect(portInfo[0].NetworkID).To(Equal(subportNet.ID)) + } } // All IP addresses on all ports should be reported in Addresses