0
0
mirror of https://github.com/bpg/terraform-provider-proxmox.git synced 2025-08-22 19:38:35 +00:00

feat(vm): add support for SMBIOS settings (#454)

* feat(vm): add support for SMBIOS settings

* fix linter errors

* fix smbios error at clone
This commit is contained in:
Pavel Boldyrev 2023-08-05 20:31:17 -04:00 committed by GitHub
parent 824e51c650
commit 85ff60d4bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 247 additions and 6 deletions

View File

@ -414,6 +414,14 @@ output "ubuntu_vm_public_key" {
- `virtio-scsi-single` - VirtIO SCSI (single queue).
- `megasas` - LSI Logic MegaRAID SAS.
- `pvscsi` - VMware Paravirtual SCSI.
- `smbios` - (Optional) The SMBIOS (type1) settings for the VM.
- `family`- (Optional) The family string.
- `manufacturer` - (Optional) The manufacturer.
- `product` - (Optional) The product ID.
- `serial` - (Optional) The serial number.
- `sku` - (Optional) The SKU number.
- `uuid` - (Optional) The UUID (defaults to randomly generated UUID).
- `version` - (Optional) The version.
- `started` - (Optional) Whether to start the virtual machine (defaults
to `true`).
- `startup` - (Optional) Defines startup and shutdown behavior of the VM.

View File

@ -13,6 +13,12 @@ resource "proxmox_virtual_environment_vm" "example_template" {
numa = true
}
smbios {
manufacturer = "Terraform"
product = "Terraform Provider Proxmox"
version = "0.0.1"
}
startup {
order = "3"
up_delay = "60"

View File

@ -140,7 +140,7 @@ type CustomSharedMemory struct {
// CustomSMBIOS handles QEMU SMBIOS parameters.
type CustomSMBIOS struct {
Base64 *types.CustomBool `json:"base64,omitempty" url:"base64,omitempty"`
Base64 *types.CustomBool `json:"base64,omitempty" url:"base64,omitempty,int"`
Family *string `json:"family,omitempty" url:"family,omitempty"`
Manufacturer *string `json:"manufacturer,omitempty" url:"manufacturer,omitempty"`
Product *string `json:"product,omitempty" url:"product,omitempty"`
@ -1673,7 +1673,7 @@ func (r *CustomSMBIOS) UnmarshalJSON(b []byte) error {
pairs := strings.Split(s, ",")
for _, p := range pairs {
v := strings.Split(strings.TrimSpace(p), "=")
v := strings.SplitN(strings.TrimSpace(p), "=", 2)
if len(v) == 2 {
switch v[0] {

View File

@ -8,6 +8,7 @@ package resource
import (
"context"
"encoding/base64"
"errors"
"fmt"
"sort"
@ -16,6 +17,7 @@ import (
"time"
"unicode"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
@ -103,6 +105,12 @@ const (
dvResourceVirtualEnvironmentVMOperatingSystemType = "other"
dvResourceVirtualEnvironmentVMPoolID = ""
dvResourceVirtualEnvironmentVMSerialDeviceDevice = "socket"
dvResourceVirtualEnvironmentVMSMBIOSFamily = ""
dvResourceVirtualEnvironmentVMSMBIOSManufacturer = ""
dvResourceVirtualEnvironmentVMSMBIOSProduct = ""
dvResourceVirtualEnvironmentVMSMBIOSSKU = ""
dvResourceVirtualEnvironmentVMSMBIOSSerial = ""
dvResourceVirtualEnvironmentVMSMBIOSVersion = ""
dvResourceVirtualEnvironmentVMStarted = true
dvResourceVirtualEnvironmentVMStartupOrder = -1
dvResourceVirtualEnvironmentVMStartupUpDelay = -1
@ -234,6 +242,14 @@ const (
mkResourceVirtualEnvironmentVMPoolID = "pool_id"
mkResourceVirtualEnvironmentVMSerialDevice = "serial_device"
mkResourceVirtualEnvironmentVMSerialDeviceDevice = "device"
mkResourceVirtualEnvironmentVMSMBIOS = "smbios"
mkResourceVirtualEnvironmentVMSMBIOSFamily = "family"
mkResourceVirtualEnvironmentVMSMBIOSManufacturer = "manufacturer"
mkResourceVirtualEnvironmentVMSMBIOSProduct = "product"
mkResourceVirtualEnvironmentVMSMBIOSSKU = "sku"
mkResourceVirtualEnvironmentVMSMBIOSSerial = "serial"
mkResourceVirtualEnvironmentVMSMBIOSUUID = "uuid"
mkResourceVirtualEnvironmentVMSMBIOSVersion = "version"
mkResourceVirtualEnvironmentVMStarted = "started"
mkResourceVirtualEnvironmentVMStartup = "startup"
mkResourceVirtualEnvironmentVMStartupOrder = "order"
@ -1212,6 +1228,59 @@ func VM() *schema.Resource {
MaxItems: maxResourceVirtualEnvironmentVMSerialDevices,
MinItems: 0,
},
mkResourceVirtualEnvironmentVMSMBIOS: {
Type: schema.TypeList,
Description: "Specifies SMBIOS (type1) settings for the VM",
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
mkResourceVirtualEnvironmentVMSMBIOSFamily: {
Type: schema.TypeString,
Description: "Sets SMBIOS family string",
Optional: true,
Default: dvResourceVirtualEnvironmentVMSMBIOSFamily,
},
mkResourceVirtualEnvironmentVMSMBIOSManufacturer: {
Type: schema.TypeString,
Description: "Sets SMBIOS manufacturer",
Optional: true,
Default: dvResourceVirtualEnvironmentVMSMBIOSManufacturer,
},
mkResourceVirtualEnvironmentVMSMBIOSProduct: {
Type: schema.TypeString,
Description: "Sets SMBIOS product ID",
Optional: true,
Default: dvResourceVirtualEnvironmentVMSMBIOSProduct,
},
mkResourceVirtualEnvironmentVMSMBIOSSerial: {
Type: schema.TypeString,
Description: "Sets SMBIOS serial number",
Optional: true,
Default: dvResourceVirtualEnvironmentVMSMBIOSSerial,
},
mkResourceVirtualEnvironmentVMSMBIOSSKU: {
Type: schema.TypeString,
Description: "Sets SMBIOS SKU",
Optional: true,
Default: dvResourceVirtualEnvironmentVMSMBIOSSKU,
},
mkResourceVirtualEnvironmentVMSMBIOSUUID: {
Type: schema.TypeString,
Description: "Sets SMBIOS UUID",
Optional: true,
Computed: true,
},
mkResourceVirtualEnvironmentVMSMBIOSVersion: {
Type: schema.TypeString,
Description: "Sets SMBIOS version",
Optional: true,
Default: dvResourceVirtualEnvironmentVMSMBIOSVersion,
},
},
},
MaxItems: 1,
MinItems: 0,
},
mkResourceVirtualEnvironmentVMStarted: {
Type: schema.TypeBool,
Description: "Whether to start the virtual machine",
@ -1252,7 +1321,6 @@ func VM() *schema.Resource {
MaxItems: 1,
MinItems: 0,
},
mkResourceVirtualEnvironmentVMTabletDevice: {
Type: schema.TypeBool,
Description: "Whether to enable the USB tablet device",
@ -2189,6 +2257,8 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
serialDevices := vmGetSerialDeviceList(d)
smbios := vmGetSMBIOS(d)
startupOrder := vmGetStartupOrder(d)
onBoot := types.CustomBool(d.Get(mkResourceVirtualEnvironmentVMOnBoot).(bool))
@ -2311,6 +2381,7 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
SerialDevices: serialDevices,
SharedMemory: memorySharedObject,
StartOnBoot: &onBoot,
SMBIOS: smbios,
StartupOrder: startupOrder,
TabletDeviceEnabled: &tabletDevice,
Template: &template,
@ -3053,6 +3124,68 @@ func vmGetSerialDeviceList(d *schema.ResourceData) vms.CustomSerialDevices {
return list
}
func vmGetSMBIOS(d *schema.ResourceData) *vms.CustomSMBIOS {
smbiosSections := d.Get(mkResourceVirtualEnvironmentVMSMBIOS).([]interface{})
//nolint:nestif
if len(smbiosSections) > 0 {
smbiosBlock := smbiosSections[0].(map[string]interface{})
b64 := types.CustomBool(true)
family, _ := smbiosBlock[mkResourceVirtualEnvironmentVMSMBIOSFamily].(string)
manufacturer, _ := smbiosBlock[mkResourceVirtualEnvironmentVMSMBIOSManufacturer].(string)
product, _ := smbiosBlock[mkResourceVirtualEnvironmentVMSMBIOSProduct].(string)
serial, _ := smbiosBlock[mkResourceVirtualEnvironmentVMSMBIOSSerial].(string)
sku, _ := smbiosBlock[mkResourceVirtualEnvironmentVMSMBIOSSKU].(string)
version, _ := smbiosBlock[mkResourceVirtualEnvironmentVMSMBIOSVersion].(string)
uid, _ := smbiosBlock[mkResourceVirtualEnvironmentVMSMBIOSUUID].(string)
smbios := vms.CustomSMBIOS{
Base64: &b64,
}
if family != "" {
v := base64.StdEncoding.EncodeToString([]byte(family))
smbios.Family = &v
}
if manufacturer != "" {
v := base64.StdEncoding.EncodeToString([]byte(manufacturer))
smbios.Manufacturer = &v
}
if product != "" {
v := base64.StdEncoding.EncodeToString([]byte(product))
smbios.Product = &v
}
if serial != "" {
v := base64.StdEncoding.EncodeToString([]byte(serial))
smbios.Serial = &v
}
if sku != "" {
v := base64.StdEncoding.EncodeToString([]byte(sku))
smbios.SKU = &v
}
if version != "" {
v := base64.StdEncoding.EncodeToString([]byte(version))
smbios.Version = &v
}
if uid != "" {
smbios.UUID = &uid
}
if smbios.UUID == nil || *smbios.UUID == "" {
smbios.UUID = types.StrPtr(uuid.New().String())
}
return &smbios
}
return nil
}
func vmGetStartupOrder(d *schema.ResourceData) *vms.CustomStartupOrder {
startup := d.Get(mkResourceVirtualEnvironmentVMStartup).([]interface{})
if len(startup) > 0 {
@ -4137,6 +4270,90 @@ func vmReadCustom(
diags = append(diags, diag.FromErr(err)...)
}
// Compare the SMBIOS to the one stored in the state.
var smbios map[string]interface{}
//nolint:nestif
if vmConfig.SMBIOS != nil {
smbios = map[string]interface{}{}
if vmConfig.SMBIOS.Family != nil {
b, err := base64.StdEncoding.DecodeString(*vmConfig.SMBIOS.Family)
diags = append(diags, diag.FromErr(err)...)
smbios[mkResourceVirtualEnvironmentVMSMBIOSFamily] = string(b)
} else {
smbios[mkResourceVirtualEnvironmentVMSMBIOSFamily] = dvResourceVirtualEnvironmentVMSMBIOSFamily
}
if vmConfig.SMBIOS.Manufacturer != nil {
b, err := base64.StdEncoding.DecodeString(*vmConfig.SMBIOS.Manufacturer)
diags = append(diags, diag.FromErr(err)...)
smbios[mkResourceVirtualEnvironmentVMSMBIOSManufacturer] = string(b)
} else {
smbios[mkResourceVirtualEnvironmentVMSMBIOSManufacturer] = dvResourceVirtualEnvironmentVMSMBIOSManufacturer
}
if vmConfig.SMBIOS.Product != nil {
b, err := base64.StdEncoding.DecodeString(*vmConfig.SMBIOS.Product)
diags = append(diags, diag.FromErr(err)...)
smbios[mkResourceVirtualEnvironmentVMSMBIOSProduct] = string(b)
} else {
smbios[mkResourceVirtualEnvironmentVMSMBIOSProduct] = dvResourceVirtualEnvironmentVMSMBIOSProduct
}
if vmConfig.SMBIOS.Serial != nil {
b, err := base64.StdEncoding.DecodeString(*vmConfig.SMBIOS.Serial)
diags = append(diags, diag.FromErr(err)...)
smbios[mkResourceVirtualEnvironmentVMSMBIOSSerial] = string(b)
} else {
smbios[mkResourceVirtualEnvironmentVMSMBIOSSerial] = dvResourceVirtualEnvironmentVMSMBIOSSerial
}
if vmConfig.SMBIOS.SKU != nil {
b, err := base64.StdEncoding.DecodeString(*vmConfig.SMBIOS.SKU)
diags = append(diags, diag.FromErr(err)...)
smbios[mkResourceVirtualEnvironmentVMSMBIOSSKU] = string(b)
} else {
smbios[mkResourceVirtualEnvironmentVMSMBIOSSKU] = dvResourceVirtualEnvironmentVMSMBIOSSKU
}
if vmConfig.SMBIOS.Version != nil {
b, err := base64.StdEncoding.DecodeString(*vmConfig.SMBIOS.Version)
diags = append(diags, diag.FromErr(err)...)
smbios[mkResourceVirtualEnvironmentVMSMBIOSVersion] = string(b)
} else {
smbios[mkResourceVirtualEnvironmentVMSMBIOSVersion] = dvResourceVirtualEnvironmentVMSMBIOSVersion
}
if vmConfig.SMBIOS.UUID != nil {
smbios[mkResourceVirtualEnvironmentVMSMBIOSUUID] = *vmConfig.SMBIOS.UUID
} else {
smbios[mkResourceVirtualEnvironmentVMSMBIOSUUID] = nil
}
}
currentSMBIOS := d.Get(mkResourceVirtualEnvironmentVMSMBIOS).([]interface{})
//nolint:gocritic
if len(clone) > 0 {
if len(currentSMBIOS) > 0 {
err := d.Set(mkResourceVirtualEnvironmentVMSMBIOS, currentSMBIOS)
diags = append(diags, diag.FromErr(err)...)
}
} else if len(smbios) == 0 {
err := d.Set(mkResourceVirtualEnvironmentVMSMBIOS, []interface{}{})
diags = append(diags, diag.FromErr(err)...)
} else if len(currentSMBIOS) > 0 ||
smbios[mkResourceVirtualEnvironmentVMSMBIOSFamily] != dvResourceVirtualEnvironmentVMSMBIOSFamily ||
smbios[mkResourceVirtualEnvironmentVMSMBIOSManufacturer] != dvResourceVirtualEnvironmentVMSMBIOSManufacturer ||
smbios[mkResourceVirtualEnvironmentVMSMBIOSProduct] != dvResourceVirtualEnvironmentVMSMBIOSProduct ||
smbios[mkResourceVirtualEnvironmentVMSMBIOSSerial] != dvResourceVirtualEnvironmentVMSMBIOSSerial ||
smbios[mkResourceVirtualEnvironmentVMSMBIOSSKU] != dvResourceVirtualEnvironmentVMSMBIOSSKU ||
smbios[mkResourceVirtualEnvironmentVMSMBIOSVersion] != dvResourceVirtualEnvironmentVMSMBIOSVersion {
err := d.Set(mkResourceVirtualEnvironmentVMSMBIOS, []interface{}{smbios})
diags = append(diags, diag.FromErr(err)...)
}
// Compare the startup order to the one stored in the state.
var startup map[string]interface{}
@ -4171,15 +4388,15 @@ func vmReadCustom(
err := d.Set(mkResourceVirtualEnvironmentVMStartup, []interface{}{startup})
diags = append(diags, diag.FromErr(err)...)
}
} else if len(startup) == 0 {
err := d.Set(mkResourceVirtualEnvironmentVMStartup, []interface{}{})
diags = append(diags, diag.FromErr(err)...)
} else if len(currentStartup) > 0 ||
startup[mkResourceVirtualEnvironmentVMStartupOrder] != mkResourceVirtualEnvironmentVMStartupOrder ||
startup[mkResourceVirtualEnvironmentVMStartupUpDelay] != dvResourceVirtualEnvironmentVMStartupUpDelay ||
startup[mkResourceVirtualEnvironmentVMStartupDownDelay] != dvResourceVirtualEnvironmentVMStartupDownDelay {
err := d.Set(mkResourceVirtualEnvironmentVMStartup, []interface{}{startup})
diags = append(diags, diag.FromErr(err)...)
} else {
err := d.Set(mkResourceVirtualEnvironmentVMStartup, []interface{}{})
diags = append(diags, diag.FromErr(err)...)
}
// Compare the VGA configuration to the one stored in the state.
@ -4955,8 +5172,18 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
rebootRequired = true
}
if d.HasChange(mkResourceVirtualEnvironmentVMSMBIOS) {
updateBody.SMBIOS = vmGetSMBIOS(d)
if updateBody.SMBIOS == nil {
del = append(del, "smbios1")
}
}
if d.HasChange(mkResourceVirtualEnvironmentVMStartup) {
updateBody.StartupOrder = vmGetStartupOrder(d)
if updateBody.StartupOrder == nil {
del = append(del, "startup")
}
}
// Prepare the new VGA configuration.