Skip to content

Commit

Permalink
liquid-nova: add translation rules for subcapacities and subresources
Browse files Browse the repository at this point in the history
  • Loading branch information
Varsius committed Jan 14, 2025
1 parent e2a4094 commit bcfaa15
Show file tree
Hide file tree
Showing 3 changed files with 271 additions and 46 deletions.
144 changes: 144 additions & 0 deletions internal/api/translation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,50 @@ func TestTranslateIronicSubcapacities(t *testing.T) {
testSubcapacityTranslation(t, "ironic-flavors", extraSetup, subcapacitiesInLiquidFormat, subcapacitiesInLegacyFormat)
}

func TestTranslateNovaSubcapacities(t *testing.T) {
subcapacitiesInLiquidFormat := []assert.JSONObject{
{
"name": "nova-compute-bb91",
"capacity": 448,
"usage": 1101,
"attributes": assert.JSONObject{
"aggregate_name": "vc-a-0",
"traits": []string{"COMPUTE_IMAGE_TYPE_ISO", "COMPUTE_IMAGE_TYPE_VMDK", "COMPUTE_NET_ATTACH_INTERFACE", "COMPUTE_NODE", "COMPUTE_RESCUE_BFV", "COMPUTE_SAME_HOST_COLD_MIGRATE", "CUSTOM_BIGVM_DISABLED"},
},
},
{
"name": "nova-compute-bb274",
"capacity": 104,
"usage": 315,
"attributes": assert.JSONObject{
"aggregate_name": "vc-a-1",
"traits": []string{"COMPUTE_IMAGE_TYPE_ISO", "COMPUTE_IMAGE_TYPE_VMDK", "COMPUTE_NET_ATTACH_INTERFACE", "COMPUTE_NODE", "COMPUTE_RESCUE_BFV", "COMPUTE_SAME_HOST_COLD_MIGRATE"},
},
},
}

subcapacitiesInLegacyFormat := []assert.JSONObject{
{
"service_host": "nova-compute-bb91",
"az": "az-one",
"aggregate": "vc-a-0",
"capacity": 448,
"usage": 1101,
"traits": []string{"COMPUTE_IMAGE_TYPE_ISO", "COMPUTE_IMAGE_TYPE_VMDK", "COMPUTE_NET_ATTACH_INTERFACE", "COMPUTE_NODE", "COMPUTE_RESCUE_BFV", "COMPUTE_SAME_HOST_COLD_MIGRATE", "CUSTOM_BIGVM_DISABLED"},
},
{
"service_host": "nova-compute-bb274",
"az": "az-one",
"aggregate": "vc-a-1",
"capacity": 104,
"usage": 315,
"traits": []string{"COMPUTE_IMAGE_TYPE_ISO", "COMPUTE_IMAGE_TYPE_VMDK", "COMPUTE_NET_ATTACH_INTERFACE", "COMPUTE_NODE", "COMPUTE_RESCUE_BFV", "COMPUTE_SAME_HOST_COLD_MIGRATE"},
},
}

testSubcapacityTranslation(t, "nova-flavors", nil, subcapacitiesInLiquidFormat, subcapacitiesInLegacyFormat)
}

func testSubcapacityTranslation(t *testing.T, ruleID string, extraSetup func(s *test.Setup), subcapacitiesInLiquidFormat, subcapacitiesInLegacyFormat []assert.JSONObject) {
s := test.NewSetup(t,
test.WithDBFixtureFile("fixtures/start-data-small.sql"),
Expand Down Expand Up @@ -346,6 +390,106 @@ func TestTranslateIronicSubresources(t *testing.T) {
testSubresourceTranslation(t, "ironic-flavors", extraSetup, subresourcesInLiquidFormat, subresourcesInLegacyFormat)
}

func TestTranslateNovaSubresources(t *testing.T) {
subresourcesInLiquidFormat := []assert.JSONObject{
{
"id": "c655dfeb-18fa-479d-b0bf-36cd63c2e901",
"name": "d042639-test-server",
"attributes": assert.JSONObject{
"status": "ACTIVE",
"metadata": assert.JSONObject{
"image_buildnumber": "",
"image_name": "SAP-compliant-ubuntu-24-04",
},
"tags": []string{},
"availability_zone": "az-one",
"flavor": assert.JSONObject{
"name": "g_c1_m2_v2",
"vcpu": 1,
"ram_mib": 2032,
"disk_gib": 64,
"video_ram_mib": 16,
},
"os_type": "image-deleted",
},
},
{
"id": "7cd0f695-75b5-4514-82a2-953237e4c7d6",
"name": "nsxt-e2e-test-vm-1",
"attributes": assert.JSONObject{
"status": "ACTIVE",
"metadata": assert.JSONObject{},
"tags": []string{},
"availability_zone": "az-one",
"flavor": assert.JSONObject{
"name": "g_c8_m16",
"vcpu": 8,
"ram_mib": 16368,
"disk_gib": 64,
"video_ram_mib": 16,
},
"os_type": "image-deleted",
},
},
}

subresourcesInLegacyFormat := []assert.JSONObject{
{
"id": "c655dfeb-18fa-479d-b0bf-36cd63c2e901",
"name": "d042639-test-server",
"status": "ACTIVE",
"metadata": assert.JSONObject{
"image_buildnumber": "",
"image_name": "SAP-compliant-ubuntu-24-04",
},
"tags": []string{},
"availability_zone": "az-one",
"hypervisor": "vmware",
"flavor": "g_c1_m2_v2",
"vcpu": 1,
"ram": assert.JSONObject{
"value": 2032,
"unit": "MiB",
},
"disk": assert.JSONObject{
"value": 64,
"unit": "GiB",
},
"video_ram": assert.JSONObject{
"value": 16,
"unit": "MiB",
},
"os_type": "image-deleted",
},
{
"id": "7cd0f695-75b5-4514-82a2-953237e4c7d6",
"name": "nsxt-e2e-test-vm-1",
"status": "ACTIVE",
"metadata": assert.JSONObject{},
"tags": []string{},
"availability_zone": "az-one",
"hypervisor": "vmware",
"flavor": "g_c8_m16",
"vcpu": 8,
"ram": assert.JSONObject{
"value": 16368,
"unit": "MiB",
},
"disk": assert.JSONObject{
"value": 64,
"unit": "GiB",
},
"video_ram": assert.JSONObject{
"value": 16,
"unit": "MiB",
},
"os_type": "image-deleted",
},
}

testSubresourceTranslation(t, "nova-flavors", nil, subresourcesInLiquidFormat, subresourcesInLegacyFormat)
}

func testSubresourceTranslation(t *testing.T, ruleID string, extraSetup func(s *test.Setup), subresourcesInLiquidFormat, subresourcesInLegacyFormat []assert.JSONObject) {
s := test.NewSetup(t,
test.WithDBFixtureFile("fixtures/start-data-small.sql"),
Expand Down
131 changes: 127 additions & 4 deletions internal/core/translation_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ func NewTranslationRule(id string) (TranslationRule, error) {
return TranslationRule{translateCinderOrManilaSubcapacities, nil}, nil
case "ironic-flavors":
return TranslationRule{translateIronicSubcapacities, translateIronicSubresources}, nil
case "nova-flavors":
return TranslationRule{translateNovaSubcapacities, translateNovaSubresources}, nil
default:
return TranslationRule{}, fmt.Errorf("no such TranslationRule: %q", id)
}
Expand Down Expand Up @@ -276,10 +278,6 @@ func translateIronicSubresources(input string, az limes.AvailabilityZone, resNam
return input, nil
}

if input == "" || input == "[]" {
return input, nil
}

var resAttrs struct {
Cores uint64 `json:"cores"`
MemoryMiB uint64 `json:"ram_mib"`
Expand Down Expand Up @@ -341,3 +339,128 @@ func translateIronicSubresources(input string, az limes.AvailabilityZone, resNam
buf, err := json.Marshal(outputs)
return string(buf), err
}

func translateNovaSubcapacities(input string, az limes.AvailabilityZone, _ liquid.ResourceName, resInfo liquid.ResourceInfo) (string, error) {
if input == "" || input == "[]" {
return input, nil
}

type BinpackVector[T float64 | uint64] struct {
VCPUs T `json:"vcpus"`
MemoryMB T `json:"memory_mib"`
LocalGB T `json:"local_disk_gib"`
}

type newFormat struct {
ID string `json:"id"`
Name string `json:"name"`
Capacity uint64 `json:"capacity"`
Usage *uint64 `json:"usage"`
Attributes struct {
AggregateName string `json:"aggregate_name"`
Traits []string `json:"traits"`
} `json:"attributes"`
}

var inputs []newFormat
err := json.Unmarshal([]byte(input), &inputs)
if err != nil {
return "", err
}

type oldFormat struct {
ServiceHost string `json:"service_host"`
AvailabilityZone limes.AvailabilityZone `json:"az"`
AggregateName string `json:"aggregate"`
Capacity *uint64 `json:"capacity,omitempty"`
Usage *uint64 `json:"usage,omitempty"`
CapacityVector *BinpackVector[uint64] `json:"capacity_vector,omitempty"`
UsageVector *BinpackVector[uint64] `json:"usage_vector,omitempty"`
Traits []string `json:"traits"`
}
outputs := make([]oldFormat, len(inputs))
for idx, in := range inputs {
out := oldFormat{
ServiceHost: in.Name,
AvailabilityZone: az,
AggregateName: in.Attributes.AggregateName,
Capacity: &in.Capacity,
Usage: in.Usage,
Traits: in.Attributes.Traits,
}
outputs[idx] = out
}
buf, err := json.Marshal(outputs)
return string(buf), err
}

func translateNovaSubresources(input string, az limes.AvailabilityZone, resName liquid.ResourceName, resInfo liquid.ResourceInfo) (string, error) {
if input == "" || input == "[]" {
return input, nil
}

type newFormat struct {
ID string `json:"id"`
Name string `json:"name"`
Attributes struct {
Status string `json:"status"`
Metadata map[string]string `json:"metadata"`
Tags []string `json:"tags"`
AZ liquid.AvailabilityZone `json:"availability_zone"`
Flavor struct {
Name string `json:"name"`
VCPUs uint64 `json:"vcpu"`
MemoryMiB uint64 `json:"ram_mib"`
DiskGiB uint64 `json:"disk_gib"`
VideoMemoryMiB *uint64 `json:"video_ram_mib"`
HWVersion string `json:"-"`
} `json:"flavor"`
OSType string `json:"os_type"`
}
}
var inputs []newFormat
err := json.Unmarshal([]byte(input), &inputs)
if err != nil {
return "", err
}

type oldFormat struct {
ID string `json:"id"`
Name string `json:"name"`
Status string `json:"status"`
Metadata map[string]string `json:"metadata"`
Tags []string `json:"tags"`
AZ limes.AvailabilityZone `json:"availability_zone"`
HypervisorType string `json:"hypervisor,omitempty"`
FlavorName string `json:"flavor"`
VCPUs uint64 `json:"vcpu"`
MemoryMiB limes.ValueWithUnit `json:"ram"`
DiskGiB limes.ValueWithUnit `json:"disk"`
VideoMemoryMiB *limes.ValueWithUnit `json:"video_ram,omitempty"`
HWVersion string `json:"-"`
OSType string `json:"os_type"`
}
outputs := make([]oldFormat, len(inputs))
for idx, in := range inputs {
out := oldFormat{
ID: in.ID,
Name: in.Name,
Status: in.Attributes.Status,
Metadata: in.Attributes.Metadata,
Tags: in.Attributes.Tags,
AZ: in.Attributes.AZ,
HypervisorType: "vmware",
FlavorName: in.Attributes.Flavor.Name,
VCPUs: in.Attributes.Flavor.VCPUs,
MemoryMiB: limes.ValueWithUnit{Unit: limes.UnitMebibytes, Value: in.Attributes.Flavor.MemoryMiB},
DiskGiB: limes.ValueWithUnit{Unit: limes.UnitGibibytes, Value: in.Attributes.Flavor.DiskGiB},
OSType: in.Attributes.OSType,
}
if in.Attributes.Flavor.VideoMemoryMiB != nil {
out.VideoMemoryMiB = &limes.ValueWithUnit{Unit: limes.UnitMebibytes, Value: *in.Attributes.Flavor.VideoMemoryMiB}
}
outputs[idx] = out
}
buf, err := json.Marshal(outputs)
return string(buf), err
}
42 changes: 0 additions & 42 deletions internal/liquids/nova/hypervisor_subcapacity.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,45 +161,3 @@ func (b *DeprecatedSplitFlavorSubcapacityBuilder) AddHypervisor(h MatchingHyperv
Traits: h.Traits,
})
}

// SplitSubcapacityBuilder is used to build subcapacity lists for split flavors.
// These subcapacities are reported on the first flavor in alphabetic order.
type SplitFlavorSubcapacityBuilder struct {
Subcapacities []liquid.Subcapacity
}

type SplitFlavorSubcapacityAttributes struct {
AggregateName string `json:"aggregate_name"`
CapacityVector *BinpackVector[uint64] `json:"capacity_vector,omitempty"`
UsageVector *BinpackVector[uint64] `json:"usage_vector,omitempty"`
Traits []string `json:"traits"`
}

func (b *SplitFlavorSubcapacityBuilder) AddHypervisor(h MatchingHypervisor) error {
pc := h.PartialCapacity()
subcapBuilder := liquid.SubcapacityBuilder[SplitFlavorSubcapacityAttributes]{
Name: h.Hypervisor.Service.Host,
Capacity: 0,
Attributes: SplitFlavorSubcapacityAttributes{
AggregateName: h.AggregateName,
CapacityVector: &BinpackVector[uint64]{
VCPUs: pc.VCPUs.Capacity,
MemoryMB: pc.MemoryMB.Capacity,
LocalGB: pc.LocalGB.Capacity,
},
UsageVector: &BinpackVector[uint64]{
VCPUs: pc.VCPUs.Usage,
MemoryMB: pc.MemoryMB.Usage,
LocalGB: pc.LocalGB.Usage,
},
Traits: h.Traits,
},
}
subcap, err := subcapBuilder.Finalize()
if err != nil {
return fmt.Errorf("could not serialze attributes of subcapacity: %w", err)
}
b.Subcapacities = append(b.Subcapacities, subcap)

return nil
}

0 comments on commit bcfaa15

Please sign in to comment.