Skip to content

Commit

Permalink
Merge pull request #599 from sapcc/nova-fix-ironic-ignoring
Browse files Browse the repository at this point in the history
plugins/nova: fix ignoring of baremetal instances in liquid-ironic compat mode
  • Loading branch information
majewsky authored Nov 13, 2024
2 parents 34a3733 + 7c8f7c7 commit 8b1b522
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 11 deletions.
2 changes: 1 addition & 1 deletion internal/plugins/capacity_nova.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func (p *capacityNovaPlugin) PluginTypeID() string {
func (p *capacityNovaPlugin) Scrape(ctx context.Context, backchannel core.CapacityPluginBackchannel, allAZs []limes.AvailabilityZone) (result map[db.ServiceType]map[liquid.ResourceName]core.PerAZ[core.CapacityData], serializedMetrics []byte, err error) {
// collect info about flavors with separate instance quota
// (we are calling these "split flavors" here, as opposed to "pooled flavors" that share a common pool of CPU/instances/RAM capacity)
allSplitFlavorNames, err := p.FlavorAliases.ListFlavorsWithSeparateInstanceQuota(ctx, p.NovaV2, true) // true = ignore Ironic flavors
allSplitFlavorNames, _, err := p.FlavorAliases.ListFlavorsWithSeparateInstanceQuota(ctx, p.NovaV2, true) // true = ignore Ironic flavors
if err != nil {
return nil, nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion internal/plugins/capacity_sapcc_ironic.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ var cpNodeNameRx = regexp.MustCompile(`^node(?:swift)?\d+-(cp\d+)$`)
// Scrape implements the core.CapacityPlugin interface.
func (p *capacitySapccIronicPlugin) Scrape(ctx context.Context, _ core.CapacityPluginBackchannel, allAZs []limes.AvailabilityZone) (result map[db.ServiceType]map[liquid.ResourceName]core.PerAZ[core.CapacityData], serializedMetrics []byte, err error) {
// collect info about flavors with separate instance quota
flavorNames, err := p.FlavorAliases.ListFlavorsWithSeparateInstanceQuota(ctx, p.NovaV2, false) // false = do not ignore Ironic flavors
flavorNames, _, err := p.FlavorAliases.ListFlavorsWithSeparateInstanceQuota(ctx, p.NovaV2, false) // false = do not ignore Ironic flavors
if err != nil {
return nil, nil, err
}
Expand Down
18 changes: 14 additions & 4 deletions internal/plugins/nova.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"maps"
"math/big"
"regexp"
"slices"

"github.com/gophercloud/gophercloud/v2"
"github.com/gophercloud/gophercloud/v2/openstack"
Expand All @@ -50,8 +51,9 @@ type novaPlugin struct {
WithSubresources bool `yaml:"with_subresources"`
LiquidIronicCompatibilityMode bool `yaml:"liquid_ironic_compat_mode"` // NOTE: if true, assume that liquid-ironic is in use and ignore Ironic flavors
// computed state
resources map[liquid.ResourceName]liquid.ResourceInfo `yaml:"-"`
hasPooledResource map[string]map[liquid.ResourceName]bool `yaml:"-"`
resources map[liquid.ResourceName]liquid.ResourceInfo `yaml:"-"`
hasPooledResource map[string]map[liquid.ResourceName]bool `yaml:"-"`
ignoredFlavorNames []string `yaml:"-"`
// connections
NovaV2 *gophercloud.ServiceClient `yaml:"-"`
OSTypeProber *nova.OSTypeProber `yaml:"-"`
Expand Down Expand Up @@ -130,11 +132,11 @@ func (p *novaPlugin) Init(ctx context.Context, provider *gophercloud.ProviderCli
}

// find per-flavor instance resources
flavorNames, err := p.SeparateInstanceQuotas.FlavorAliases.ListFlavorsWithSeparateInstanceQuota(ctx, p.NovaV2, p.LiquidIronicCompatibilityMode)
splitFlavorNames, ignoredFlavorNames, err := p.SeparateInstanceQuotas.FlavorAliases.ListFlavorsWithSeparateInstanceQuota(ctx, p.NovaV2, p.LiquidIronicCompatibilityMode)
if err != nil {
return err
}
for _, flavorName := range flavorNames {
for _, flavorName := range splitFlavorNames {
if p.SeparateInstanceQuotas.FlavorNameSelection.MatchFlavorName(flavorName) {
resName := p.SeparateInstanceQuotas.FlavorAliases.LimesResourceNameForFlavor(flavorName)
p.resources[resName] = liquid.ResourceInfo{
Expand All @@ -143,6 +145,7 @@ func (p *novaPlugin) Init(ctx context.Context, provider *gophercloud.ProviderCli
}
}
}
p.ignoredFlavorNames = ignoredFlavorNames

return p.HypervisorTypeRules.Validate()
}
Expand Down Expand Up @@ -259,6 +262,9 @@ func (p *novaPlugin) Scrape(ctx context.Context, project core.KeystoneProject, a
},
}
for flavorName, flavorLimits := range limitsData.Limits.AbsolutePerFlavor {
if slices.Contains(p.ignoredFlavorNames, flavorName) {
continue
}
if p.SeparateInstanceQuotas.FlavorNameSelection.MatchFlavorName(flavorName) {
result[p.SeparateInstanceQuotas.FlavorAliases.LimesResourceNameForFlavor(flavorName)] = core.ResourceData{
Quota: flavorLimits.MaxTotalInstances,
Expand Down Expand Up @@ -298,6 +304,10 @@ func (p *novaPlugin) Scrape(ctx context.Context, project core.KeystoneProject, a
for _, subres := range allSubresources {
az := subres.AZ

if slices.Contains(p.ignoredFlavorNames, subres.FlavorName) {
continue
}

// use separate instance resource if we have a matching "instances_$FLAVOR" resource
instanceResourceName := p.SeparateInstanceQuotas.FlavorAliases.LimesResourceNameForFlavor(subres.FlavorName)
isPooled := false
Expand Down
14 changes: 9 additions & 5 deletions internal/plugins/nova/flavor_translate.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,17 +139,21 @@ func (t FlavorTranslationTable) NovaQuotaNameForLimesResourceName(resourceName l

// ListFlavorsWithSeparateInstanceQuota queries Nova for all separate instance
// quotas, and returns the flavor names that Nova prefers for each.
func (t FlavorTranslationTable) ListFlavorsWithSeparateInstanceQuota(ctx context.Context, computeV2 *gophercloud.ServiceClient, ignoreIronicFlavors bool) ([]string, error) {
var flavorNames []string
err := FlavorSelection{}.ForeachFlavor(ctx, computeV2, func(f flavors.Flavor) error {
//
// By default, the second return value (ignoredFlavorNames) will be empty.
// If ignoreIronicFlavors is given, it will contain the names of all Ironic flavors,
// which are then guaranteed to not be in the first list (splitFlavorNames).
func (t FlavorTranslationTable) ListFlavorsWithSeparateInstanceQuota(ctx context.Context, computeV2 *gophercloud.ServiceClient, ignoreIronicFlavors bool) (splitFlavorNames, ignoredFlavorNames []string, err error) {
err = FlavorSelection{}.ForeachFlavor(ctx, computeV2, func(f flavors.Flavor) error {
if ignoreIronicFlavors && f.ExtraSpecs["capabilities:hypervisor_type"] == "ironic" {
ignoredFlavorNames = append(ignoredFlavorNames, f.Name)
return nil
}
if f.ExtraSpecs["quota:separate"] == "true" {
flavorNames = append(flavorNames, f.Name)
splitFlavorNames = append(splitFlavorNames, f.Name)
t.recordNovaPreferredName(f.Name)
}
return nil
})
return flavorNames, err
return splitFlavorNames, ignoredFlavorNames, err
}

0 comments on commit 8b1b522

Please sign in to comment.