diff --git a/api/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml b/api/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml index 6a8e5d8d..a5fc1c9f 100644 --- a/api/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml +++ b/api/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml @@ -50,6 +50,30 @@ spec: description: OctaviaAmphoraControllerSpec defines common state for all Octavia Amphora Controllers properties: + amphoraCustomFlavors: + description: AmphoraCustomFlavors - User-defined flavors for Octavia + items: + description: OctaviaAmphoraFlavor Settings for custom Amphora flavors + properties: + RAM: + type: integer + RxTxFactor: + type: string + VCPUs: + type: integer + description: + type: string + disk: + type: integer + name: + type: string + required: + - RAM + - VCPUs + - disk + - name + type: object + type: array certsPassphraseSecret: default: octavia-ca-passphrase description: Name of secret containing passphrase for the CA private diff --git a/api/bases/octavia.openstack.org_octavias.yaml b/api/bases/octavia.openstack.org_octavias.yaml index 9f9ca882..9466d1b4 100644 --- a/api/bases/octavia.openstack.org_octavias.yaml +++ b/api/bases/octavia.openstack.org_octavias.yaml @@ -44,6 +44,30 @@ spec: spec: description: OctaviaSpec defines the desired state of Octavia properties: + amphoraCustomFlavors: + description: AmphoraCustomFlavors - User-defined flavors for Octavia + items: + description: OctaviaAmphoraFlavor Settings for custom Amphora flavors + properties: + RAM: + type: integer + RxTxFactor: + type: string + VCPUs: + type: integer + description: + type: string + disk: + type: integer + name: + type: string + required: + - RAM + - VCPUs + - disk + - name + type: object + type: array certsSecret: default: octavia-certs-secret description: LoadBalancerCerts - Secret containing certs for securing @@ -434,6 +458,31 @@ spec: description: OctaviaHousekeeping - Spec definition for the Octavia Housekeeping agent for the Octavia deployment properties: + amphoraCustomFlavors: + description: AmphoraCustomFlavors - User-defined flavors for Octavia + items: + description: OctaviaAmphoraFlavor Settings for custom Amphora + flavors + properties: + RAM: + type: integer + RxTxFactor: + type: string + VCPUs: + type: integer + description: + type: string + disk: + type: integer + name: + type: string + required: + - RAM + - VCPUs + - disk + - name + type: object + type: array certsPassphraseSecret: default: octavia-ca-passphrase description: Name of secret containing passphrase for the CA private @@ -619,6 +668,31 @@ spec: description: OctaviaHousekeeping - Spec definition for the Octavia Housekeeping agent for the Octavia deployment properties: + amphoraCustomFlavors: + description: AmphoraCustomFlavors - User-defined flavors for Octavia + items: + description: OctaviaAmphoraFlavor Settings for custom Amphora + flavors + properties: + RAM: + type: integer + RxTxFactor: + type: string + VCPUs: + type: integer + description: + type: string + disk: + type: integer + name: + type: string + required: + - RAM + - VCPUs + - disk + - name + type: object + type: array certsPassphraseSecret: default: octavia-ca-passphrase description: Name of secret containing passphrase for the CA private @@ -804,6 +878,31 @@ spec: description: OctaviaHousekeeping - Spec definition for the Octavia Housekeeping agent for the Octavia deployment properties: + amphoraCustomFlavors: + description: AmphoraCustomFlavors - User-defined flavors for Octavia + items: + description: OctaviaAmphoraFlavor Settings for custom Amphora + flavors + properties: + RAM: + type: integer + RxTxFactor: + type: string + VCPUs: + type: integer + description: + type: string + disk: + type: integer + name: + type: string + required: + - RAM + - VCPUs + - disk + - name + type: object + type: array certsPassphraseSecret: default: octavia-ca-passphrase description: Name of secret containing passphrase for the CA private diff --git a/api/v1beta1/amphoracontroller_types.go b/api/v1beta1/amphoracontroller_types.go index ec17c306..6bdb167f 100644 --- a/api/v1beta1/amphoracontroller_types.go +++ b/api/v1beta1/amphoracontroller_types.go @@ -134,6 +134,11 @@ type OctaviaAmphoraControllerSpec struct { // +kubebuilder:validation:Optional // +kubebuilder:default={manageLbMgmtNetworks: true, subnetIpVersion: 4} LbMgmtNetworks OctaviaLbMgmtNetworks `json:"lbMgmtNetwork"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default={} + // AmphoraCustomFlavors - User-defined flavors for Octavia + AmphoraCustomFlavors []OctaviaAmphoraFlavor `json:"amphoraCustomFlavors,omitempty"` } // OctaviaAmphoraControllerStatus defines the observed state of the Octavia Amphora Controller diff --git a/api/v1beta1/octavia_types.go b/api/v1beta1/octavia_types.go index a6f0690a..302b7d12 100644 --- a/api/v1beta1/octavia_types.go +++ b/api/v1beta1/octavia_types.go @@ -133,6 +133,11 @@ type OctaviaSpec struct { // LoadBalancerSSHPrivKey - The name of the secret that will be used to // store the private key for connecting to amphorae via SSH LoadBalancerSSHPrivKey string `json:"sshPrivkeySecret,omitempty"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default={} + // AmphoraCustomFlavors - User-defined flavors for Octavia + AmphoraCustomFlavors []OctaviaAmphoraFlavor `json:"amphoraCustomFlavors,omitempty"` } // PasswordSelector to identify the DB and AdminUser password from the Secret @@ -160,6 +165,27 @@ type OctaviaLbMgmtNetworks struct { SubnetIPVersion int `json:"subnetIpVersion,omitempty"` } +// OctaviaAmphoraFlavor Settings for custom Amphora flavors +type OctaviaAmphoraFlavor struct { + // +kubebuilder:validation:Required + Name string `json:"name"` + + // +kubebuilder:validation:Optional + Description string `json:"description"` + + // +kubebuilder:validation:Required + VCPUs int `json:"VCPUs"` + + // +kubebuilder:validation:Required + RAM int `json:"RAM"` + + // +kubebuilder:validation:Required + Disk int `json:"disk"` + + // +kubebuilder:validation:Optional + RxTxFactor string `json:"RxTxFactor"` +} + // OctaviaStatus defines the observed state of Octavia type OctaviaStatus struct { diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index c1fd657c..00271091 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -305,6 +305,11 @@ func (in *OctaviaAmphoraControllerSpec) DeepCopyInto(out *OctaviaAmphoraControll copy(*out, *in) } out.LbMgmtNetworks = in.LbMgmtNetworks + if in.AmphoraCustomFlavors != nil { + in, out := &in.AmphoraCustomFlavors, &out.AmphoraCustomFlavors + *out = make([]OctaviaAmphoraFlavor, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OctaviaAmphoraControllerSpec. @@ -361,6 +366,21 @@ func (in *OctaviaAmphoraControllerStatus) DeepCopy() *OctaviaAmphoraControllerSt return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OctaviaAmphoraFlavor) DeepCopyInto(out *OctaviaAmphoraFlavor) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OctaviaAmphoraFlavor. +func (in *OctaviaAmphoraFlavor) DeepCopy() *OctaviaAmphoraFlavor { + if in == nil { + return nil + } + out := new(OctaviaAmphoraFlavor) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OctaviaDefaults) DeepCopyInto(out *OctaviaDefaults) { *out = *in @@ -446,6 +466,11 @@ func (in *OctaviaSpec) DeepCopyInto(out *OctaviaSpec) { in.OctaviaHealthManager.DeepCopyInto(&out.OctaviaHealthManager) in.OctaviaWorker.DeepCopyInto(&out.OctaviaWorker) out.LbMgmtNetworks = in.LbMgmtNetworks + if in.AmphoraCustomFlavors != nil { + in, out := &in.AmphoraCustomFlavors, &out.AmphoraCustomFlavors + *out = make([]OctaviaAmphoraFlavor, len(*in)) + copy(*out, *in) + } } // 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 6a8e5d8d..a5fc1c9f 100644 --- a/config/crd/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml +++ b/config/crd/bases/octavia.openstack.org_octaviaamphoracontrollers.yaml @@ -50,6 +50,30 @@ spec: description: OctaviaAmphoraControllerSpec defines common state for all Octavia Amphora Controllers properties: + amphoraCustomFlavors: + description: AmphoraCustomFlavors - User-defined flavors for Octavia + items: + description: OctaviaAmphoraFlavor Settings for custom Amphora flavors + properties: + RAM: + type: integer + RxTxFactor: + type: string + VCPUs: + type: integer + description: + type: string + disk: + type: integer + name: + type: string + required: + - RAM + - VCPUs + - disk + - name + type: object + type: array certsPassphraseSecret: default: octavia-ca-passphrase description: Name of secret containing passphrase for the CA private diff --git a/config/crd/bases/octavia.openstack.org_octavias.yaml b/config/crd/bases/octavia.openstack.org_octavias.yaml index 9f9ca882..9466d1b4 100644 --- a/config/crd/bases/octavia.openstack.org_octavias.yaml +++ b/config/crd/bases/octavia.openstack.org_octavias.yaml @@ -44,6 +44,30 @@ spec: spec: description: OctaviaSpec defines the desired state of Octavia properties: + amphoraCustomFlavors: + description: AmphoraCustomFlavors - User-defined flavors for Octavia + items: + description: OctaviaAmphoraFlavor Settings for custom Amphora flavors + properties: + RAM: + type: integer + RxTxFactor: + type: string + VCPUs: + type: integer + description: + type: string + disk: + type: integer + name: + type: string + required: + - RAM + - VCPUs + - disk + - name + type: object + type: array certsSecret: default: octavia-certs-secret description: LoadBalancerCerts - Secret containing certs for securing @@ -434,6 +458,31 @@ spec: description: OctaviaHousekeeping - Spec definition for the Octavia Housekeeping agent for the Octavia deployment properties: + amphoraCustomFlavors: + description: AmphoraCustomFlavors - User-defined flavors for Octavia + items: + description: OctaviaAmphoraFlavor Settings for custom Amphora + flavors + properties: + RAM: + type: integer + RxTxFactor: + type: string + VCPUs: + type: integer + description: + type: string + disk: + type: integer + name: + type: string + required: + - RAM + - VCPUs + - disk + - name + type: object + type: array certsPassphraseSecret: default: octavia-ca-passphrase description: Name of secret containing passphrase for the CA private @@ -619,6 +668,31 @@ spec: description: OctaviaHousekeeping - Spec definition for the Octavia Housekeeping agent for the Octavia deployment properties: + amphoraCustomFlavors: + description: AmphoraCustomFlavors - User-defined flavors for Octavia + items: + description: OctaviaAmphoraFlavor Settings for custom Amphora + flavors + properties: + RAM: + type: integer + RxTxFactor: + type: string + VCPUs: + type: integer + description: + type: string + disk: + type: integer + name: + type: string + required: + - RAM + - VCPUs + - disk + - name + type: object + type: array certsPassphraseSecret: default: octavia-ca-passphrase description: Name of secret containing passphrase for the CA private @@ -804,6 +878,31 @@ spec: description: OctaviaHousekeeping - Spec definition for the Octavia Housekeeping agent for the Octavia deployment properties: + amphoraCustomFlavors: + description: AmphoraCustomFlavors - User-defined flavors for Octavia + items: + description: OctaviaAmphoraFlavor Settings for custom Amphora + flavors + properties: + RAM: + type: integer + RxTxFactor: + type: string + VCPUs: + type: integer + description: + type: string + disk: + type: integer + name: + type: string + required: + - RAM + - VCPUs + - disk + - name + type: object + type: array certsPassphraseSecret: default: octavia-ca-passphrase description: Name of secret containing passphrase for the CA private diff --git a/controllers/octavia_controller.go b/controllers/octavia_controller.go index 073cde9f..b839fdff 100644 --- a/controllers/octavia_controller.go +++ b/controllers/octavia_controller.go @@ -826,6 +826,7 @@ func (r *OctaviaReconciler) amphoraControllerDaemonSetCreateOrUpdate( daemonset.Spec.LoadBalancerCerts = instance.Spec.LoadBalancerCerts daemonset.Spec.LoadBalancerSSHPrivKey = instance.Spec.LoadBalancerSSHPrivKey daemonset.Spec.LoadBalancerSSHPubKey = instance.Spec.LoadBalancerSSHPubKey + daemonset.Spec.AmphoraCustomFlavors = instance.Spec.AmphoraCustomFlavors if len(daemonset.Spec.NodeSelector) == 0 { daemonset.Spec.NodeSelector = instance.Spec.NodeSelector } diff --git a/pkg/amphoracontrollers/flavors.go b/pkg/amphoracontrollers/flavors.go index 4c7b4a9b..2b4f7eee 100644 --- a/pkg/amphoracontrollers/flavors.go +++ b/pkg/amphoracontrollers/flavors.go @@ -16,88 +16,241 @@ package amphoracontrollers import ( "context" + "encoding/json" "fmt" + "strconv" "strings" "github.com/go-logr/logr" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" + computeflavors "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/flavorprofiles" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/flavors" "github.com/openstack-k8s-operators/lib-common/modules/common/helper" "github.com/openstack-k8s-operators/lib-common/modules/openstack" octaviav1 "github.com/openstack-k8s-operators/octavia-operator/api/v1beta1" "github.com/openstack-k8s-operators/octavia-operator/pkg/octavia" ) -func ensureNovaFlavors(osclient *openstack.OpenStack, log *logr.Logger) (string, error) { - client, err := octavia.GetComputeClient(osclient) - if err != nil { - return "", err +// OctaviaFlavors - +type OctaviaFlavors struct { + Name string + Description string + VCPUs int + RAM int + Disk int + RxTxFactor int +} + +// FlavorProfileData - +type FlavorProfileData struct { + ComputeFlavorID string `json:"compute_flavor"` +} + +var ( + // TODO(gthiemonge) we may consider updating the size of the disk + // 3GB is fine when enabling log offloading+disabling local disk storage + // but when using the defaults, the disk can be filled when testing network performances. + defaultFlavors = []OctaviaFlavors{ + { + Name: "amphora", + Description: "Flavor for Octavia amphora instances (1 vCPU, 1 GB RAM, 3 GB disk, default flavor)", + VCPUs: 1, + RAM: 1024, + Disk: 3, + RxTxFactor: 1.0, + }, { + Name: "amphora-4vcpus", + Description: "Flavor for Octavia amphora instances (4 vCPUs, 4 GB RAM, 3 GB disk)", + VCPUs: 4, + RAM: 4096, + Disk: 3, + RxTxFactor: 1.0, + }, } +) +func getAmphoraFlavors(computeClient *gophercloud.ServiceClient) (map[string]computeflavors.Flavor, error) { // Get Octavia flavors - listOpts := flavors.ListOpts{ - AccessType: flavors.AllAccess, + listOpts := computeflavors.ListOpts{ + AccessType: computeflavors.AllAccess, } - allPages, err := flavors.ListDetail(client, listOpts).AllPages() + allPages, err := computeflavors.ListDetail(computeClient, listOpts).AllPages() if err != nil { - return "", err + return nil, err } - allFlavors, err := flavors.ExtractFlavors(allPages) + allFlavors, err := computeflavors.ExtractFlavors(allPages) if err != nil { - return "", err + return nil, err } - amphoraFlavors := make(map[string]flavors.Flavor) + amphoraFlavors := make(map[string]computeflavors.Flavor) for _, flavor := range allFlavors { if strings.HasPrefix(flavor.Name, "octavia-") { amphoraFlavors[flavor.Name] = flavor } } + return amphoraFlavors, nil +} + +func getOctaviaFlavorProfiles(lbClient *gophercloud.ServiceClient) (map[string]flavorprofiles.FlavorProfile, error) { + listOpts := flavorprofiles.ListOpts{} + allPages, err := flavorprofiles.List(lbClient, listOpts).AllPages() + if err != nil { + return nil, err + } + allFlavorProfiles, err := flavorprofiles.ExtractFlavorProfiles(allPages) + if err != nil { + return nil, err + } + flavorProfiles := make(map[string]flavorprofiles.FlavorProfile) + for _, flavorProfile := range allFlavorProfiles { + flavorProfiles[flavorProfile.Name] = flavorProfile + } + return flavorProfiles, nil +} + +func getOctaviaFlavors(lbClient *gophercloud.ServiceClient) (map[string]flavors.Flavor, error) { + listOpts := flavors.ListOpts{} + allPages, err := flavors.List(lbClient, listOpts).AllPages() + if err != nil { + return nil, err + } + allFlavors, err := flavors.ExtractFlavors(allPages) + if err != nil { + return nil, err + } + flavors := make(map[string]flavors.Flavor) + for _, flavor := range allFlavors { + flavors[flavor.Name] = flavor + } + return flavors, nil +} + +func ensureFlavors(osclient *openstack.OpenStack, log *logr.Logger, instance *octaviav1.OctaviaAmphoraController) (string, error) { + computeClient, err := octavia.GetComputeClient(osclient) + if err != nil { + return "", err + } + + lbClient, err := octavia.GetLoadBalancerClient(osclient) + if err != nil { + return "", err + } + + amphoraFlavors, err := getAmphoraFlavors(computeClient) + if err != nil { + return "", err + } isPublic := false - // TODO(gthiemonge) we may consider updating the size of the disk - // 3GB is fine when enabling log offloading+disabling local disk storage - // but when using the defaults, the disk can be filled when testing network performances. - defaultFlavorsCreateOpts := []flavors.CreateOpts{ - { - Name: "octavia-amphora", - Description: "Flavor for Octavia amphora instances (1 vCPU, 1 GB RAM, 3 GB disk, default flavor)", - VCPUs: 1, - RAM: 1024, - Disk: gophercloud.IntToPointer(3), - RxTxFactor: 1.0, + flavorsCreateOpts := []computeflavors.CreateOpts{} + for _, defaultFlavor := range defaultFlavors { + flavorsCreateOpts = append(flavorsCreateOpts, computeflavors.CreateOpts{ + Name: fmt.Sprintf("octavia-%s", defaultFlavor.Name), + Description: defaultFlavor.Description, + VCPUs: defaultFlavor.VCPUs, + RAM: defaultFlavor.RAM, + Disk: gophercloud.IntToPointer(defaultFlavor.Disk), + RxTxFactor: float64(defaultFlavor.RxTxFactor), IsPublic: &isPublic, - }, { - Name: "octavia-amphora-4vcpus", - Description: "Flavor for Octavia amphora instances (4 vCPUs, 4 GB RAM, 3 GB disk)", - VCPUs: 4, - RAM: 4096, - Disk: gophercloud.IntToPointer(3), - RxTxFactor: 1.0, + }) + } + for _, flavor := range instance.Spec.AmphoraCustomFlavors { + rxTxFactor := 1.0 + if flavor.RxTxFactor != "" { + if rxTxFactor, err = strconv.ParseFloat(flavor.RxTxFactor, 64); err != nil { + return "", err + } + } + flavorsCreateOpts = append(flavorsCreateOpts, computeflavors.CreateOpts{ + Name: fmt.Sprintf("octavia-%s", flavor.Name), + Description: flavor.Description, + VCPUs: flavor.VCPUs, + RAM: flavor.RAM, + Disk: gophercloud.IntToPointer(flavor.Disk), + RxTxFactor: rxTxFactor, IsPublic: &isPublic, - }, + }) } + + // Select the first flavor as the default flavor defaultFlavorID := "" - defaultFlavorName := defaultFlavorsCreateOpts[0].Name + defaultFlavorName := flavorsCreateOpts[0].Name // Default flavor already exists, get its ID if flavor, ok := amphoraFlavors[defaultFlavorName]; ok { defaultFlavorID = flavor.ID } - // Create missing flavors - for idx, defaultFlavorOpts := range defaultFlavorsCreateOpts { - if _, ok := amphoraFlavors[defaultFlavorOpts.Name]; !ok { - log.Info(fmt.Sprintf("Creating Amphora flavor \"%s\"", defaultFlavorOpts.Name)) - flavor, err := flavors.Create(client, defaultFlavorOpts).Extract() + // Create missing compute flavors + for idx, flavorOpts := range flavorsCreateOpts { + if _, ok := amphoraFlavors[flavorOpts.Name]; !ok { + log.Info(fmt.Sprintf("Creating Amphora flavor \"%s\"", flavorOpts.Name)) + flavor, err := computeflavors.Create(computeClient, flavorOpts).Extract() if err != nil { return "", err } + amphoraFlavors[flavorOpts.Name] = *flavor if idx == 0 { defaultFlavorID = flavor.ID } } } + // Get Octavia FlavorProfiles and Flavors + flavorProfileMap, err := getOctaviaFlavorProfiles(lbClient) + if err != nil { + return "", err + } + + flavorMap, err := getOctaviaFlavors(lbClient) + if err != nil { + return "", err + } + + for _, flavorOpts := range flavorsCreateOpts { + // Create FlavorProfiles if they don't exist + + name := strings.TrimPrefix(flavorOpts.Name, "octavia-") + + if _, ok := flavorProfileMap[name]; !ok { + flavorProfileData := FlavorProfileData{ + ComputeFlavorID: amphoraFlavors[flavorOpts.Name].ID, + } + fpDataJSON, err := json.Marshal(flavorProfileData) + if err != nil { + return "", err + } + flavorProfileCreateOpts := flavorprofiles.CreateOpts{ + Name: name, + ProviderName: "amphora", + FlavorData: string(fpDataJSON), + } + + log.Info(fmt.Sprintf("Creating Octavia flavor profile \"%s\"", flavorProfileCreateOpts.Name)) + fp, err := flavorprofiles.Create(lbClient, flavorProfileCreateOpts).Extract() + if err != nil { + return "", err + } + flavorProfileMap[fp.Name] = *fp + } + + // Create Flavors if they don't exist + if _, ok := flavorMap[name]; !ok { + flavorCreateOpts := flavors.CreateOpts{ + Name: name, + Description: flavorOpts.Description, + FlavorProfileId: flavorProfileMap[name].ID, + Enabled: true, + } + log.Info(fmt.Sprintf("Creating Octavia flavor \"%s\"", flavorCreateOpts.Name)) + _, err := flavors.Create(lbClient, flavorCreateOpts).Extract() + if err != nil { + return "", err + } + } + } + return defaultFlavorID, nil } @@ -107,15 +260,13 @@ func ensureNovaFlavors(osclient *openstack.OpenStack, log *logr.Logger) (string, func EnsureFlavors(ctx context.Context, instance *octaviav1.OctaviaAmphoraController, log *logr.Logger, helper *helper.Helper) (string, error) { osclient, err := GetOpenstackClient(ctx, instance, helper) if err != nil { - return "", err + return "", fmt.Errorf("Error while getting a service client when creating flavors: %w", err) } - defaultNovaFlavorID, err := ensureNovaFlavors(osclient, log) + defaultNovaFlavorID, err := ensureFlavors(osclient, log, instance) if err != nil { - return "", err + return "", fmt.Errorf("Error while creating flavors: %w", err) } - // TODO(gthiemonge) Create Octavia flavorprofiles and flavors when gophercloud support them - return defaultNovaFlavorID, nil } diff --git a/pkg/octavia/client.go b/pkg/octavia/client.go index ca4082b3..a0a8f318 100644 --- a/pkg/octavia/client.go +++ b/pkg/octavia/client.go @@ -116,3 +116,12 @@ func GetComputeClient(o *openstack.OpenStack) (*gophercloud.ServiceClient, error client.Microversion = "2.55" return client, nil } + +// GetLoadBalancerClient - +func GetLoadBalancerClient(o *openstack.OpenStack) (*gophercloud.ServiceClient, error) { + endpointOpts := gophercloud.EndpointOpts{ + Region: o.GetRegion(), + Availability: gophercloud.AvailabilityInternal, + } + return gophercloudopenstack.NewLoadBalancerV2(o.GetOSClient().ProviderClient, endpointOpts) +}