diff --git a/liquidapi/constants.go b/liquidapi/constants.go index 372d761..fd0b19f 100644 --- a/liquidapi/constants.go +++ b/liquidapi/constants.go @@ -8,7 +8,7 @@ import ( ) // The following list of regexes is derived from a hardcoded list of valid values -// for an image's "vmware_ostype" attribute in Nova/Glance. This list was copied from +// for an image's "vmware_ostype" and "kvm_ostype" attributes in Nova/Glance. This list was copied from // https://github.com/openstack/nova/blob/master/nova/virt/vmwareapi/constants.py // and the comment over there says: // @@ -21,9 +21,9 @@ import ( // OSTypeProber. Therefore, we replaced all obvious version numbers in the valid // values with placeholders, so that this does not break as easily in the future. // We do not recommend utilizing this regex list for anything else other than -// validating the "vmware_ostype" attribute on existing images. +// validating the "vmware_ostype" and "kvm_ostype" attributes on existing images. -var isValidVMwareOSTypeRegex = []*regexp.Regexp{ +var isValidOSTypeRegex = []*regexp.Regexp{ regexp.MustCompile(`^almalinux_64Guest$`), regexp.MustCompile(`^amazonlinux(\d+)_64Guest$`), regexp.MustCompile(`^asianux(\d+)_64Guest$`), diff --git a/liquidapi/constants_test.go b/liquidapi/constants_test.go index e175441..f4bcffb 100644 --- a/liquidapi/constants_test.go +++ b/liquidapi/constants_test.go @@ -12,7 +12,7 @@ import ( // so that we ensure our regexes catch all cases. Version updates should not // break this, only "net-new" OS types will break the test. -func TestIsValidVMwareOSType(t *testing.T) { +func TestIsValidOSType(t *testing.T) { var versionsToCheck = []string{ "almalinux_64Guest", "amazonlinux2_64Guest", @@ -211,12 +211,12 @@ func TestIsValidVMwareOSType(t *testing.T) { } for _, versionToCheck := range versionsToCheck { t.Run(versionToCheck, func(t *testing.T) { - for _, regex := range isValidVMwareOSTypeRegex { + for _, regex := range isValidOSTypeRegex { if regex.MatchString(versionToCheck) { return } } - t.Fatal(`version "` + versionToCheck + `" is not valid according to any of the regexes in isValidVMwareOSTypeRegex, but it should be`) + t.Fatal(`version "` + versionToCheck + `" is not valid according to any of the regexes in isValidOSTypeRegex, but it should be`) }) } } diff --git a/liquidapi/ostype_prober.go b/liquidapi/ostype_prober.go index 204715c..dde63b5 100644 --- a/liquidapi/ostype_prober.go +++ b/liquidapi/ostype_prober.go @@ -28,6 +28,8 @@ import ( // - "image-unknown", if no valid image reference exists in the instance metadata // - "image-deleted", if the image has been deleted since the instance was booted // - the value in the "vmware_ostype" attribute on the image metadata, if that field exists and the value is valid +// - the value in the "kvm_ostype" attribute on the image metadata, if that field exists and the value is valid +// - the value in the "properties.os_distro" attribute on the image metadata, if that field exists and the value is valid // - "$TYPE", if the image metadata contains a tag of the form "ostype:$TYPE" // - "unknown", if no other rule matches // @@ -37,6 +39,7 @@ import ( // - "rootdisk-missing", if the boot volume has an empty ID // - "rootdisk-inspect-error", if the boot volume cannot be located or if its metadata cannot be inspected in Glance // - the value in the "volume_image_metadata.vmware_ostype" attribute on the volume metadata, if that field exists and the value is valid +// - the value in the "volume_image_metadata.kvm_ostype" attribute on the volume metadata, if that field exists and the value is valid // - "unknown", if no other rule matches type OSTypeProber struct { // caches for repeated queries @@ -150,6 +153,7 @@ func (p *OSTypeProber) findFromBootVolume(ctx context.Context, instanceID string var volume struct { ImageMetadata struct { VMwareOSType string `json:"vmware_ostype"` + KVMOSType string `json:"kvm_ostype"` } `json:"volume_image_metadata"` } err = volumes.Get(ctx, p.CinderV3, rootVolumeID).ExtractInto(&volume) @@ -157,11 +161,16 @@ func (p *OSTypeProber) findFromBootVolume(ctx context.Context, instanceID string return "", err } - for _, regex := range isValidVMwareOSTypeRegex { + for _, regex := range isValidOSTypeRegex { if regex.MatchString(volume.ImageMetadata.VMwareOSType) { return volume.ImageMetadata.VMwareOSType, nil } } + for _, regex := range isValidOSTypeRegex { + if regex.MatchString(volume.ImageMetadata.KVMOSType) { + return volume.ImageMetadata.KVMOSType, nil + } + } return "unknown", nil } @@ -169,6 +178,7 @@ func (p *OSTypeProber) findFromImage(ctx context.Context, imageID string) (strin var result struct { Tags []string `json:"tags"` VMwareOSType string `json:"vmware_ostype"` + KVMOSType string `json:"kvm_ostype"` Properties map[string]any `json:"properties"` } err := images.Get(ctx, p.GlanceV2, imageID).ExtractInto(&result) @@ -182,15 +192,22 @@ func (p *OSTypeProber) findFromImage(ctx context.Context, imageID string) (strin } // prefer vmware_ostype attribute since this is validated by Nova upon booting the VM - for _, regex := range isValidVMwareOSTypeRegex { + for _, regex := range isValidOSTypeRegex { if regex.MatchString(result.VMwareOSType) { return result.VMwareOSType, nil } } + // fall back to kvm_ostype if vmware_ostype is not set + for _, regex := range isValidOSTypeRegex { + if regex.MatchString(result.KVMOSType) { + return result.KVMOSType, nil + } + } + // on some images, vmware_ostype values are maintained as properties.os_distro instead if osDistroStr, ok := result.Properties["os_distro"].(string); ok { - for _, regex := range isValidVMwareOSTypeRegex { + for _, regex := range isValidOSTypeRegex { if regex.MatchString(osDistroStr) { return osDistroStr, nil }