diff --git a/api/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml b/api/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml index 15496196..cc14f1e0 100644 --- a/api/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml +++ b/api/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml @@ -87,6 +87,21 @@ spec: to add additional files. Those get added to the service config dir in /etc/ . TODO: -> implement' type: object + lbMgmtNetwork: + default: + manageLbMgmtNetworks: true + subnetIpVersion: 4 + description: OctaviaLbMgmtNetworks Settings for Octavia management + networks + properties: + manageLbMgmtNetworks: + default: true + type: boolean + subnetIpVersion: + default: 4 + description: IP Version of the managed subnets + type: integer + type: object networkAttachments: description: NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network @@ -190,6 +205,11 @@ spec: description: 'ServiceUser - service user name (TODO: beagles, do we need this at all)' type: string + tenantName: + default: service + description: TenantName - the name of the OpenStack tenant that controls + the Octavia resources TODO(gthiemonge) same as ServiceAccount? + type: string transportURLSecret: description: TransportURLSecret - Secret containing RabbitMQ transportURL type: string diff --git a/api/bases/octavia.openstack.org_octavias.yaml b/api/bases/octavia.openstack.org_octavias.yaml index 919203ea..0ea7fa7e 100644 --- a/api/bases/octavia.openstack.org_octavias.yaml +++ b/api/bases/octavia.openstack.org_octavias.yaml @@ -85,6 +85,21 @@ spec: to add additional files. Those get added to the service config dir in /etc/ . TODO: -> implement' type: object + lbMgmtNetwork: + default: + manageLbMgmtNetworks: true + subnetIpVersion: 4 + description: OctaviaLbMgmtNetworks Settings for Octavia management + networks + properties: + manageLbMgmtNetworks: + default: true + type: boolean + subnetIpVersion: + default: 4 + description: IP Version of the managed subnets + type: integer + type: object nodeSelector: additionalProperties: type: string @@ -479,6 +494,21 @@ spec: also be used to add additional files. Those get added to the service config dir in /etc/ . TODO: -> implement' type: object + lbMgmtNetwork: + default: + manageLbMgmtNetworks: true + subnetIpVersion: 4 + description: OctaviaLbMgmtNetworks Settings for Octavia management + networks + properties: + manageLbMgmtNetworks: + default: true + type: boolean + subnetIpVersion: + default: 4 + description: IP Version of the managed subnets + type: integer + type: object networkAttachments: description: NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network @@ -583,6 +613,11 @@ spec: description: 'ServiceUser - service user name (TODO: beagles, do we need this at all)' type: string + tenantName: + default: service + description: TenantName - the name of the OpenStack tenant that + controls the Octavia resources TODO(gthiemonge) same as ServiceAccount? + type: string transportURLSecret: description: TransportURLSecret - Secret containing RabbitMQ transportURL type: string @@ -635,6 +670,21 @@ spec: also be used to add additional files. Those get added to the service config dir in /etc/ . TODO: -> implement' type: object + lbMgmtNetwork: + default: + manageLbMgmtNetworks: true + subnetIpVersion: 4 + description: OctaviaLbMgmtNetworks Settings for Octavia management + networks + properties: + manageLbMgmtNetworks: + default: true + type: boolean + subnetIpVersion: + default: 4 + description: IP Version of the managed subnets + type: integer + type: object networkAttachments: description: NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network @@ -739,6 +789,11 @@ spec: description: 'ServiceUser - service user name (TODO: beagles, do we need this at all)' type: string + tenantName: + default: service + description: TenantName - the name of the OpenStack tenant that + controls the Octavia resources TODO(gthiemonge) same as ServiceAccount? + type: string transportURLSecret: description: TransportURLSecret - Secret containing RabbitMQ transportURL type: string @@ -791,6 +846,21 @@ spec: also be used to add additional files. Those get added to the service config dir in /etc/ . TODO: -> implement' type: object + lbMgmtNetwork: + default: + manageLbMgmtNetworks: true + subnetIpVersion: 4 + description: OctaviaLbMgmtNetworks Settings for Octavia management + networks + properties: + manageLbMgmtNetworks: + default: true + type: boolean + subnetIpVersion: + default: 4 + description: IP Version of the managed subnets + type: integer + type: object networkAttachments: description: NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network @@ -895,6 +965,11 @@ spec: description: 'ServiceUser - service user name (TODO: beagles, do we need this at all)' type: string + tenantName: + default: service + description: TenantName - the name of the OpenStack tenant that + controls the Octavia resources TODO(gthiemonge) same as ServiceAccount? + type: string transportURLSecret: description: TransportURLSecret - Secret containing RabbitMQ transportURL type: string diff --git a/api/v1beta1/amphoracontroller_types.go b/api/v1beta1/amphoracontroller_types.go index 4e2e836c..b68dbcdf 100644 --- a/api/v1beta1/amphoracontroller_types.go +++ b/api/v1beta1/amphoracontroller_types.go @@ -17,7 +17,6 @@ limitations under the License. package v1beta1 import ( - "github.com/openstack-k8s-operators/lib-common/modules/common/condition" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -116,6 +115,16 @@ type OctaviaAmphoraControllerSpec struct { // +kubebuilder:validation:Optional // NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network NetworkAttachments []string `json:"networkAttachments,omitempty"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=service + // TenantName - the name of the OpenStack tenant that controls the Octavia resources + // TODO(gthiemonge) same as ServiceAccount? + TenantName string `json:"tenantName"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default={manageLbMgmtNetworks: true, subnetIpVersion: 4} + LbMgmtNetworks OctaviaLbMgmtNetworks `json:"lbMgmtNetwork"` } // OctaviaAmphoraControllerStatus defines the observed state of the Octavia Amphora Controller @@ -144,10 +153,10 @@ type OctaviaAmphoraControllerStatus struct { // OctaviaAmphoraController is the Schema for the octaviaworkers API type OctaviaAmphoraController struct { - metav1.TypeMeta `json:",inline"` + metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec OctaviaAmphoraControllerSpec `json:"spec,omitempty"` + Spec OctaviaAmphoraControllerSpec `json:"spec,omitempty"` Status OctaviaAmphoraControllerStatus `json:"status,omitempty"` } diff --git a/api/v1beta1/conditions.go b/api/v1beta1/conditions.go index 07c7fa44..c3dba159 100644 --- a/api/v1beta1/conditions.go +++ b/api/v1beta1/conditions.go @@ -37,36 +37,36 @@ const ( // // OctaviaAPIReady condition messages // - // OctaviaAPIReadyInitMessage - OctaviaAPIReadyInitMessage = "OctaviaAPI not started" - - // OctaviaAPIReadyErrorMessage - OctaviaAPIReadyErrorMessage = "OctaviaAPI error occured %s" - - // - // OctaviaHealthManagerReady condition messages - // - // OctaviaHealthManagerReadyInitMessage - OctaviaHealthManagerReadyInitMessage = "OctaviaHealthManager is not started" - - // OctaviaHealthManagerReadyErrorMessage - OctaviaHealthManagerReadyErrorMessage = "OctaviaHealthManager error occured %s" - - // - // OctaviaHousekeepingReady condition messages - // - // OctaviaHousekeepingReadyInitMessage - OctaviaHousekeepingReadyInitMessage = "OctaviaHousekeeping are not started" - - // OctaviaAmphoraControllerReadyErrorMessage - OctaviaHousekeepingReadyErrorMessage = "OctaviaHousekeeping error occured %s" - - // - // OctaviaWorkerReady condition messages - // - // OctaviaWorkerReadyInitMessage - OctaviaWorkerReadyInitMessage = "OctaviaWorker are not started" - - // OctaviaAmphoraControllerReadyErrorMessage - OctaviaWorkerReadyErrorMessage = "OctaviaWorker error occured %s" + // OctaviaAPIReadyInitMessage + OctaviaAPIReadyInitMessage = "OctaviaAPI not started" + + // OctaviaAPIReadyErrorMessage + OctaviaAPIReadyErrorMessage = "OctaviaAPI error occured %s" + + // + // OctaviaHealthManagerReady condition messages + // + // OctaviaHealthManagerReadyInitMessage + OctaviaHealthManagerReadyInitMessage = "OctaviaHealthManager is not started" + + // OctaviaHealthManagerReadyErrorMessage + OctaviaHealthManagerReadyErrorMessage = "OctaviaHealthManager error occured %s" + + // + // OctaviaHousekeepingReady condition messages + // + // OctaviaHousekeepingReadyInitMessage + OctaviaHousekeepingReadyInitMessage = "OctaviaHousekeeping are not started" + + // OctaviaAmphoraControllerReadyErrorMessage + OctaviaHousekeepingReadyErrorMessage = "OctaviaHousekeeping error occured %s" + + // + // OctaviaWorkerReady condition messages + // + // OctaviaWorkerReadyInitMessage + OctaviaWorkerReadyInitMessage = "OctaviaWorker are not started" + + // OctaviaAmphoraControllerReadyErrorMessage + OctaviaWorkerReadyErrorMessage = "OctaviaWorker error occured %s" ) diff --git a/api/v1beta1/octavia_types.go b/api/v1beta1/octavia_types.go index 7d29489e..9b5d9a0e 100644 --- a/api/v1beta1/octavia_types.go +++ b/api/v1beta1/octavia_types.go @@ -117,6 +117,10 @@ type OctaviaSpec struct { // +kubebuilder:validation:Optional // OctaviaHousekeeping - Spec definition for the Octavia Housekeeping agent for the Octavia deployment OctaviaWorker OctaviaAmphoraControllerSpec `json:"octaviaWorker"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default={manageLbMgmtNetworks: true, subnetIpVersion: 4} + LbMgmtNetworks OctaviaLbMgmtNetworks `json:"lbMgmtNetwork"` } // PasswordSelector to identify the DB and AdminUser password from the Secret @@ -132,6 +136,18 @@ type PasswordSelector struct { Service string `json:"service,omitempty"` } +// OctaviaLbMgmtNetworks Settings for Octavia management networks +type OctaviaLbMgmtNetworks struct { + // +kubebuilder:validation:Optional + // +kubebuilder:default=true + ManageLbMgmtNetworks bool `json:"manageLbMgmtNetworks,omitempty"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=4 + // IP Version of the managed subnets + SubnetIPVersion int `json:"subnetIpVersion,omitempty"` +} + // OctaviaStatus defines the observed state of Octavia type OctaviaStatus struct { @@ -199,16 +215,15 @@ func (instance Octavia) IsReady() bool { func SetupDefaults() { // Acquire environmental defaults and initialize Octavia defaults with them octaviaDefaults := OctaviaDefaults{ - APIContainerImageURL: util.GetEnvVar("RELATED_IMAGE_OCTAVIA_API_IMAGE_URL_DEFAULT", OctaviaAPIContainerImage), - HousekeepingContainerImageURL: util.GetEnvVar("RELATED_IMAGE_OCTAVIA_HOUSEKEEPING_IMAGE_URL_DEFAULT", OctaviaHousekeepingContainerImage), + APIContainerImageURL: util.GetEnvVar("RELATED_IMAGE_OCTAVIA_API_IMAGE_URL_DEFAULT", OctaviaAPIContainerImage), + HousekeepingContainerImageURL: util.GetEnvVar("RELATED_IMAGE_OCTAVIA_HOUSEKEEPING_IMAGE_URL_DEFAULT", OctaviaHousekeepingContainerImage), HealthManagerContainerImageURL: util.GetEnvVar("RELATED_IMAGE_OCTAVIA_HEALTHMANAGER_IMAGE_URL_DEFAULT", OctaviaHealthManagerContainerImage), - WorkerContainerImageURL: util.GetEnvVar("RELATED_IMAGE_OCTAVIA_WORKER_IMAGE_URL_DEFAULT", OctaviaWorkerContainerImage), + WorkerContainerImageURL: util.GetEnvVar("RELATED_IMAGE_OCTAVIA_WORKER_IMAGE_URL_DEFAULT", OctaviaWorkerContainerImage), } SetupOctaviaDefaults(octaviaDefaults) } - // RbacConditionsSet - set the conditions for the rbac object func (instance Octavia) RbacConditionsSet(c *condition.Condition) { instance.Status.Conditions.Set(c) diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 47956523..d474924c 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -325,6 +325,7 @@ func (in *OctaviaAmphoraControllerSpec) DeepCopyInto(out *OctaviaAmphoraControll *out = make([]string, len(*in)) copy(*out, *in) } + out.LbMgmtNetworks = in.LbMgmtNetworks } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OctaviaAmphoraControllerSpec. @@ -396,6 +397,21 @@ func (in *OctaviaDefaults) DeepCopy() *OctaviaDefaults { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OctaviaLbMgmtNetworks) DeepCopyInto(out *OctaviaLbMgmtNetworks) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OctaviaLbMgmtNetworks. +func (in *OctaviaLbMgmtNetworks) DeepCopy() *OctaviaLbMgmtNetworks { + if in == nil { + return nil + } + out := new(OctaviaLbMgmtNetworks) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OctaviaList) DeepCopyInto(out *OctaviaList) { *out = *in @@ -451,6 +467,7 @@ func (in *OctaviaSpec) DeepCopyInto(out *OctaviaSpec) { in.OctaviaHousekeeping.DeepCopyInto(&out.OctaviaHousekeeping) in.OctaviaHealthManager.DeepCopyInto(&out.OctaviaHealthManager) in.OctaviaWorker.DeepCopyInto(&out.OctaviaWorker) + out.LbMgmtNetworks = in.LbMgmtNetworks } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OctaviaSpec. diff --git a/config/crd/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml b/config/crd/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml index 15496196..cc14f1e0 100644 --- a/config/crd/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml +++ b/config/crd/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml @@ -87,6 +87,21 @@ spec: to add additional files. Those get added to the service config dir in /etc/ . TODO: -> implement' type: object + lbMgmtNetwork: + default: + manageLbMgmtNetworks: true + subnetIpVersion: 4 + description: OctaviaLbMgmtNetworks Settings for Octavia management + networks + properties: + manageLbMgmtNetworks: + default: true + type: boolean + subnetIpVersion: + default: 4 + description: IP Version of the managed subnets + type: integer + type: object networkAttachments: description: NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network @@ -190,6 +205,11 @@ spec: description: 'ServiceUser - service user name (TODO: beagles, do we need this at all)' type: string + tenantName: + default: service + description: TenantName - the name of the OpenStack tenant that controls + the Octavia resources TODO(gthiemonge) same as ServiceAccount? + type: string transportURLSecret: description: TransportURLSecret - Secret containing RabbitMQ transportURL type: string diff --git a/config/crd/bases/octavia.openstack.org_octavias.yaml b/config/crd/bases/octavia.openstack.org_octavias.yaml index 919203ea..0ea7fa7e 100644 --- a/config/crd/bases/octavia.openstack.org_octavias.yaml +++ b/config/crd/bases/octavia.openstack.org_octavias.yaml @@ -85,6 +85,21 @@ spec: to add additional files. Those get added to the service config dir in /etc/ . TODO: -> implement' type: object + lbMgmtNetwork: + default: + manageLbMgmtNetworks: true + subnetIpVersion: 4 + description: OctaviaLbMgmtNetworks Settings for Octavia management + networks + properties: + manageLbMgmtNetworks: + default: true + type: boolean + subnetIpVersion: + default: 4 + description: IP Version of the managed subnets + type: integer + type: object nodeSelector: additionalProperties: type: string @@ -479,6 +494,21 @@ spec: also be used to add additional files. Those get added to the service config dir in /etc/ . TODO: -> implement' type: object + lbMgmtNetwork: + default: + manageLbMgmtNetworks: true + subnetIpVersion: 4 + description: OctaviaLbMgmtNetworks Settings for Octavia management + networks + properties: + manageLbMgmtNetworks: + default: true + type: boolean + subnetIpVersion: + default: 4 + description: IP Version of the managed subnets + type: integer + type: object networkAttachments: description: NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network @@ -583,6 +613,11 @@ spec: description: 'ServiceUser - service user name (TODO: beagles, do we need this at all)' type: string + tenantName: + default: service + description: TenantName - the name of the OpenStack tenant that + controls the Octavia resources TODO(gthiemonge) same as ServiceAccount? + type: string transportURLSecret: description: TransportURLSecret - Secret containing RabbitMQ transportURL type: string @@ -635,6 +670,21 @@ spec: also be used to add additional files. Those get added to the service config dir in /etc/ . TODO: -> implement' type: object + lbMgmtNetwork: + default: + manageLbMgmtNetworks: true + subnetIpVersion: 4 + description: OctaviaLbMgmtNetworks Settings for Octavia management + networks + properties: + manageLbMgmtNetworks: + default: true + type: boolean + subnetIpVersion: + default: 4 + description: IP Version of the managed subnets + type: integer + type: object networkAttachments: description: NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network @@ -739,6 +789,11 @@ spec: description: 'ServiceUser - service user name (TODO: beagles, do we need this at all)' type: string + tenantName: + default: service + description: TenantName - the name of the OpenStack tenant that + controls the Octavia resources TODO(gthiemonge) same as ServiceAccount? + type: string transportURLSecret: description: TransportURLSecret - Secret containing RabbitMQ transportURL type: string @@ -791,6 +846,21 @@ spec: also be used to add additional files. Those get added to the service config dir in /etc/ . TODO: -> implement' type: object + lbMgmtNetwork: + default: + manageLbMgmtNetworks: true + subnetIpVersion: 4 + description: OctaviaLbMgmtNetworks Settings for Octavia management + networks + properties: + manageLbMgmtNetworks: + default: true + type: boolean + subnetIpVersion: + default: 4 + description: IP Version of the managed subnets + type: integer + type: object networkAttachments: description: NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network @@ -895,6 +965,11 @@ spec: description: 'ServiceUser - service user name (TODO: beagles, do we need this at all)' type: string + tenantName: + default: service + description: TenantName - the name of the OpenStack tenant that + controls the Octavia resources TODO(gthiemonge) same as ServiceAccount? + type: string transportURLSecret: description: TransportURLSecret - Secret containing RabbitMQ transportURL type: string diff --git a/controllers/amphoracontroller_controller.go b/controllers/amphoracontroller_controller.go index 699ff541..96551ba2 100644 --- a/controllers/amphoracontroller_controller.go +++ b/controllers/amphoracontroller_controller.go @@ -35,9 +35,10 @@ import ( keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" oko_secret "github.com/openstack-k8s-operators/lib-common/modules/common/secret" + neutronv1 "github.com/openstack-k8s-operators/neutron-operator/api/v1beta1" octaviav1 "github.com/openstack-k8s-operators/octavia-operator/api/v1beta1" "github.com/openstack-k8s-operators/octavia-operator/pkg/amphoracontrollers" - "github.com/openstack-k8s-operators/octavia-operator/pkg/octavia" + octavia "github.com/openstack-k8s-operators/octavia-operator/pkg/octavia" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" k8s_errors "k8s.io/apimachinery/pkg/api/errors" @@ -209,7 +210,14 @@ func (r *OctaviaAmphoraControllerReconciler) reconcileNormal(ctx context.Context } configMapVars[transportURLSecret.Name] = env.SetValue(hash) - err = r.generateServiceConfigMaps(ctx, instance, helper, &configMapVars) + // Create load balancer management network and get its Id + networkID, err := amphoracontrollers.EnsureLbMgmtNetworks(ctx, instance, &r.Log, helper) + if err != nil { + return ctrl.Result{}, err + } + r.Log.Info(fmt.Sprintf("Using management network \"%s\"", networkID)) + + err = r.generateServiceConfigMaps(ctx, instance, helper, &configMapVars, networkID) if err != nil { instance.Status.Conditions.Set(condition.FalseCondition( condition.ServiceConfigReadyCondition, @@ -362,6 +370,7 @@ func (r *OctaviaAmphoraControllerReconciler) generateServiceConfigMaps( instance *octaviav1.OctaviaAmphoraController, helper *helper.Helper, envVars *map[string]env.Setter, + lbMgmtNetworkID string, ) error { r.Log.Info(fmt.Sprintf("generating service config map for %s (%s)", instance.Name, instance.Kind)) cmLabels := labels.GetLabels(instance, labels.GetGroupLabel(octavia.ServiceName), map[string]string{}) @@ -387,6 +396,7 @@ func (r *OctaviaAmphoraControllerReconciler) generateServiceConfigMaps( templateParameters["KeystoneInternalURL"] = keystoneInternalURL templateParameters["KeystonePublicURL"] = keystonePublicURL templateParameters["ServiceRoleName"] = instance.Spec.Role + templateParameters["LbMgmtNetworkId"] = lbMgmtNetworkID // TODO(beagles): populate the template parameters cms := []util.Template{ @@ -450,5 +460,6 @@ func (r *OctaviaAmphoraControllerReconciler) SetupWithManager(mgr ctrl.Manager) Owns(&corev1.Secret{}). Owns(&corev1.ConfigMap{}). Owns(&appsv1.Deployment{}). + Owns(&neutronv1.NeutronAPI{}). Complete(r) } diff --git a/controllers/octavia_controller.go b/controllers/octavia_controller.go index 62db03ef..52305214 100644 --- a/controllers/octavia_controller.go +++ b/controllers/octavia_controller.go @@ -807,6 +807,8 @@ func (r *OctaviaReconciler) amphoraControllerDeploymentCreateOrUpdate( deployment.Spec.Secret = instance.Spec.Secret deployment.Spec.TransportURLSecret = instance.Status.TransportURLSecret deployment.Spec.ServiceAccount = instance.RbacResourceName() + deployment.Spec.LbMgmtNetworks.ManageLbMgmtNetworks = instance.Spec.LbMgmtNetworks.ManageLbMgmtNetworks + deployment.Spec.LbMgmtNetworks.SubnetIPVersion = instance.Spec.LbMgmtNetworks.SubnetIPVersion if len(deployment.Spec.NodeSelector) == 0 { deployment.Spec.NodeSelector = instance.Spec.NodeSelector } diff --git a/go.mod b/go.mod index de1edafb..575d72e8 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/openstack-k8s-operators/lib-common/modules/common v0.3.1-0.20231006072650-7fe7fe16bcd1 github.com/openstack-k8s-operators/lib-common/modules/openstack v0.3.1-0.20231006072650-7fe7fe16bcd1 github.com/openstack-k8s-operators/mariadb-operator/api v0.3.0 + github.com/openstack-k8s-operators/neutron-operator/api v0.3.0 github.com/openstack-k8s-operators/octavia-operator/api v0.0.0-00010101000000-000000000000 github.com/openstack-k8s-operators/ovn-operator/api v0.3.1-0.20231005121428-58b0e24477c0 go.uber.org/zap v1.26.0 @@ -51,6 +52,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/openshift/api v3.9.0+incompatible // indirect + github.com/openstack-k8s-operators/lib-common/modules/storage v0.1.1-0.20230927082538-4f614f333d17 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.15.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect diff --git a/go.sum b/go.sum index 85b466db..8849c141 100644 --- a/go.sum +++ b/go.sum @@ -142,8 +142,12 @@ github.com/openstack-k8s-operators/lib-common/modules/common v0.3.1-0.2023100607 github.com/openstack-k8s-operators/lib-common/modules/common v0.3.1-0.20231006072650-7fe7fe16bcd1/go.mod h1:Ozg6SxfwOtMkiH553c0XQBWuygZQq4jDQCpR4hZqlxM= github.com/openstack-k8s-operators/lib-common/modules/openstack v0.3.1-0.20231006072650-7fe7fe16bcd1 h1:C1UGjKkwP1d/GwAjoIlgA6h/8FcIAT5De7ShPGSTXEw= github.com/openstack-k8s-operators/lib-common/modules/openstack v0.3.1-0.20231006072650-7fe7fe16bcd1/go.mod h1:LOXXvTQCwhOBNd+0FTlgllpa3wqlkI6Vf3Q5QVRVPlw= +github.com/openstack-k8s-operators/lib-common/modules/storage v0.1.1-0.20230927082538-4f614f333d17 h1:epXz35gpdGp/CbYzAT926971T9kpO6r1v2GX8I1HHGM= +github.com/openstack-k8s-operators/lib-common/modules/storage v0.1.1-0.20230927082538-4f614f333d17/go.mod h1:kKFAr7wZw3mX83hlQVbf2hV7TGhNNxVgMSNt9YtUPzI= github.com/openstack-k8s-operators/mariadb-operator/api v0.3.0 h1:FB0xB6whYM6W4XIncYo2mPiOJWkFsIOWtCT+UOtvOaQ= github.com/openstack-k8s-operators/mariadb-operator/api v0.3.0/go.mod h1:xhiz5wFdKWwVM7BF/VYon4TT3NuUPXp/Pyn2hWcp0CE= +github.com/openstack-k8s-operators/neutron-operator/api v0.3.0 h1:QLcbVW93dai51o/P5w28+UMIF96+ullprZg9KMIc8J8= +github.com/openstack-k8s-operators/neutron-operator/api v0.3.0/go.mod h1:UV+KJ1V/b+FQr5Nq1Ga3BDxJcfAT6nyirrCRnzcY+ME= github.com/openstack-k8s-operators/ovn-operator/api v0.3.1-0.20231005121428-58b0e24477c0 h1:BOvgqkPaGeXeYxC8xRgU8qqfWQagD9NWmVIkX7IceJI= github.com/openstack-k8s-operators/ovn-operator/api v0.3.1-0.20231005121428-58b0e24477c0/go.mod h1:hLgc5WItywTzBSbd9/kZto4R2K5VnaW+OH1nKQtZ4vk= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/main.go b/main.go index d9196c83..8f08661b 100644 --- a/main.go +++ b/main.go @@ -39,6 +39,7 @@ import ( rabbitmqv1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1" keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" + neutronv1 "github.com/openstack-k8s-operators/neutron-operator/api/v1beta1" ovn1beta1 "github.com/openstack-k8s-operators/ovn-operator/api/v1beta1" octaviav1 "github.com/openstack-k8s-operators/octavia-operator/api/v1beta1" @@ -56,6 +57,7 @@ func init() { utilruntime.Must(mariadbv1.AddToScheme(scheme)) utilruntime.Must(keystonev1.AddToScheme(scheme)) utilruntime.Must(rabbitmqv1.AddToScheme(scheme)) + utilruntime.Must(neutronv1.AddToScheme(scheme)) utilruntime.Must(octaviav1.AddToScheme(scheme)) utilruntime.Must(ovn1beta1.AddToScheme(scheme)) utilruntime.Must(networkv1.AddToScheme(scheme)) diff --git a/pkg/amphoracontrollers/const.go b/pkg/amphoracontrollers/const.go new file mode 100644 index 00000000..fc920ac7 --- /dev/null +++ b/pkg/amphoracontrollers/const.go @@ -0,0 +1,68 @@ +/* + +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. +*/ + +package amphoracontrollers + +const ( + // Common consts for Management network + + // LbMgmtNetName - + LbMgmtNetName = "lb-mgmt-net" + + // LbMgmtNetDescription - + LbMgmtNetDescription = "LBaaS Management Network" + + // LbMgmtSubnetName - + LbMgmtSubnetName = "lb-mgmt-subnet" + + // LbMgmtSubnetDescription - + LbMgmtSubnetDescription = "LBaaS Management Subnet" + + // IPv4 consts + + // LbMgmtSubnetCIDR - + LbMgmtSubnetCIDR = "172.24.0.0/16" + + // LbMgmtSubnetAllocationPoolStart - + LbMgmtSubnetAllocationPoolStart = "172.24.0.2" + + // LbMgmtSubnetAllocationPoolEnd - + LbMgmtSubnetAllocationPoolEnd = "172.24.255.254" + + // LbMgmtSubnetGatewayIP - + LbMgmtSubnetGatewayIP = "" + + // IPv6 consts + // using Unique local address (fc00::/7) + // with Global ID 6c:6261:6173 ("lbaas") + + // LbMgmtSubnetIPv6CIDR - + LbMgmtSubnetIPv6CIDR = "fd6c:6261:6173:0001::/64" + + // LbMgmtSubnetIPv6AllocationPoolStart - + LbMgmtSubnetIPv6AllocationPoolStart = "fd6c:6261:6173:0001::2" + + // LbMgmtSubnetIPv6AllocationPoolEnd - + LbMgmtSubnetIPv6AllocationPoolEnd = "fd6c:6261:6173:0001:ffff:ffff:ffff:ffff" + + // LbMgmtSubnetIPv6AddressMode - + LbMgmtSubnetIPv6AddressMode = "slaac" + + // LbMgmtSubnetIPv6RAMode - + LbMgmtSubnetIPv6RAMode = "slaac" + + // LbMgmtSubnetIPv6GatewayIP - + LbMgmtSubnetIPv6GatewayIP = "" +) diff --git a/pkg/amphoracontrollers/lb_mgmt_network.go b/pkg/amphoracontrollers/lb_mgmt_network.go new file mode 100644 index 00000000..fb30eedd --- /dev/null +++ b/pkg/amphoracontrollers/lb_mgmt_network.go @@ -0,0 +1,225 @@ +/* +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. +*/ + +package amphoracontrollers + +import ( + "context" + "fmt" + + "github.com/go-logr/logr" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" + keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" + "github.com/openstack-k8s-operators/lib-common/modules/common/helper" + octaviav1 "github.com/openstack-k8s-operators/octavia-operator/api/v1beta1" + "github.com/openstack-k8s-operators/octavia-operator/pkg/octavia" +) + +func ensureLbMgmtSubnet(client *gophercloud.ServiceClient, instance *octaviav1.OctaviaAmphoraController, log *logr.Logger, serviceTenantID string, lbMgmtNetID string) (*subnets.Subnet, error) { + ipVersion := instance.Spec.LbMgmtNetworks.SubnetIPVersion + + listOpts := subnets.ListOpts{ + Name: LbMgmtSubnetName, + NetworkID: lbMgmtNetID, + TenantID: serviceTenantID, + IPVersion: ipVersion, + } + allPages, err := subnets.List(client, listOpts).AllPages() + if err != nil { + return nil, err + } + allSubnets, err := subnets.ExtractSubnets(allPages) + if err != nil { + return nil, err + } + + var createOpts subnets.CreateOpts + if ipVersion == 6 { + gatewayIP := LbMgmtSubnetIPv6GatewayIP + createOpts = subnets.CreateOpts{ + Name: LbMgmtSubnetName, + Description: LbMgmtSubnetDescription, + NetworkID: lbMgmtNetID, + TenantID: serviceTenantID, + CIDR: LbMgmtSubnetIPv6CIDR, + IPVersion: gophercloud.IPVersion(ipVersion), + IPv6AddressMode: LbMgmtSubnetIPv6AddressMode, + IPv6RAMode: LbMgmtSubnetIPv6RAMode, + AllocationPools: []subnets.AllocationPool{ + { + Start: LbMgmtSubnetIPv6AllocationPoolStart, + End: LbMgmtSubnetIPv6AllocationPoolEnd, + }, + }, + GatewayIP: &gatewayIP, + } + } else { + gatewayIP := LbMgmtSubnetGatewayIP + createOpts = subnets.CreateOpts{ + Name: LbMgmtSubnetName, + Description: LbMgmtSubnetDescription, + NetworkID: lbMgmtNetID, + TenantID: serviceTenantID, + CIDR: LbMgmtSubnetCIDR, + IPVersion: gophercloud.IPVersion(ipVersion), + AllocationPools: []subnets.AllocationPool{ + { + Start: LbMgmtSubnetAllocationPoolStart, + End: LbMgmtSubnetAllocationPoolEnd, + }, + }, + GatewayIP: &gatewayIP, + } + } + + var lbMgmtSubnet *subnets.Subnet + if len(allSubnets) == 0 { + log.Info(fmt.Sprintf("Creating Octavia management subnet \"%s\"", createOpts.Name)) + lbMgmtSubnet, err = subnets.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + } else { + lbMgmtSubnet = &allSubnets[0] + delta := subnets.UpdateOpts{} + updateNeeded := false + if lbMgmtSubnet.Description != createOpts.Description { + delta.Description = &createOpts.Description + updateNeeded = true + } + if lbMgmtSubnet.AllocationPools[0].Start != createOpts.AllocationPools[0].Start || + lbMgmtSubnet.AllocationPools[0].End != createOpts.AllocationPools[0].End { + delta.AllocationPools = []subnets.AllocationPool{ + { + Start: createOpts.AllocationPools[0].Start, + End: createOpts.AllocationPools[0].End, + }, + } + updateNeeded = true + } + if lbMgmtSubnet.GatewayIP != *createOpts.GatewayIP { + delta.GatewayIP = createOpts.GatewayIP + updateNeeded = true + } + if updateNeeded { + log.Info(fmt.Sprintf("Updating Octavia management subnet \"%s\"", createOpts.Name)) + lbMgmtSubnet, err = subnets.Update(client, lbMgmtSubnet.ID, delta).Extract() + if err != nil { + return nil, err + } + } + } + return lbMgmtSubnet, nil +} + +func getLbMgmtNetwork(client *gophercloud.ServiceClient, instance *octaviav1.OctaviaAmphoraController, serviceTenantID string) (*networks.Network, error) { + listOpts := networks.ListOpts{ + Name: LbMgmtNetName, + TenantID: serviceTenantID, + } + allPages, err := networks.List(client, listOpts).AllPages() + if err != nil { + return nil, err + } + allNetworks, err := networks.ExtractNetworks(allPages) + if err != nil { + return nil, err + } + if len(allNetworks) > 0 { + return &allNetworks[0], nil + } + return nil, nil +} + +func ensureLbMgmtNetwork(client *gophercloud.ServiceClient, instance *octaviav1.OctaviaAmphoraController, log *logr.Logger, serviceTenantID string) (*networks.Network, error) { + lbMgmtNet, err := getLbMgmtNetwork(client, instance, serviceTenantID) + if err != nil { + return nil, err + } + + asu := true + createOpts := networks.CreateOpts{ + Name: LbMgmtNetName, + Description: LbMgmtNetDescription, + AdminStateUp: &asu, + TenantID: serviceTenantID, + } + if lbMgmtNet == nil { + log.Info(fmt.Sprintf("Creating Octavia management network \"%s\"", createOpts.Name)) + lbMgmtNet, err = networks.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + } else { + emptyOpts := networks.UpdateOpts{} + delta := networks.UpdateOpts{} + if lbMgmtNet.Description != createOpts.Description { + delta.Description = &createOpts.Description + } + if lbMgmtNet.AdminStateUp != *createOpts.AdminStateUp { + delta.AdminStateUp = createOpts.AdminStateUp + } + if delta != emptyOpts { + log.Info(fmt.Sprintf("Updating Octavia management network \"%s\"", createOpts.Name)) + lbMgmtNet, err = networks.Update(client, lbMgmtNet.ID, delta).Extract() + if err != nil { + return nil, err + } + } + } + + _, err = ensureLbMgmtSubnet(client, instance, log, serviceTenantID, lbMgmtNet.ID) + if err != nil { + return nil, err + } + + return lbMgmtNet, nil +} + +// EnsureLbMgmtNetworks - ensure that the Octavia management network is created +// +// returns the UUID of the network +func EnsureLbMgmtNetworks(ctx context.Context, instance *octaviav1.OctaviaAmphoraController, log *logr.Logger, helper *helper.Helper) (string, error) { + keystoneAPI, err := keystonev1.GetKeystoneAPI(ctx, helper, instance.Namespace, map[string]string{}) + if err != nil { + return "", err + } + o, _, err := octavia.GetAdminServiceClient(ctx, helper, keystoneAPI) + if err != nil { + return "", err + } + client, err := octavia.GetNetworkClient(o) + if err != nil { + return "", err + } + serviceTenant, err := octavia.GetProject(o, instance.Spec.TenantName) + if err != nil { + return "", err + } + var network *networks.Network + if instance.Spec.LbMgmtNetworks.ManageLbMgmtNetworks { + network, err = ensureLbMgmtNetwork(client, instance, log, serviceTenant.ID) + } else { + network, err = getLbMgmtNetwork(client, instance, serviceTenant.ID) + if network == nil { + return "", fmt.Errorf("Cannot find network \"%s\"", LbMgmtNetName) + } + } + if err != nil { + return "", err + } + return network.ID, nil +} diff --git a/templates/octaviaamphoracontroller/config/octavia.conf b/templates/octaviaamphoracontroller/config/octavia.conf index 3c6a9c93..8d3445f9 100644 --- a/templates/octaviaamphoracontroller/config/octavia.conf +++ b/templates/octaviaamphoracontroller/config/octavia.conf @@ -29,6 +29,7 @@ interface=internal port_detach_timeout=300 [haproxy_amphora] [controller_worker] +amp_boot_network_list={{ .LbMgmtNetworkId }} [task_flow] [oslo_messaging] # topic=octavia-rpc