diff --git a/api/v1alpha4/types.go b/api/v1alpha4/types.go index db17a7a5e1..7130df23d2 100644 --- a/api/v1alpha4/types.go +++ b/api/v1alpha4/types.go @@ -130,6 +130,8 @@ type PortOpts struct { ProjectID string `json:"projectId,omitempty"` SecurityGroups *[]string `json:"securityGroups,omitempty"` AllowedAddressPairs []AddressPair `json:"allowedAddressPairs,omitempty"` + // Enables and disables trunk at port level. If not provided, openStackMachine.Spec.Trunk is inherited. + Trunk *bool `json:"trunk,omitempty"` // The ID of the host where the port is allocated HostID string `json:"hostId,omitempty"` diff --git a/api/v1alpha4/zz_generated.deepcopy.go b/api/v1alpha4/zz_generated.deepcopy.go index 1986e99329..2b45b72ce7 100644 --- a/api/v1alpha4/zz_generated.deepcopy.go +++ b/api/v1alpha4/zz_generated.deepcopy.go @@ -812,6 +812,11 @@ func (in *PortOpts) DeepCopyInto(out *PortOpts) { *out = make([]AddressPair, len(*in)) copy(*out, *in) } + if in.Trunk != nil { + in, out := &in.Trunk, &out.Trunk + *out = new(bool) + **out = **in + } if in.Profile != nil { in, out := &in.Profile, &out.Profile *out = make(map[string]string, len(*in)) 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 c8fa83aa2b..8d024dd3c5 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml @@ -1322,6 +1322,10 @@ spec: type: array tenantId: type: string + trunk: + description: Enables and disables trunk at port level. + If not provided, openStackMachine.Spec.Trunk is inherited. + type: boolean vnicType: description: The virtual network interface card (vNIC) type that is bound to the neutron port. @@ -1771,6 +1775,10 @@ spec: type: array tenantId: type: string + trunk: + description: Enables and disables trunk at port level. + If not provided, openStackMachine.Spec.Trunk is inherited. + type: boolean vnicType: description: The virtual network interface card (vNIC) type that is bound to the neutron port. @@ -2047,6 +2055,10 @@ spec: type: array tenantId: type: string + trunk: + description: Enables and disables trunk at port level. If + not provided, openStackMachine.Spec.Trunk is inherited. + type: boolean vnicType: description: The virtual network interface card (vNIC) type that is bound to the neutron port. @@ -2235,6 +2247,10 @@ spec: type: array tenantId: type: string + trunk: + description: Enables and disables trunk at port level. If + not provided, openStackMachine.Spec.Trunk is inherited. + type: boolean vnicType: description: The virtual network interface card (vNIC) type that is bound to the neutron port. 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 79af0fde6a..f7fbeac564 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml @@ -304,6 +304,11 @@ spec: type: array tenantId: type: string + trunk: + description: Enables and disables trunk at port + level. If not provided, openStackMachine.Spec.Trunk + is inherited. + type: boolean vnicType: description: The virtual network interface card (vNIC) type that is bound to the neutron port. 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 8e8569a448..90a7bd4805 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml @@ -611,6 +611,10 @@ spec: type: array tenantId: type: string + trunk: + description: Enables and disables trunk at port level. If not + provided, openStackMachine.Spec.Trunk is inherited. + type: boolean vnicType: description: The virtual network interface card (vNIC) type that is bound to the neutron port. 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 125477405a..f82d2dd9e3 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml @@ -561,6 +561,10 @@ spec: type: array tenantId: type: string + trunk: + description: Enables and disables trunk at port level. + If not provided, openStackMachine.Spec.Trunk is inherited. + type: boolean vnicType: description: The virtual network interface card (vNIC) type that is bound to the neutron port. diff --git a/pkg/cloud/services/compute/instance.go b/pkg/cloud/services/compute/instance.go index 05ee883db0..492a174c27 100644 --- a/pkg/cloud/services/compute/instance.go +++ b/pkg/cloud/services/compute/instance.go @@ -145,11 +145,16 @@ func (s *Service) constructNetworks(openStackCluster *infrav1.OpenStackCluster, } } for i, port := range openStackMachine.Spec.Ports { + pOpts := &openStackMachine.Spec.Ports[i] + // No Trunk field specified for the port, inherits openStackMachine.Spec.Trunk. + if pOpts.Trunk == nil { + pOpts.Trunk = &openStackMachine.Spec.Trunk + } if port.NetworkID != "" { nets = append(nets, infrav1.Network{ ID: port.NetworkID, Subnet: &infrav1.Subnet{}, - PortOpts: &openStackMachine.Spec.Ports[i], + PortOpts: pOpts, }) } else { nets = append(nets, infrav1.Network{ @@ -157,7 +162,7 @@ func (s *Service) constructNetworks(openStackCluster *infrav1.OpenStackCluster, Subnet: &infrav1.Subnet{ ID: openStackCluster.Status.Network.Subnet.ID, }, - PortOpts: &openStackMachine.Spec.Ports[i], + PortOpts: pOpts, }) } } @@ -168,6 +173,9 @@ func (s *Service) constructNetworks(openStackCluster *infrav1.OpenStackCluster, Subnet: &infrav1.Subnet{ ID: openStackCluster.Status.Network.Subnet.ID, }, + PortOpts: &infrav1.PortOpts{ + Trunk: &openStackMachine.Spec.Trunk, + }, }} } return nets, nil @@ -181,24 +189,16 @@ func (s *Service) createInstance(eventObject runtime.Object, clusterName string, if network.ID == "" { return nil, fmt.Errorf("no network was found or provided. Please check your machine configuration and try again") } - + iTags := []string{} + if len(instanceSpec.Tags) > 0 { + iTags = instanceSpec.Tags + } portName := getPortName(instanceSpec.Name, network.PortOpts, i) - port, err := s.getOrCreatePort(eventObject, clusterName, portName, network, instanceSpec.SecurityGroups) + port, err := s.getOrCreatePort(eventObject, clusterName, portName, network, &instanceSpec.SecurityGroups, iTags) if err != nil { return nil, err } - if instanceSpec.Trunk { - trunk, err := s.getOrCreateTrunk(eventObject, clusterName, instanceSpec.Name, port.ID) - if err != nil { - return nil, err - } - - if err = s.replaceAllAttributesTags(eventObject, trunk.ID, instanceSpec.Tags); err != nil { - return nil, err - } - } - for _, fip := range port.FixedIPs { if fip.SubnetID == instanceSpec.Subnet { accessIPv4 = fip.IPAddress @@ -420,7 +420,7 @@ func (s *Service) getServerNetworks(networkParams []infrav1.NetworkParam) ([]inf return nets, nil } -func (s *Service) getOrCreatePort(eventObject runtime.Object, clusterName string, portName string, net infrav1.Network, instanceSecurityGroups []string) (*ports.Port, error) { +func (s *Service) getOrCreatePort(eventObject runtime.Object, clusterName string, portName string, net infrav1.Network, instanceSecurityGroups *[]string, tags []string) (*ports.Port, error) { mc := metrics.NewMetricPrometheusContext("port", "list") allPages, err := ports.List(s.networkClient, ports.ListOpts{ Name: portName, @@ -467,7 +467,7 @@ func (s *Service) getOrCreatePort(eventObject runtime.Object, clusterName string // inherit port security groups from the instance if not explicitly specified if securityGroups == nil { - securityGroups = &instanceSecurityGroups + securityGroups = instanceSecurityGroups } } @@ -523,6 +523,19 @@ func (s *Service) getOrCreatePort(eventObject runtime.Object, clusterName string } record.Eventf(eventObject, "SuccessfulCreatePort", "Created port %s with id %s", port.Name, port.ID) + if portOpts.Trunk != nil && *portOpts.Trunk { + trunkDescription := names.GetDescription(clusterName) + trunk, err := s.getOrCreateTrunk(eventObject, trunkDescription, port.Name, port.ID) + if err != nil { + record.Warnf(eventObject, "FailedCreateTrunk", "Failed to create trunk for port %s: %v", portName, err) + return nil, err + } + if err = s.replaceAllAttributesTags(eventObject, trunk.ID, tags); err != nil { + record.Warnf(eventObject, "FailedReplaceTags", "Failed to replace trunk tags %s: %v", portName, err) + return nil, err + } + } + return port, nil } @@ -541,18 +554,18 @@ func getPortProfile(p map[string]string) map[string]interface{} { return portProfile } -func (s *Service) getOrCreateTrunk(eventObject runtime.Object, clusterName, trunkName, portID string) (*trunks.Trunk, error) { +func (s *Service) getOrCreateTrunk(eventObject runtime.Object, description, trunkName, portID string) (*trunks.Trunk, error) { mc := metrics.NewMetricPrometheusContext("trunk", "list") allPages, err := trunks.List(s.networkClient, trunks.ListOpts{ Name: trunkName, PortID: portID, }).AllPages() if mc.ObserveRequest(err) != nil { - return nil, fmt.Errorf("searching for existing trunk for server: %v", err) + return nil, fmt.Errorf("searching for existing trunk for port: %v", err) } trunkList, err := trunks.ExtractTrunks(allPages) if err != nil { - return nil, fmt.Errorf("searching for existing trunk for server: %v", err) + return nil, fmt.Errorf("searching for existing trunk for port: %v", err) } if len(trunkList) != 0 { @@ -562,7 +575,7 @@ func (s *Service) getOrCreateTrunk(eventObject runtime.Object, clusterName, trun trunkCreateOpts := trunks.CreateOpts{ Name: trunkName, PortID: portID, - Description: names.GetDescription(clusterName), + Description: description, } mc = metrics.NewMetricPrometheusContext("trunk", "create")