diff --git a/builder/nutanix/config.go b/builder/nutanix/config.go index fb0fd57..b3a3272 100644 --- a/builder/nutanix/config.go +++ b/builder/nutanix/config.go @@ -35,7 +35,7 @@ type Config struct { ClusterConfig `mapstructure:",squash"` VmConfig `mapstructure:",squash"` ForceDeregister bool `mapstructure:"force_deregister" json:"force_deregister" required:"false"` - + ctx interpolate.Context } @@ -52,6 +52,7 @@ type VmDisk struct { ImageType string `mapstructure:"image_type" json:"image_type" required:"false"` SourceImageName string `mapstructure:"source_image_name" json:"source_image_name" required:"false"` SourceImageUUID string `mapstructure:"source_image_uuid" json:"source_image_uuid" required:"false"` + SourceImageURI string `mapstructure:"source_image_uri" json:"source_image_uri" required:"false"` DiskSizeGB int64 `mapstructure:"disk_size_gb" json:"disk_size_gb" required:"false"` } diff --git a/builder/nutanix/config.hcl2spec.go b/builder/nutanix/config.hcl2spec.go index e73b29e..c60ad22 100644 --- a/builder/nutanix/config.hcl2spec.go +++ b/builder/nutanix/config.hcl2spec.go @@ -266,6 +266,7 @@ type FlatVmDisk struct { ImageType *string `mapstructure:"image_type" json:"image_type" required:"false" cty:"image_type" hcl:"image_type"` SourceImageName *string `mapstructure:"source_image_name" json:"source_image_name" required:"false" cty:"source_image_name" hcl:"source_image_name"` SourceImageUUID *string `mapstructure:"source_image_uuid" json:"source_image_uuid" required:"false" cty:"source_image_uuid" hcl:"source_image_uuid"` + SourceImageURI *string `mapstructure:"source_image_uri" json:"source_image_uri" required:"false" cty:"source_image_uri" hcl:"source_image_uri"` DiskSizeGB *int64 `mapstructure:"disk_size_gb" json:"disk_size_gb" required:"false" cty:"disk_size_gb" hcl:"disk_size_gb"` } @@ -284,6 +285,7 @@ func (*FlatVmDisk) HCL2Spec() map[string]hcldec.Spec { "image_type": &hcldec.AttrSpec{Name: "image_type", Type: cty.String, Required: false}, "source_image_name": &hcldec.AttrSpec{Name: "source_image_name", Type: cty.String, Required: false}, "source_image_uuid": &hcldec.AttrSpec{Name: "source_image_uuid", Type: cty.String, Required: false}, + "source_image_uri": &hcldec.AttrSpec{Name: "source_image_uri", Type: cty.String, Required: false}, "disk_size_gb": &hcldec.AttrSpec{Name: "disk_size_gb", Type: cty.Number, Required: false}, } return s diff --git a/builder/nutanix/driver.go b/builder/nutanix/driver.go index 2f40433..01e7af2 100644 --- a/builder/nutanix/driver.go +++ b/builder/nutanix/driver.go @@ -22,8 +22,9 @@ type Driver interface { //GetImage(string) (*nutanixImage, error) GetHost(string) (*nutanixHost, error) PowerOff(string) error - UploadImage(string, VmConfig) (*nutanixImage, error) + UploadImage(string, string, string, VmConfig) (*nutanixImage, error) DeleteImage(string) error + GetImage(string) (*nutanixImage, error) SaveVMDisk(string, string, bool) (*nutanixImage, error) WaitForShutdown(string, <-chan struct{}) bool } @@ -106,6 +107,31 @@ func findSubnetByName(conn *v3.Client, name string) (*v3.SubnetIntentResponse, e return found[0], nil } +func sourceImageExists(conn *v3.Client, name string, uri string) (*v3.ImageIntentResponse, error) { + filter := fmt.Sprintf("name==%s", name) + resp, err := conn.V3.ListAllImage(filter) + if err != nil { + return nil, err + } + + entities := resp.Entities + + found := make([]*v3.ImageIntentResponse, 0) + for _, v := range entities { + if (*v.Spec.Name == name) && (*v.Status.Resources.SourceURI == uri) { + found = append(found, v) + } + } + + if len(found) > 1 { + return nil, fmt.Errorf("your query returned more than one result with same Name/URI") + } + + if len(found) == 0 { + return nil, nil + } + return found[0], nil +} func findImageByUUID(conn *v3.Client, uuid string) (*v3.ImageIntentResponse, error) { return conn.V3.GetImage(uuid) @@ -210,6 +236,13 @@ func (d *NutanixDriver) CreateRequest(vm VmConfig) (*v3.VMIntentInput, error) { for _, disk := range vm.VmDisks { if disk.ImageType == "DISK_IMAGE" { image := &v3.ImageIntentResponse{} + if disk.SourceImageURI !="" { + image, err := d.UploadImage(disk.SourceImageURI, "URI", disk.ImageType,vm) + if err != nil { + return nil, fmt.Errorf("error while findImageByUUID, Error %s", err.Error()) + } + disk.SourceImageUUID= *image.image.Metadata.UUID + } if disk.SourceImageUUID != "" { image, err = findImageByUUID(conn, disk.SourceImageUUID) if err != nil { @@ -260,6 +293,13 @@ func (d *NutanixDriver) CreateRequest(vm VmConfig) (*v3.VMIntentInput, error) { } if disk.ImageType == "ISO_IMAGE" { image := &v3.ImageIntentResponse{} + if disk.SourceImageURI !="" { + image, err := d.UploadImage(disk.SourceImageURI, "URI", disk.ImageType,vm) + if err != nil { + return nil, fmt.Errorf("error while findImageByUUID, Error %s", err.Error()) + } + disk.SourceImageUUID= *image.image.Metadata.UUID + } if disk.SourceImageUUID != "" { image, err = findImageByUUID(conn, disk.SourceImageUUID) if err != nil { @@ -448,7 +488,7 @@ func (d *NutanixDriver) Delete(vmUUID string) error { } //UploadImage (string, VmConfig) (*nutanixImage, error) -func (d *NutanixDriver) UploadImage(imagePath string, vm VmConfig) (*nutanixImage, error) { +func (d *NutanixDriver) UploadImage(imagePath string, sourceType string, imageType string, vm VmConfig) (*nutanixImage, error) { configCreds := client.Credentials{ URL: fmt.Sprintf("%s:%d", d.ClusterConfig.Endpoint, d.ClusterConfig.Port), Endpoint: d.ClusterConfig.Endpoint, @@ -484,7 +524,7 @@ func (d *NutanixDriver) UploadImage(imagePath string, vm VmConfig) (*nutanixImag Spec: &v3.Image{ Name: &file, Resources: &v3.ImageResources{ - ImageType: StringPtr("ISO_IMAGE"), + ImageType: &imageType, InitialPlacementRefList: InitialPlacementRef, }, }, @@ -492,7 +532,16 @@ func (d *NutanixDriver) UploadImage(imagePath string, vm VmConfig) (*nutanixImag Kind: StringPtr("image"), }, } - + if sourceType=="URI" { + image, err:=sourceImageExists(conn, file, imagePath) + if err != nil { + return nil, fmt.Errorf("error while check if Image exists, %s", err.Error()) + } + if image !=nil { + return &nutanixImage{image: *image}, nil + } + req.Spec.Resources.SourceURI=&imagePath + } image, err := conn.V3.CreateImage(req) if err != nil { return nil, fmt.Errorf("error while Image Create, %s", err.Error()) @@ -510,24 +559,27 @@ func (d *NutanixDriver) UploadImage(imagePath string, vm VmConfig) (*nutanixImag time.Sleep(5 * time.Second) } - err = conn.V3.UploadImage(*image.Metadata.UUID, imagePath) - if err != nil { - return nil, fmt.Errorf("error while upload, %s", err.Error()) - } - for { - running, err := conn.V3.GetImage(*image.Metadata.UUID) + if sourceType=="PATH" { + err = conn.V3.UploadImage(*image.Metadata.UUID, imagePath) if err != nil { - return nil, fmt.Errorf("error while upload status, %s", err.Error()) + return nil, fmt.Errorf("error while upload, %s", err.Error()) } - if *running.Status.State == "COMPLETE" { - break - } - time.Sleep(5 * time.Second) + for { + running, err := conn.V3.GetImage(*image.Metadata.UUID) + if err != nil { + return nil, fmt.Errorf("error while upload status, %s", err.Error()) + } + if *running.Status.State == "COMPLETE" { + break + } + time.Sleep(5 * time.Second) + } } return &nutanixImage{image: *image}, nil } + func (d *NutanixDriver) DeleteImage(imageUUID string) error { configCreds := client.Credentials{ URL: fmt.Sprintf("%s:%d", d.ClusterConfig.Endpoint, d.ClusterConfig.Port), @@ -549,6 +601,28 @@ func (d *NutanixDriver) DeleteImage(imageUUID string) error { return nil } +func (d *NutanixDriver) GetImage(imagename string) (*nutanixImage, error) { + configCreds := client.Credentials{ + URL: fmt.Sprintf("%s:%d", d.ClusterConfig.Endpoint, d.ClusterConfig.Port), + Endpoint: d.ClusterConfig.Endpoint, + Username: d.ClusterConfig.Username, + Password: d.ClusterConfig.Password, + Port: string(d.ClusterConfig.Port), + Insecure: d.ClusterConfig.Insecure, + } + + conn, err := v3.NewV3Client(configCreds) + if err != nil { + return nil, fmt.Errorf("error while NewV3Client, %s", err.Error()) + } + + image, err := findImageByName(conn,imagename) + if err != nil { + return nil, fmt.Errorf("error while GetImage, %s", err.Error()) + } + return &nutanixImage{image: *image}, nil +} + func (d *NutanixDriver) GetVM(vmUUID string) (*nutanixInstance, error) { configCreds := client.Credentials{ @@ -669,6 +743,7 @@ func (d *NutanixDriver) SaveVMDisk(diskUUID string, imageName string, ForceDereg log.Println("More than one image with given Name found, will not deregister") } else if *ImageList.Metadata.TotalMatches==1 { log.Println("Exactly one image with given Name found, will deregister") + resp,err:= conn.V3.DeleteImage(*ImageList.Entities[0].Metadata.UUID) if err != nil { return nil, fmt.Errorf("error while DeleteImage, %s", err.Error()) @@ -687,8 +762,6 @@ func (d *NutanixDriver) SaveVMDisk(diskUUID string, imageName string, ForceDereg } return nil, fmt.Errorf("error while Image Delete getting Task Status, %s", err.Error()) } - - } } req := &v3.ImageIntentInput{ diff --git a/builder/nutanix/step_build_vm.go b/builder/nutanix/step_build_vm.go index ed4c56b..d6cdd69 100644 --- a/builder/nutanix/step_build_vm.go +++ b/builder/nutanix/step_build_vm.go @@ -26,7 +26,7 @@ func (s *stepBuildVM) Run(ctx context.Context, state multistep.StateBag) multist if cdPathRaw, ok := state.GetOk("cd_path"); ok { cdFilesPath := cdPathRaw.(string) log.Println("temporary iso found, " + cdFilesPath) - cdfilesImage, err := d.UploadImage(cdFilesPath, config.VmConfig) + cdfilesImage, err := d.UploadImage(cdFilesPath, "PATH", "ISO_IMAGE",config.VmConfig) if err != nil { ui.Error("Error uploading temporary image:") ui.Error(err.Error())