diff --git a/builder/nutanix/config.go b/builder/nutanix/config.go index 561f2d5..6ba30ea 100644 --- a/builder/nutanix/config.go +++ b/builder/nutanix/config.go @@ -1,4 +1,4 @@ -//go:generate packer-sdc mapstructure-to-hcl2 -type Config,Category,ClusterConfig,VmConfig,VmDisk,VmNIC +//go:generate packer-sdc mapstructure-to-hcl2 -type Config,Category,ClusterConfig,VmConfig,VmDisk,VmNIC,GPU package nutanix @@ -45,6 +45,11 @@ type Config struct { ctx interpolate.Context } +type GPU struct { + Name string `mapstructure:"name" json:"name" required:"false"` + Mode string `mapstructure:"mode" json:"mode" required:"false"` +} + type Category struct { Key string `mapstructure:"key" json:"key" required:"false"` Value string `mapstructure:"value" json:"value" required:"false"` @@ -86,6 +91,7 @@ type VmConfig struct { UserData string `mapstructure:"user_data" json:"user_data" required:"false"` VMCategories []Category `mapstructure:"vm_categories" required:"false"` Project string `mapstructure:"project" required:"false"` + GPU []GPU `mapstructure:"gpu" required:"false"` } func (c *Config) Prepare(raws ...interface{}) ([]string, error) { diff --git a/builder/nutanix/config.hcl2spec.go b/builder/nutanix/config.hcl2spec.go index 2c48d70..c2ab752 100644 --- a/builder/nutanix/config.hcl2spec.go +++ b/builder/nutanix/config.hcl2spec.go @@ -146,6 +146,7 @@ type FlatConfig struct { UserData *string `mapstructure:"user_data" json:"user_data" required:"false" cty:"user_data" hcl:"user_data"` VMCategories []FlatCategory `mapstructure:"vm_categories" required:"false" cty:"vm_categories" hcl:"vm_categories"` Project *string `mapstructure:"project" required:"false" cty:"project" hcl:"project"` + GPU []FlatGPU `mapstructure:"gpu" required:"false" cty:"gpu" hcl:"gpu"` ForceDeregister *bool `mapstructure:"force_deregister" json:"force_deregister" required:"false" cty:"force_deregister" hcl:"force_deregister"` ImageDescription *string `mapstructure:"image_description" json:"image_description" required:"false" cty:"image_description" hcl:"image_description"` ImageCategories []FlatCategory `mapstructure:"image_categories" required:"false" cty:"image_categories" hcl:"image_categories"` @@ -247,6 +248,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, "vm_categories": &hcldec.BlockListSpec{TypeName: "vm_categories", Nested: hcldec.ObjectSpec((*FlatCategory)(nil).HCL2Spec())}, "project": &hcldec.AttrSpec{Name: "project", Type: cty.String, Required: false}, + "gpu": &hcldec.BlockListSpec{TypeName: "gpu", Nested: hcldec.ObjectSpec((*FlatGPU)(nil).HCL2Spec())}, "force_deregister": &hcldec.AttrSpec{Name: "force_deregister", Type: cty.Bool, Required: false}, "image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false}, "image_categories": &hcldec.BlockListSpec{TypeName: "image_categories", Nested: hcldec.ObjectSpec((*FlatCategory)(nil).HCL2Spec())}, @@ -258,6 +260,31 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { return s } +// FlatGPU is an auto-generated flat version of GPU. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatGPU struct { + Name *string `mapstructure:"name" json:"name" required:"false" cty:"name" hcl:"name"` + Mode *string `mapstructure:"mode" json:"mode" required:"false" cty:"mode" hcl:"mode"` +} + +// FlatMapstructure returns a new FlatGPU. +// FlatGPU is an auto-generated flat version of GPU. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*GPU) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatGPU) +} + +// HCL2Spec returns the hcl spec of a GPU. +// This spec is used by HCL to read the fields of GPU. +// The decoded values from this spec will then be applied to a FlatGPU. +func (*FlatGPU) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "name": &hcldec.AttrSpec{Name: "name", Type: cty.String, Required: false}, + "mode": &hcldec.AttrSpec{Name: "mode", Type: cty.String, Required: false}, + } + return s +} + // FlatVmConfig is an auto-generated flat version of VmConfig. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. type FlatVmConfig struct { @@ -274,6 +301,7 @@ type FlatVmConfig struct { UserData *string `mapstructure:"user_data" json:"user_data" required:"false" cty:"user_data" hcl:"user_data"` VMCategories []FlatCategory `mapstructure:"vm_categories" required:"false" cty:"vm_categories" hcl:"vm_categories"` Project *string `mapstructure:"project" required:"false" cty:"project" hcl:"project"` + GPU []FlatGPU `mapstructure:"gpu" required:"false" cty:"gpu" hcl:"gpu"` } // FlatMapstructure returns a new FlatVmConfig. @@ -301,6 +329,7 @@ func (*FlatVmConfig) HCL2Spec() map[string]hcldec.Spec { "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, "vm_categories": &hcldec.BlockListSpec{TypeName: "vm_categories", Nested: hcldec.ObjectSpec((*FlatCategory)(nil).HCL2Spec())}, "project": &hcldec.AttrSpec{Name: "project", Type: cty.String, Required: false}, + "gpu": &hcldec.BlockListSpec{TypeName: "gpu", Nested: hcldec.ObjectSpec((*FlatGPU)(nil).HCL2Spec())}, } return s } diff --git a/builder/nutanix/driver.go b/builder/nutanix/driver.go index 72f69af..3753b4e 100644 --- a/builder/nutanix/driver.go +++ b/builder/nutanix/driver.go @@ -151,6 +151,36 @@ func findSubnetByName(conn *v3.Client, name string) (*v3.SubnetIntentResponse, e return found[0], nil } +func findGPUByName(conn *v3.Client, name string) (*v3.VMGpu, error) { + hosts, err := conn.V3.ListAllHost() + if err != nil { + return nil, err + } + + for _, host := range hosts.Entities { + if host == nil || + host.Status == nil || + host.Status.ClusterReference == nil || + host.Status.Resources == nil || + len(host.Status.Resources.GPUList) == 0 { + continue + } + + for _, peGpu := range host.Status.Resources.GPUList { + if peGpu == nil { + continue + } + if peGpu.Name == name { + return &v3.VMGpu{ + DeviceID: peGpu.DeviceID, + Vendor: &peGpu.Vendor, + }, nil + } + } + } + return nil, fmt.Errorf("failed to find GPU %s", name) +} + func sourceImageExists(conn *v3.Client, name string, uri string) (*v3.ImageIntentResponse, error) { filter := fmt.Sprintf("name==%s", name) resp, err := conn.V3.ListAllImage(filter) @@ -441,6 +471,16 @@ func (d *NutanixDriver) CreateRequest(vm VmConfig, state multistep.StateBag) (*v } NICList = append(NICList, &newNIC) } + GPUList := make([]*v3.VMGpu, 0, len(vm.GPU)) + for _, gpu := range vm.GPU { + vmGPU, err := findGPUByName(conn, gpu.Name) + if err != nil { + return nil, fmt.Errorf("error while findGPUByName %s", err.Error()) + } + vmGPU.Mode = &gpu.Mode + GPUList = append(GPUList, vmGPU) + } + PowerStateOn := "ON" cluster := &v3.ClusterIntentResponse{} @@ -466,6 +506,7 @@ func (d *NutanixDriver) CreateRequest(vm VmConfig, state multistep.StateBag) (*v PowerState: &PowerStateOn, DiskList: DiskList, NicList: NICList, + GpuList: GPUList, }, ClusterReference: BuildReference(*cluster.Metadata.UUID, "cluster"), Description: StringPtr(fmt.Sprintf(vmDescription, d.Config.VmConfig.ImageName)),