diff --git a/.gitignore b/.gitignore index b293db0..66a0b7a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ decort/vendor/ examples/ +url_scrapping/ terraform-provider-decort* diff --git a/decort/data_source_grid.go b/decort/data_source_grid.go index ef33329..cba38ed 100644 --- a/decort/data_source_grid.go +++ b/decort/data_source_grid.go @@ -25,6 +25,8 @@ Visit https://github.com/rudecs/terraform-provider-decort for full source code p package decort import ( + "strconv" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" ) @@ -43,7 +45,7 @@ func dataSourceGridRead(d *schema.ResourceData, m interface{}) error { if err != nil { return err } - d.SetId("1234") + d.SetId(strconv.Itoa(grid.Id)) flattenGrid(d, grid) return nil diff --git a/decort/data_source_grid_list.go b/decort/data_source_grid_list.go index 04e0e96..3a420da 100644 --- a/decort/data_source_grid_list.go +++ b/decort/data_source_grid_list.go @@ -25,19 +25,22 @@ Visit https://github.com/rudecs/terraform-provider-decort for full source code p package decort import ( + "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" ) func flattenGridList(gl GridList) []map[string]interface{} { - res := make([]map[string]interface{}, len(gl), len(gl)) + res := make([]map[string]interface{}, 0) for _, item := range gl { - temp := map[string]interface{}{} - temp["name"] = item.Name - temp["flag"] = item.Flag - temp["gid"] = item.Gid - temp["guid"] = item.Guid - temp["location_code"] = item.LocationCode - temp["id"] = item.Id + temp := map[string]interface{}{ + "name": item.Name, + "flag": item.Flag, + "gid": item.Gid, + "guid": item.Guid, + "location_code": item.LocationCode, + "id": item.Id, + } + res = append(res, temp) } return res @@ -48,7 +51,8 @@ func dataSourceGridListRead(d *schema.ResourceData, m interface{}) error { if err != nil { return err } - d.SetId("1234") + id := uuid.New() + d.SetId(id.String()) d.Set("items", flattenGridList(gridList)) return nil diff --git a/decort/data_source_image.go b/decort/data_source_image.go index 82c73bc..c7e7bd6 100644 --- a/decort/data_source_image.go +++ b/decort/data_source_image.go @@ -25,26 +25,60 @@ Visit https://github.com/rudecs/terraform-provider-decort for full source code p package decort import ( + "strconv" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" ) func flattenImage(d *schema.ResourceData, image *Image) { d.Set("name", image.Name) + d.Set("drivers", image.Drivers) d.Set("url", image.Url) d.Set("gid", image.Gid) d.Set("image_id", image.ImageId) d.Set("boot_type", image.Boottype) d.Set("image_type", image.Imagetype) + d.Set("bootable", image.Bootable) d.Set("sep_id", image.SepId) + d.Set("unc_path", image.UNCPath) + d.Set("link_to", image.LinkTo) + d.Set("status", image.Status) + d.Set("tech_status", image.TechStatus) + d.Set("version", image.Version) + d.Set("size", image.Size) + d.Set("enabled", image.Enabled) + d.Set("computeci_id", image.ComputeciId) + d.Set("pool_name", image.PoolName) + d.Set("username", image.Username) + d.Set("username_dl", image.UsernameDL) + d.Set("password", image.Password) + d.Set("password_dl", image.PasswordDL) + d.Set("account_id", image.AccountId) + d.Set("guid", image.Guid) + d.Set("milestones", image.Milestones) + d.Set("provider_name", image.ProviderName) + d.Set("purge_attempts", image.PurgeAttempts) + d.Set("reference_id", image.ReferenceId) + d.Set("res_id", image.ResId) + d.Set("res_name", image.ResName) + d.Set("rescuecd", image.Rescuecd) + d.Set("architecture", image.Architecture) + d.Set("hot_resize", image.Hotresize) + d.Set("history", flattenHistory(image.History)) + d.Set("last_modified", image.LastModified) + d.Set("meta", flattenMeta(image.Meta)) + d.Set("desc", image.Desc) + d.Set("shared_with", image.SharedWith) return } func dataSourceImageRead(d *schema.ResourceData, m interface{}) error { image, err := utilityImageCheckPresence(d, m) if err != nil { + return err } - d.SetId("1234") + d.SetId(strconv.Itoa(image.Guid)) flattenImage(d, image) return nil @@ -77,6 +111,34 @@ func dataSourceImageSchemaMake() map[string]*schema.Schema { Computed: true, Description: "Image type linux, windows or other", }, + "shared_with": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + "history": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "guid": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeInt, + Computed: true, + }, + "timestamp": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, "drivers": { Type: schema.TypeList, Computed: true, @@ -85,6 +147,14 @@ func dataSourceImageSchemaMake() map[string]*schema.Schema { }, Description: "List of types of compute suitable for image. Example: [ \"KVM_X86\" ]", }, + "meta": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "meta", + }, "hot_resize": { Type: schema.TypeBool, Computed: true, @@ -145,30 +215,84 @@ func dataSourceImageSchemaMake() map[string]*schema.Schema { Computed: true, Description: "Does this image boot OS", }, - "virtual": { - Type: schema.TypeMap, + "unc_path": { + Type: schema.TypeString, Computed: true, - Description: "Create virtual image", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - Description: "name of the virtual image to create", - }, - "v_image_id": { - Type: schema.TypeInt, - Computed: true, - Description: "", - }, - }, - }, + Description: "unc path", }, "link_to": { Type: schema.TypeInt, Computed: true, Description: "", }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "status", + }, + "tech_status": { + Type: schema.TypeString, + Computed: true, + Description: "tech atatus", + }, + "version": { + Type: schema.TypeString, + Computed: true, + Description: "version", + }, + "size": { + Type: schema.TypeInt, + Computed: true, + Description: "image size", + }, + "enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "computeci_id": { + Type: schema.TypeInt, + Computed: true, + }, + "guid": { + Type: schema.TypeInt, + Computed: true, + }, + "milestones": { + Type: schema.TypeInt, + Computed: true, + }, + "provider_name": { + Type: schema.TypeString, + Computed: true, + }, + "purge_attempts": { + Type: schema.TypeInt, + Computed: true, + }, + "reference_id": { + Type: schema.TypeString, + Computed: true, + }, + "res_id": { + Type: schema.TypeString, + Computed: true, + }, + "res_name": { + Type: schema.TypeString, + Computed: true, + }, + "rescuecd": { + Type: schema.TypeBool, + Computed: true, + }, + "last_modified": { + Type: schema.TypeInt, + Computed: true, + }, + "desc": { + Type: schema.TypeString, + Computed: true, + }, } } diff --git a/decort/data_source_image_list.go b/decort/data_source_image_list.go index 0d611ec..ad6f6d9 100644 --- a/decort/data_source_image_list.go +++ b/decort/data_source_image_list.go @@ -25,20 +25,53 @@ Visit https://github.com/rudecs/terraform-provider-decort for full source code p package decort import ( + "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" ) func flattenImageList(il ImageList) []map[string]interface{} { - res := make([]map[string]interface{}, len(il), len(il)) + res := make([]map[string]interface{}, 0) for _, item := range il { - temp := map[string]interface{}{} - temp["name"] = item.Name - temp["url"] = item.Url - temp["gid"] = item.Gid - temp["drivers"] = item.Drivers - temp["image_id"] = item.ImageId - temp["boot_type"] = item.Boottype - temp["image_type"] = item.Imagetype + temp := map[string]interface{}{ + "name": item.Name, + "url": item.Url, + "gid": item.Gid, + "guid": item.Guid, + "drivers": item.Drivers, + "image_id": item.ImageId, + "boot_type": item.Boottype, + "bootable": item.Bootable, + "image_type": item.Imagetype, + "status": item.Status, + "tech_status": item.TechStatus, + "version": item.Version, + "username": item.Username, + "username_dl": item.UsernameDL, + "password": item.Password, + "password_dl": item.PasswordDL, + "purge_attempts": item.PurgeAttempts, + "architecture": item.Architecture, + "account_id": item.AccountId, + "computeci_id": item.ComputeciId, + "enabled": item.Enabled, + "reference_id": item.ReferenceId, + "res_id": item.ResId, + "res_name": item.ResName, + "rescuecd": item.Rescuecd, + "provider_name": item.ProviderName, + "milestones": item.Milestones, + "size": item.Size, + "sep_id": item.SepId, + "link_to": item.LinkTo, + "unc_path": item.UNCPath, + "pool_name": item.PoolName, + "hot_resize": item.Hotresize, + "history": flattenHistory(item.History), + "last_modified": item.LastModified, + "meta": flattenMeta(item.Meta), + "desc": item.Desc, + "shared_with": item.SharedWith, + } res = append(res, temp) } return res @@ -49,7 +82,8 @@ func dataSourceImageListRead(d *schema.ResourceData, m interface{}) error { if err != nil { return err } - d.SetId("1234") + id := uuid.New() + d.SetId(id.String()) d.Set("items", flattenImageList(imageList)) return nil @@ -82,7 +116,7 @@ func dataSourceImageListSchemaMake() map[string]*schema.Schema { Computed: true, Description: "image list", Elem: &schema.Resource{ - Schema: resourceImageSchemaMake(), + Schema: dataSourceImageSchemaMake(), }, }, } diff --git a/decort/data_source_image_list_stacks.go b/decort/data_source_image_list_stacks.go new file mode 100644 index 0000000..508c7fd --- /dev/null +++ b/decort/data_source_image_list_stacks.go @@ -0,0 +1,182 @@ +/* +Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved. +Author: Stanislav Solovev, , + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration +Technology platfom. + +Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates. +*/ + +package decort + +import ( + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func flattenImageListStacks(d *schema.ResourceData, stack ImageListStacks) []map[string]interface{} { + temp := make([]map[string]interface{}, 0) + for _, item := range stack { + t := map[string]interface{}{ + "api_url": item.ApiURL, + "api_key": item.ApiKey, + "app_id": item.AppId, + "desc": item.Desc, + "drivers": item.Drivers, + "error": item.Error, + "guid": item.Guid, + "id": item.Id, + "images": item.Images, + "login": item.Login, + "name": item.Name, + "passwd": item.Passwd, + "reference_id": item.ReferenceId, + "status": item.Status, + "type": item.Type, + } + + temp = append(temp, t) + } + return temp +} + +func dataSourceImageListStacksRead(d *schema.ResourceData, m interface{}) error { + imageListStacks, err := utilityImageListStacksCheckPresence(d, m) + if err != nil { + return err + } + id := uuid.New() + d.SetId(id.String()) + d.Set("items", flattenImageListStacks(d, imageListStacks)) + + return nil +} + +func dataSourceImageListStackSchemaMake() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "api_url": { + Type: schema.TypeString, + Computed: true, + }, + "api_key": { + Type: schema.TypeString, + Computed: true, + }, + "app_id": { + Type: schema.TypeString, + Computed: true, + }, + "desc": { + Type: schema.TypeString, + Computed: true, + }, + "drivers": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "error": { + Type: schema.TypeInt, + Computed: true, + }, + "guid": { + Type: schema.TypeInt, + Computed: true, + }, + "id": { + Type: schema.TypeInt, + Computed: true, + }, + "images": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + "login": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "passwd": { + Type: schema.TypeString, + Computed: true, + }, + "reference_id": { + Type: schema.TypeString, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "type": { + Type: schema.TypeString, + Computed: true, + }, + } +} + +func dataSourceImageListStacksSchemaMake() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "image_id": { + Type: schema.TypeInt, + Required: true, + Description: "image id", + }, + "page": { + Type: schema.TypeInt, + Optional: true, + Description: "page number", + }, + "size": { + Type: schema.TypeInt, + Optional: true, + Description: "page size", + }, + "items": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: dataSourceImageListStackSchemaMake(), + }, + Description: "items of stacks list", + }, + } +} + +func dataSourceImageListStacks() *schema.Resource { + return &schema.Resource{ + SchemaVersion: 1, + + Read: dataSourceImageListStacksRead, + + Timeouts: &schema.ResourceTimeout{ + Read: &Timeout30s, + Default: &Timeout60s, + }, + + Schema: dataSourceImageListStacksSchemaMake(), + } +} diff --git a/decort/models_api.go b/decort/models_api.go index a7af6ca..7673e3e 100644 --- a/decort/models_api.go +++ b/decort/models_api.go @@ -668,42 +668,101 @@ type SshKeyConfig struct { UserShell string } -///////////////// -// images api +//////////////////// +// IMAGE API // //////////////////// const imageCreateAPI = "/restmachine/cloudbroker/image/createImage" +const imageSyncCreateAPI = "/restmachine/cloudbroker/image/syncCreateImage" +const imageCreateVirtualAPI = "/restmachine/cloudbroker/image/createVirtual" +const imageCreateCDROMAPI = "/restmachine/cloudbroker/image/createCDROMImage" +const imageListStacksApi = "/restmachine/cloudbroker/image/listStacks" const imageGetAPI = "/restmachine/cloudbroker/image/get" const imageListGetAPI = "/restmachine/cloudbroker/image/list" const imageEditAPI = "/restmachine/cloudbroker/image/edit" const imageDeleteAPI = "/restmachine/cloudbroker/image/delete" -const imageEditNameAPI = "/restmachine/cloudapi/image/rename" -const imageLinkAPI = "/restmachine/cloudapi/image/link" +const imageDeleteCDROMAPI = "/restmachine/cloudbroker/image/deleteCDROMImage" +const imageEnableAPI = "/restmachine/cloudbroker/image/enable" +const imageDisableAPI = "/restmachine/cloudbroker/image/disable" +const imageEditNameAPI = "/restmachine/cloudbroker/image/rename" +const imageLinkAPI = "/restmachine/cloudbroker/image/link" +const imageShareAPI = "/restmachine/cloudbroker/image/share" +const imageComputeciSetAPI = "/restmachine/cloudbroker/image/computeciSet" +const imageComputeciUnsetAPI = "/restmachine/cloudbroker/image/computeciUnset" +const imageUpdateNodesAPI = "/restmachine/cloudbroker/image/updateNodes" +const imageDeleteImagesAPI = "/restmachine/cloudbroker/image/deleteImages" + +type History struct { + Guid string `json:"guid"` + Id int `json:"id"` + Timestamp int64 `json:"timestamp"` +} type Image struct { - ImageId int `json:"id"` - Name string `json:"name"` - Url string `json:"url"` - Gid int `json:"gid"` - Boottype string `json:"bootType"` - Imagetype string `json:"imagetype"` - Drivers []string `json:"drivers"` - Hotresize bool `json:"hotresize"` - Bootable bool `json:"bootable"` - Username string `json:"username"` - Password string `json:"password"` - AccountId int `json:"accountId"` - UsernameDL string `json:"usernameDL"` - PasswordDL string `json:"passwordDL"` - SepId int `json:"sepId"` - PoolName string `json:"poolName"` - Architecture string `json:"architecture"` + ImageId int `json:"id"` + Name string `json:"name"` + Url string `json:"url"` + Gid int `json:"gid"` + Guid int `json:"guid"` + Boottype string `json:"bootType"` + Imagetype string `json:"type"` + Drivers []string `json:"drivers"` + Hotresize bool `json:"hotResize"` + Bootable bool `json:"bootable"` + Username string `json:"username"` + Password string `json:"password"` + AccountId int `json:"accountId"` + UsernameDL string `json:"usernameDL"` + PasswordDL string `json:"passwordDL"` + SepId int `json:"sepId"` + PoolName string `json:"pool"` + Architecture string `json:"architecture"` + UNCPath string `json:"UNCPath"` + LinkTo int `json:"linkTo"` + Status string `json:"status"` + TechStatus string `json:"techStatus"` + Size int `json:"size"` + Version string `json:"version"` + Enabled bool `json:"enabled"` + ComputeciId int `json:"computeciId"` + Milestones int `json:"milestones"` + ProviderName string `json:"provider_name"` + PurgeAttempts int `json:"purgeAttempts"` + ReferenceId string `json:"referenceId"` + ResId string `json:"resId"` + ResName string `json:"resName"` + Rescuecd bool `json:"rescuecd"` + Meta []interface{} `json:"_meta"` + History []History `json:"history"` + LastModified int64 `json:"lastModified"` + Desc string `json:"desc"` + SharedWith []int `json:"sharedWith"` } type ImageList []Image -///////////// -////GRID -//////////////// +type ImageStack struct { + ApiURL string `json:"apiUrl"` + ApiKey string `json:"apikey"` + AppId string `json:"appId"` + Desc string `json:"desc"` + Drivers []string `json:"drivers"` + Error int `json:"error"` + Guid int `json:"guid"` + Id int `json:"id"` + Images []int `json:"images"` + Login string `json:"login"` + Name string `json:"name"` + Passwd string `json:"passwd"` + ReferenceId string `json:"referenceId"` + Status string `json:"status"` + Type string `json:"type"` +} + +type ImageListStacks []ImageStack + +///////////////// +// GRID API // +///////////////// const GridListGetAPI = "/restmachine/cloudbroker/grid/list" const GridGetAPI = "/restmachine/cloudbroker/grid/get" diff --git a/decort/provider.go b/decort/provider.go index d5cf0e2..f7f26a5 100644 --- a/decort/provider.go +++ b/decort/provider.go @@ -99,26 +99,30 @@ func Provider() *schema.Provider { }, ResourcesMap: map[string]*schema.Resource{ - "decort_resgroup": resourceResgroup(), - "decort_kvmvm": resourceCompute(), - "decort_disk": resourceDisk(), - "decort_vins": resourceVins(), - "decort_pfw": resourcePfw(), - "decort_k8s": resourceK8s(), - "decort_k8s_wg": resourceK8sWg(), - "decort_image": resourceImage(), + "decort_resgroup": resourceResgroup(), + "decort_kvmvm": resourceCompute(), + "decort_disk": resourceDisk(), + "decort_vins": resourceVins(), + "decort_pfw": resourcePfw(), + "decort_k8s": resourceK8s(), + "decort_k8s_wg": resourceK8sWg(), + "decort_image": resourceImage(), + "decort_virtual_image": resourceVirtualImage(), + "decort_cdrom_image": resourceCDROMImage(), + "decort_delete_images": resourceDeleteImages(), }, DataSourcesMap: map[string]*schema.Resource{ - "decort_account": dataSourceAccount(), - "decort_resgroup": dataSourceResgroup(), - "decort_kvmvm": dataSourceCompute(), - "decort_image": dataSourceImage(), - "decort_disk": dataSourceDisk(), - "decort_vins": dataSourceVins(), - "decort_grid": dataSourceGrid(), - "decort_grid_list": dataSourceGridList(), - "decort_image_list": dataSourceImageList(), + "decort_account": dataSourceAccount(), + "decort_resgroup": dataSourceResgroup(), + "decort_kvmvm": dataSourceCompute(), + "decort_image": dataSourceImage(), + "decort_disk": dataSourceDisk(), + "decort_vins": dataSourceVins(), + "decort_grid": dataSourceGrid(), + "decort_grid_list": dataSourceGridList(), + "decort_image_list": dataSourceImageList(), + "decort_image_list_stacks": dataSourceImageListStacks(), // "decort_pfw": dataSourcePfw(), }, diff --git a/decort/resource_cdrom_image.go b/decort/resource_cdrom_image.go new file mode 100644 index 0000000..9711bf4 --- /dev/null +++ b/decort/resource_cdrom_image.go @@ -0,0 +1,449 @@ +/* +Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved. +Author: Stanislav Solovev, + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration +Technology platfom. + +Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates. +*/ + +package decort + +import ( + "net/url" + "strconv" + + "github.com/hashicorp/terraform-plugin-sdk/helper/customdiff" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + log "github.com/sirupsen/logrus" +) + +func resourceCDROMImageCreate(d *schema.ResourceData, m interface{}) error { + log.Debugf("resourceCDROMImageCreate: called for image %s", d.Get("name").(string)) + + controller := m.(*ControllerCfg) + urlValues := &url.Values{} + urlValues.Add("name", d.Get("name").(string)) + urlValues.Add("url", d.Get("url").(string)) + urlValues.Add("gid", strconv.Itoa(d.Get("gid").(int))) + + tstr := d.Get("drivers").([]interface{}) + temp := "" + l := len(tstr) + for i, str := range tstr { + s := "\"" + str.(string) + "\"" + if i != (l - 1) { + s += "," + } + temp = temp + s + } + temp = "[" + temp + "]" + urlValues.Add("drivers", temp) + + if username, ok := d.GetOk("username"); ok { + urlValues.Add("username", username.(string)) + } + if password, ok := d.GetOk("password"); ok { + urlValues.Add("password", password.(string)) + } + if accountId, ok := d.GetOk("account_id"); ok { + urlValues.Add("accountId", strconv.Itoa(accountId.(int))) + } + if usernameDL, ok := d.GetOk("username_dl"); ok { + urlValues.Add("usernameDL", usernameDL.(string)) + } + if passwordDL, ok := d.GetOk("password_dl"); ok { + urlValues.Add("passwordDL", passwordDL.(string)) + } + if sepId, ok := d.GetOk("sep_id"); ok { + urlValues.Add("sepId", strconv.Itoa(sepId.(int))) + } + if poolName, ok := d.GetOk("pool_name"); ok { + urlValues.Add("pool_name", poolName.(string)) + } + if architecture, ok := d.GetOk("architecture"); ok { + urlValues.Add("architecture", architecture.(string)) + } + + imageId, err := controller.decortAPICall("POST", imageCreateCDROMAPI, urlValues) + if err != nil { + return err + } + + d.SetId(imageId) + d.Set("image_id", imageId) + + image, err := utilityImageCheckPresence(d, m) + if err != nil { + return err + } + + d.SetId(strconv.Itoa(image.ImageId)) + d.Set("bootable", image.Bootable) + //d.Set("image_id", image.ImageId) + + err = resourceImageRead(d, m) + if err != nil { + return err + } + + return nil +} + +func resourceCDROMImageDelete(d *schema.ResourceData, m interface{}) error { + log.Debugf("resourceCDROMImageDelete: called for %s, id: %s", d.Get("name").(string), d.Id()) + + image, err := utilityImageCheckPresence(d, m) + if image == nil { + if err != nil { + return err + } + return nil + } + + controller := m.(*ControllerCfg) + urlValues := &url.Values{} + urlValues.Add("imageId", strconv.Itoa(d.Get("image_id").(int))) + + if permanently, ok := d.GetOk("permanently"); ok { + urlValues.Add("permanently", strconv.FormatBool(permanently.(bool))) + } + + _, err = controller.decortAPICall("POST", imageDeleteCDROMAPI, urlValues) + if err != nil { + return err + } + d.SetId("") + + return nil +} + +func resourceCDROMImageSchemaMake() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "Name of the rescue disk", + }, + "url": { + Type: schema.TypeString, + Required: true, + Description: "URL where to download ISO from", + }, + "gid": { + Type: schema.TypeInt, + Required: true, + Description: "grid (platform) ID where this template should be create in", + }, + "boot_type": { + Type: schema.TypeString, + Computed: true, + Description: "Boot type of image bios or uefi", + }, + "image_type": { + Type: schema.TypeString, + Computed: true, + Description: "Image type linux, windows or other", + }, + "drivers": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "List of types of compute suitable for image. Example: [ \"KVM_X86\" ]", + }, + "meta": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "meta", + }, + "hot_resize": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "Does this machine supports hot resize", + }, + "username": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Optional username for the image", + }, + "password": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Optional password for the image", + }, + "account_id": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "AccountId to make the image exclusive", + }, + "username_dl": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "username for upload binary media", + }, + "password_dl": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "password for upload binary media", + }, + "sep_id": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "storage endpoint provider ID", + }, + "pool_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "pool for image create", + }, + "architecture": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "binary architecture of this image, one of X86_64 of PPC64_LE", + }, + "image_id": { + Type: schema.TypeInt, + Computed: true, + Description: "image id", + }, + "permanently": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "Whether to completely delete the image", + }, + "bootable": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "Does this image boot OS", + }, + "unc_path": { + Type: schema.TypeString, + Computed: true, + Description: "unc path", + }, + "link_to": { + Type: schema.TypeInt, + Computed: true, + Description: "", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "status", + }, + "tech_status": { + Type: schema.TypeString, + Computed: true, + Description: "tech atatus", + }, + "version": { + Type: schema.TypeString, + Computed: true, + Description: "version", + }, + "size": { + Type: schema.TypeInt, + Computed: true, + Description: "image size", + }, + "enabled": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, + "computeci_id": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "guid": { + Type: schema.TypeInt, + Computed: true, + }, + "milestones": { + Type: schema.TypeInt, + Computed: true, + }, + "provider_name": { + Type: schema.TypeString, + Computed: true, + }, + "purge_attempts": { + Type: schema.TypeInt, + Computed: true, + }, + "reference_id": { + Type: schema.TypeString, + Computed: true, + }, + "res_id": { + Type: schema.TypeString, + Computed: true, + }, + "res_name": { + Type: schema.TypeString, + Computed: true, + }, + "rescuecd": { + Type: schema.TypeBool, + Computed: true, + }, + "desc": { + Type: schema.TypeString, + Computed: true, + }, + "shared_with": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + "enabled_stacks": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "history": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "guid": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeInt, + Computed: true, + }, + "timestamp": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + } +} + +func resourceCDROMImage() *schema.Resource { + return &schema.Resource{ + SchemaVersion: 1, + + Create: resourceCDROMImageCreate, + Read: resourceImageRead, + Update: resourceImageEdit, + Delete: resourceCDROMImageDelete, + Exists: resourceImageExists, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: &Timeout60s, + Read: &Timeout30s, + Update: &Timeout60s, + Delete: &Timeout60s, + Default: &Timeout60s, + }, + + CustomizeDiff: customdiff.All( + customdiff.IfValueChange("enabled", func(old, new, meta interface{}) bool { + if old.(bool) != new.(bool) { + return true + } + return false + }, resourceImageChangeEnabled), + customdiff.IfValueChange("name", func(old, new, meta interface{}) bool { + if old.(string) != new.(string) && old.(string) != "" { + return true + } + return false + }, resourceImageEditName), + customdiff.IfValueChange("shared_with", func(old, new, meta interface{}) bool { + o := old.([]interface{}) + n := new.([]interface{}) + + if len(o) != len(n) { + return true + } else if len(o) == 0 { + return false + } + count := 0 + for i, v := range n { + if v.(int) == o[i].(int) { + count++ + } + } + if count == 0 { + return true + } + return false + }, resourceImageShare), + customdiff.IfValueChange("computeci_id", func(old, new, meta interface{}) bool { + if old.(int) != new.(int) { + return true + } + return false + }, resourceImageChangeComputeci), + customdiff.IfValueChange("enabled_stacks", func(old, new, meta interface{}) bool { + o := old.([]interface{}) + n := new.([]interface{}) + + if len(o) != len(n) { + return true + } else if len(o) == 0 { + return false + } + count := 0 + for i, v := range n { + if v.(string) == o[i].(string) { + count++ + } + } + if count == 0 { + return true + } + return false + }, resourceImageUpdateNodes), + ), + + Schema: resourceCDROMImageSchemaMake(), + } +} diff --git a/decort/resource_delete_images.go b/decort/resource_delete_images.go new file mode 100644 index 0000000..5c5cc19 --- /dev/null +++ b/decort/resource_delete_images.go @@ -0,0 +1,131 @@ +/* +Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved. +Author: Stanislav Solovev, + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration +Technology platfom. + +Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates. +*/ + +package decort + +import ( + "net/url" + "strconv" + + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + log "github.com/sirupsen/logrus" +) + +func resourceCreateListImages(d *schema.ResourceData, m interface{}) error { + id := uuid.New() + d.SetId(id.String()) + return nil +} + +func resourceDeleteListImages(d *schema.ResourceData, m interface{}) error { + log.Debugf("resourceDeleteListImages: start deleting...") + + c := m.(*ControllerCfg) + urlValues := &url.Values{} + + imageIds := d.Get("image_ids").([]interface{}) + temp := "" + l := len(imageIds) + for i, imageId := range imageIds { + s := strconv.Itoa(imageId.(int)) + if i != (l - 1) { + s += ",\n" + } else { + s += "\n" + } + temp = temp + s + } + temp = "[" + temp + "]" + + urlValues.Add("reason", d.Get("reason").(string)) + urlValues.Add("permanently", strconv.FormatBool(d.Get("permanently").(bool))) + urlValues.Add("imageIds", temp) + + _, err := c.decortAPICall("POST", imageDeleteImagesAPI, urlValues) + if err != nil { + return err + } + + d.SetId("") + + return nil +} + +func resourceReadListImages(d *schema.ResourceData, m interface{}) error { + return nil +} + +func resourceDeleteImagesSchemaMake() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "image_ids": { + Type: schema.TypeList, + Required: true, + ForceNew: true, + MinItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + Description: "images ids for deleting", + }, + "reason": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "reason for deleting the images", + }, + "permanently": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + Default: false, + Description: "whether to completely delete the images", + }, + } + +} + +func resourceDeleteImages() *schema.Resource { + return &schema.Resource{ + SchemaVersion: 1, + + Create: resourceCreateListImages, + Read: resourceReadListImages, + Delete: resourceDeleteListImages, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: &Timeout60s, + Read: &Timeout30s, + Update: &Timeout60s, + Delete: &Timeout60s, + Default: &Timeout60s, + }, + + Schema: resourceDeleteImagesSchemaMake(), + } +} diff --git a/decort/resource_image.go b/decort/resource_image.go index 21f6877..6e206c4 100644 --- a/decort/resource_image.go +++ b/decort/resource_image.go @@ -25,11 +25,12 @@ Visit https://github.com/rudecs/terraform-provider-decort for full source code p package decort import ( + "errors" "net/url" "strconv" + "github.com/hashicorp/terraform-plugin-sdk/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/helper/validation" log "github.com/sirupsen/logrus" ) @@ -46,8 +47,12 @@ func resourceImageCreate(d *schema.ResourceData, m interface{}) error { tstr := d.Get("drivers").([]interface{}) temp := "" - for _, str := range tstr { + l := len(tstr) + for i, str := range tstr { s := "\"" + str.(string) + "\"" + if i != (l - 1) { + s += "," + } temp = temp + s } temp = "[" + temp + "]" @@ -62,9 +67,6 @@ func resourceImageCreate(d *schema.ResourceData, m interface{}) error { if password, ok := d.GetOk("password"); ok { urlValues.Add("password", password.(string)) } - if accountId, ok := d.GetOk("account_id"); ok { - urlValues.Add("accountId", accountId.(string)) - } if accountId, ok := d.GetOk("account_id"); ok { urlValues.Add("accountId", strconv.Itoa(accountId.(int))) } @@ -84,7 +86,14 @@ func resourceImageCreate(d *schema.ResourceData, m interface{}) error { urlValues.Add("architecture", architecture.(string)) } - imageId, err := controller.decortAPICall("POST", imageCreateAPI, urlValues) + api := "" + isSync := d.Get("sync").(bool) + if !isSync { + api = imageCreateAPI + } else { + api = imageSyncCreateAPI + } + imageId, err := controller.decortAPICall("POST", api, urlValues) if err != nil { return err } @@ -101,6 +110,11 @@ func resourceImageCreate(d *schema.ResourceData, m interface{}) error { d.Set("bootable", image.Bootable) //d.Set("image_id", image.ImageId) + err = resourceImageRead(d, m) + if err != nil { + return err + } + return nil } @@ -113,23 +127,44 @@ func resourceImageRead(d *schema.ResourceData, m interface{}) error { return err } - d.Set("image_id", image.ImageId) d.Set("name", image.Name) + d.Set("drivers", image.Drivers) d.Set("url", image.Url) d.Set("gid", image.Gid) + d.Set("image_id", image.ImageId) d.Set("boot_type", image.Boottype) d.Set("image_type", image.Imagetype) - d.Set("drivers", image.Drivers) - d.Set("hot_resize", image.Hotresize) + d.Set("bootable", image.Bootable) + d.Set("sep_id", image.SepId) + d.Set("unc_path", image.UNCPath) + d.Set("link_to", image.LinkTo) + d.Set("status", image.Status) + d.Set("tech_status", image.TechStatus) + d.Set("version", image.Version) + d.Set("size", image.Size) + d.Set("enabled", image.Enabled) + d.Set("computeci_id", image.ComputeciId) + d.Set("pool_name", image.PoolName) d.Set("username", image.Username) - d.Set("password", image.Password) - d.Set("account_id", image.AccountId) d.Set("username_dl", image.UsernameDL) + d.Set("password", image.Password) d.Set("password_dl", image.PasswordDL) - d.Set("sep_id", image.SepId) - d.Set("pool_name", image.PoolName) + d.Set("account_id", image.AccountId) + d.Set("guid", image.Guid) + d.Set("milestones", image.Milestones) + d.Set("provider_name", image.ProviderName) + d.Set("purge_attempts", image.PurgeAttempts) + d.Set("reference_id", image.ReferenceId) + d.Set("res_id", image.ResId) + d.Set("res_name", image.ResName) + d.Set("rescuecd", image.Rescuecd) d.Set("architecture", image.Architecture) - d.Set("bootable", image.Bootable) + d.Set("meta", flattenMeta(image.Meta)) + d.Set("hot_resize", image.Hotresize) + d.Set("history", flattenHistory(image.History)) + d.Set("last_modified", image.LastModified) + d.Set("desc", image.Desc) + d.Set("shared_with", image.SharedWith) return nil } @@ -148,7 +183,11 @@ func resourceImageDelete(d *schema.ResourceData, m interface{}) error { controller := m.(*ControllerCfg) urlValues := &url.Values{} urlValues.Add("imageId", strconv.Itoa(d.Get("image_id").(int))) - urlValues.Add("reason", "") + if reason, ok := d.GetOk("reason"); ok { + urlValues.Add("reason", reason.(string)) + } else { + urlValues.Add("reason", "") + } if permanently, ok := d.GetOk("permanently"); ok { urlValues.Add("permanently", strconv.FormatBool(permanently.(bool))) } @@ -186,16 +225,71 @@ func resourceImageEditName(d *schema.ResourceDiff, m interface{}) error { if err != nil { return err } + + return nil +} + +func resourceImageEdit(d *schema.ResourceData, m interface{}) error { + log.Debugf("resourceImageEdit: called for %s, id: %s", d.Get("name").(string), d.Id()) + c := m.(*ControllerCfg) + urlValues := &url.Values{} + urlValues.Add("imageId", strconv.Itoa(d.Get("image_id").(int))) + urlValues.Add("name", d.Get("name").(string)) + + urlValues.Add("username", d.Get("username").(string)) + urlValues.Add("password", d.Get("password").(string)) + urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int))) + urlValues.Add("bootable", strconv.FormatBool(d.Get("bootable").(bool))) + urlValues.Add("hotresize", strconv.FormatBool(d.Get("hot_resize").(bool))) + + //_, err := c.decortAPICall("POST", imageEditAPI, urlValues) + _, err := c.decortAPICall("POST", imageEditAPI, urlValues) + if err != nil { + err = resourceImageRead(d, m) + if err != nil { + return err + } + return nil + } + err = resourceImageRead(d, m) + if err != nil { + return err + } + + return nil +} + +func resourceImageChangeEnabled(d *schema.ResourceDiff, m interface{}) error { + var api string + + c := m.(*ControllerCfg) + urlValues := &url.Values{} + urlValues.Add("imageId", strconv.Itoa(d.Get("image_id").(int))) + if d.Get("enabled").(bool) { + api = imageEnableAPI + } else { + api = imageDisableAPI + } + resp, err := c.decortAPICall("POST", api, urlValues) + if err != nil { + return err + } + res, err := strconv.ParseBool(resp) + if err != nil { + return err + } + if !res { + return errors.New("Cannot enable/disable") + } return nil } func resourceImageLink(d *schema.ResourceDiff, m interface{}) error { - log.Debugf("resourceImageLink: called for %s, id: %s", d.Get("name").(string), d.Id()) + log.Debugf("resourceVirtualImageLink: called for %s, id: %s", d.Get("name").(string), d.Id()) c := m.(*ControllerCfg) urlValues := &url.Values{} - link := d.Get("link").(map[string]interface{}) - urlValues.Add("imageId", strconv.Itoa(link["image_id"].(int))) - urlValues.Add("targetId", strconv.Itoa(link["target_id"].(int))) + urlValues.Add("imageId", strconv.Itoa(d.Get("image_id").(int))) + urlValues.Add("targetId", strconv.Itoa(d.Get("link_to").(int))) _, err := c.decortAPICall("POST", imageLinkAPI, urlValues) if err != nil { return err @@ -204,31 +298,79 @@ func resourceImageLink(d *schema.ResourceDiff, m interface{}) error { return nil } -func resourceImageEdit(d *schema.ResourceData, m interface{}) error { - log.Debugf("resourceImageEdit: called for %s, id: %s", d.Get("name").(string), d.Id()) +func resourceImageShare(d *schema.ResourceDiff, m interface{}) error { + log.Debugf("resourceImageShare: called for %s, id: %s", d.Get("name").(string), d.Id()) c := m.(*ControllerCfg) urlValues := &url.Values{} urlValues.Add("imageId", strconv.Itoa(d.Get("image_id").(int))) - urlValues.Add("name", d.Get("name").(string)) - if username, ok := d.GetOk("username"); ok { - urlValues.Add("username", username.(string)) + accIds := d.Get("shared_with").([]interface{}) + temp := "" + l := len(accIds) + for i, accId := range accIds { + s := strconv.Itoa(accId.(int)) + if i != (l - 1) { + s += ",\n" + } else { + s += "\n" + } + temp = temp + s } - if password, ok := d.GetOk("password"); ok { - urlValues.Add("password", password.(string)) + temp = "[" + temp + "]" + urlValues.Add("accounts", temp) + _, err := c.decortAPICall("POST", imageShareAPI, urlValues) + if err != nil { + return err } - if accountId, ok := d.GetOk("account_id"); ok { - urlValues.Add("accountId", strconv.Itoa(accountId.(int))) + + return nil +} + +func resourceImageChangeComputeci(d *schema.ResourceDiff, m interface{}) error { + c := m.(*ControllerCfg) + urlValues := &url.Values{} + + urlValues.Add("imageId", strconv.Itoa(d.Get("image_id").(int))) + computeci := d.Get("computeci_id").(int) + + api := "" + + if computeci == 0 { + api = imageComputeciUnsetAPI + } else { + urlValues.Add("computeciId", strconv.Itoa(computeci)) + api = imageComputeciSetAPI } - if bootable, ok := d.GetOk("bootable"); ok { - urlValues.Add("bootable", strconv.FormatBool(bootable.(bool))) + + _, err := c.decortAPICall("POST", api, urlValues) + if err != nil { + return err } - if hotresize, ok := d.GetOk("hot_resize"); ok { - urlValues.Add("hotresize", strconv.FormatBool(hotresize.(bool))) + + return nil +} + +func resourceImageUpdateNodes(d *schema.ResourceDiff, m interface{}) error { + log.Debugf("resourceImageUpdateNodes: called for %s, id: %s", d.Get("name").(string), d.Id()) + c := m.(*ControllerCfg) + urlValues := &url.Values{} + urlValues.Add("imageId", strconv.Itoa(d.Get("image_id").(int))) + enabledStacks := d.Get("enabled_stacks").([]interface{}) + temp := "" + l := len(enabledStacks) + for i, stackId := range enabledStacks { + s := stackId.(string) + if i != (l - 1) { + s += "," + } + temp = temp + s } - _, err := c.decortAPICall("POST", imageEditAPI, urlValues) + temp = "[" + temp + "]" + urlValues.Add("enabledStacks", temp) + _, err := c.decortAPICall("POST", imageUpdateNodesAPI, urlValues) if err != nil { return err } + return nil } @@ -237,129 +379,241 @@ func resourceImageSchemaMake() map[string]*schema.Schema { "name": { Type: schema.TypeString, Required: true, - ForceNew: true, Description: "Name of the rescue disk", }, "url": { Type: schema.TypeString, Required: true, - ForceNew: true, Description: "URL where to download media from", }, "gid": { - Type: schema.TypeInt, - Required: true, - ForceNew: true, - ValidateFunc: validation.IntBetween(1, 65535), - Description: "grid (platform) ID where this template should be create in", + Type: schema.TypeInt, + Required: true, + Description: "grid (platform) ID where this template should be create in", }, "boot_type": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{"bios", "uefi"}, false), - Description: "Boot type of image bios or uefi", + Type: schema.TypeString, + Required: true, + Description: "Boot type of image bios or uefi", }, "image_type": { Type: schema.TypeString, Required: true, - ForceNew: true, Description: "Image type linux, windows or other", }, "drivers": { Type: schema.TypeList, Required: true, - MinItems: 1, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "List of types of compute suitable for image. Example: [ \"KVM_X86\" ]", }, + "meta": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "meta", + }, "hot_resize": { Type: schema.TypeBool, Optional: true, + Computed: true, Description: "Does this machine supports hot resize", }, "username": { Type: schema.TypeString, Optional: true, + Computed: true, Description: "Optional username for the image", }, "password": { Type: schema.TypeString, Optional: true, + Computed: true, Description: "Optional password for the image", }, "account_id": { Type: schema.TypeInt, Optional: true, + Computed: true, Description: "AccountId to make the image exclusive", }, "username_dl": { Type: schema.TypeString, Optional: true, + Computed: true, Description: "username for upload binary media", }, "password_dl": { Type: schema.TypeString, Optional: true, + Computed: true, Description: "password for upload binary media", }, "sep_id": { Type: schema.TypeInt, Optional: true, + Computed: true, Description: "storage endpoint provider ID", }, "pool_name": { Type: schema.TypeString, Optional: true, + Computed: true, Description: "pool for image create", }, "architecture": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice([]string{"X86_64", "PPC64_LE"}, false), - Description: "binary architecture of this image, one of X86_64 of PPC64_LE", + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "binary architecture of this image, one of X86_64 of PPC64_LE", }, "image_id": { Type: schema.TypeInt, + Optional: true, Computed: true, Description: "image id", }, "permanently": { Type: schema.TypeBool, Optional: true, + Computed: true, Description: "Whether to completely delete the image", }, "bootable": { Type: schema.TypeBool, Optional: true, + Computed: true, Description: "Does this image boot OS", }, - "virtual": { - Type: schema.TypeMap, + "unc_path": { + Type: schema.TypeString, + Computed: true, + Description: "unc path", + }, + "link_to": { + Type: schema.TypeInt, + Computed: true, + Description: "", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "status", + }, + "tech_status": { + Type: schema.TypeString, + Computed: true, + Description: "tech atatus", + }, + "version": { + Type: schema.TypeString, + Computed: true, + Description: "version", + }, + "size": { + Type: schema.TypeInt, + Computed: true, + Description: "image size", + }, + "enabled": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, + "computeci_id": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "guid": { + Type: schema.TypeInt, + Computed: true, + }, + "milestones": { + Type: schema.TypeInt, + Computed: true, + }, + "provider_name": { + Type: schema.TypeString, + Computed: true, + }, + "purge_attempts": { + Type: schema.TypeInt, + Computed: true, + }, + "reference_id": { + Type: schema.TypeString, + Computed: true, + }, + "res_id": { + Type: schema.TypeString, + Computed: true, + }, + "res_name": { + Type: schema.TypeString, + Computed: true, + }, + "rescuecd": { + Type: schema.TypeBool, + Computed: true, + }, + "reason": { + Type: schema.TypeString, + Optional: true, + }, + "last_modified": { + Type: schema.TypeInt, + Computed: true, + }, + "desc": { + Type: schema.TypeString, + Computed: true, + }, + "shared_with": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + "sync": { + Type: schema.TypeBool, Optional: true, - Description: "Create virtual image", + Default: false, + Description: "Create image from a media identified by URL (in synchronous mode)", + }, + "enabled_stacks": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "history": { + Type: schema.TypeList, + Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - Description: "name of the virtual image to create", + "guid": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeInt, + Computed: true, }, - "v_image_id": { - Type: schema.TypeInt, - Computed: true, - Description: "", + "timestamp": { + Type: schema.TypeInt, + Computed: true, }, }, }, }, - "link_to": { - Type: schema.TypeInt, - Optional: true, - Description: "", - }, } } @@ -384,20 +638,101 @@ func resourceImage() *schema.Resource { Delete: &Timeout60s, Default: &Timeout60s, }, - /*CustomizeDiff: customdiff.All( + CustomizeDiff: customdiff.All( + customdiff.IfValueChange("enabled", func(old, new, meta interface{}) bool { + if old.(bool) != new.(bool) { + return true + } + return false + }, resourceImageChangeEnabled), customdiff.IfValueChange("name", func(old, new, meta interface{}) bool { - return !(old.(string) == new.(string)) + if old.(string) != new.(string) && old.(string) != "" { + return true + } + return false }, resourceImageEditName), - customdiff.IfValueChange("link", func(old, new, meta interface{}) bool { - o := old.(map[string]interface{}) - n := new.(map[string]interface{}) - if o["image_id"].(int) != n["image_id"].(int) && o["target_id"].(int) != n["target_id"].(int) { + customdiff.IfValueChange("shared_with", func(old, new, meta interface{}) bool { + o := old.([]interface{}) + n := new.([]interface{}) + + if len(o) != len(n) { + return true + } else if len(o) == 0 { + return false + } + + count := 0 + for i, v := range n { + if v.(int) == o[i].(int) { + count++ + } + } + if count == 0 { + return true + } + return false + }, resourceImageShare), + customdiff.IfValueChange("computeci_id", func(old, new, meta interface{}) bool { + if old.(int) != new.(int) { + return true + } + return false + }, resourceImageChangeComputeci), + customdiff.IfValueChange("enabled_stacks", func(old, new, meta interface{}) bool { + o := old.([]interface{}) + n := new.([]interface{}) + + if len(o) != len(n) { + return true + } else if len(o) == 0 { + return false + } + count := 0 + for i, v := range n { + if v.(string) == o[i].(string) { + count++ + } + } + if count == 0 { return true } return false - }, resourceImageLink), - ),*/ + }, resourceImageUpdateNodes), + ), Schema: resourceImageSchemaMake(), } } + +func flattenMeta(m []interface{}) []string { + output := []string{} + for _, item := range m { + switch d := item.(type) { + case string: + output = append(output, d) + case int: + output = append(output, strconv.Itoa(d)) + case int64: + output = append(output, strconv.FormatInt(d, 10)) + case float64: + output = append(output, strconv.FormatInt(int64(d), 10)) + default: + output = append(output, "") + } + } + return output +} + +func flattenHistory(history []History) []map[string]interface{} { + temp := make([]map[string]interface{}, 0) + for _, item := range history { + t := map[string]interface{}{ + "id": item.Id, + "guid": item.Guid, + "timestamp": item.Timestamp, + } + + temp = append(temp, t) + } + return temp +} diff --git a/decort/resource_virtual_image.go b/decort/resource_virtual_image.go new file mode 100644 index 0000000..b1cec35 --- /dev/null +++ b/decort/resource_virtual_image.go @@ -0,0 +1,402 @@ +/* +Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved. +Author: Stanislav Solovev, + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration +Technology platfom. + +Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates. +*/ + +package decort + +import ( + "net/url" + "strconv" + + "github.com/hashicorp/terraform-plugin-sdk/helper/customdiff" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + log "github.com/sirupsen/logrus" +) + +func resourceVirtualImageCreate(d *schema.ResourceData, m interface{}) error { + log.Debugf("resourceImageCreate: called for image %s", d.Get("name").(string)) + + controller := m.(*ControllerCfg) + urlValues := &url.Values{} + urlValues.Add("name", d.Get("name").(string)) + urlValues.Add("targetId", strconv.Itoa(d.Get("target_id").(int))) + + imageId, err := controller.decortAPICall("POST", imageCreateVirtualAPI, urlValues) + if err != nil { + return err + } + + d.SetId(imageId) + d.Set("image_id", imageId) + + image, err := utilityImageCheckPresence(d, m) + if err != nil { + return err + } + + d.SetId(strconv.Itoa(image.ImageId)) + d.Set("bootable", image.Bootable) + //d.Set("image_id", image.ImageId) + + err = resourceImageRead(d, m) + if err != nil { + return err + } + + return nil +} + +func resourceVirtualImageSchemaMake() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "name of the virtual image to create", + }, + "target_id": { + Type: schema.TypeInt, + Required: true, + Description: "ID of real image to link this virtual image to upon creation", + }, + "history": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "guid": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeInt, + Computed: true, + }, + "timestamp": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "url": { + Type: schema.TypeString, + Computed: true, + Description: "URL where to download media from", + }, + "gid": { + Type: schema.TypeInt, + Computed: true, + Description: "grid (platform) ID where this template should be create in", + }, + "boot_type": { + Type: schema.TypeString, + Computed: true, + Description: "Boot type of image bios or uefi", + }, + "image_type": { + Type: schema.TypeString, + Computed: true, + Description: "Image type linux, windows or other", + }, + "drivers": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "List of types of compute suitable for image. Example: [ \"KVM_X86\" ]", + }, + "meta": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "meta", + }, + "hot_resize": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "Does this machine supports hot resize", + }, + "username": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Optional username for the image", + }, + "password": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Optional password for the image", + }, + "account_id": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "AccountId to make the image exclusive", + }, + "username_dl": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "username for upload binary media", + }, + "password_dl": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "password for upload binary media", + }, + "sep_id": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "storage endpoint provider ID", + }, + "pool_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "pool for image create", + }, + "architecture": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "binary architecture of this image, one of X86_64 of PPC64_LE", + }, + "image_id": { + Type: schema.TypeInt, + Computed: true, + Description: "image id", + }, + "permanently": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "Whether to completely delete the image", + }, + "bootable": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "Does this image boot OS", + }, + "unc_path": { + Type: schema.TypeString, + Computed: true, + Description: "unc path", + }, + "link_to": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "status", + }, + "tech_status": { + Type: schema.TypeString, + Computed: true, + Description: "tech atatus", + }, + "version": { + Type: schema.TypeString, + Computed: true, + Description: "version", + }, + "size": { + Type: schema.TypeInt, + Computed: true, + Description: "image size", + }, + "enabled": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, + "computeci_id": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "guid": { + Type: schema.TypeInt, + Computed: true, + }, + "milestones": { + Type: schema.TypeInt, + Computed: true, + }, + "provider_name": { + Type: schema.TypeString, + Computed: true, + }, + "purge_attempts": { + Type: schema.TypeInt, + Computed: true, + }, + "reference_id": { + Type: schema.TypeString, + Computed: true, + }, + "res_id": { + Type: schema.TypeString, + Computed: true, + }, + "res_name": { + Type: schema.TypeString, + Computed: true, + }, + "rescuecd": { + Type: schema.TypeBool, + Computed: true, + }, + "reason": { + Type: schema.TypeString, + Optional: true, + }, + "last_modified": { + Type: schema.TypeInt, + Computed: true, + }, + "desc": { + Type: schema.TypeString, + Computed: true, + }, + "enabled_stacks": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "shared_with": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + } +} + +func resourceVirtualImage() *schema.Resource { + return &schema.Resource{ + SchemaVersion: 1, + + Create: resourceVirtualImageCreate, + Read: resourceImageRead, + Update: resourceImageEdit, + Delete: resourceImageDelete, + Exists: resourceImageExists, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: &Timeout60s, + Read: &Timeout30s, + Update: &Timeout60s, + Delete: &Timeout60s, + Default: &Timeout60s, + }, + + CustomizeDiff: customdiff.All( + customdiff.IfValueChange("enabled", func(old, new, meta interface{}) bool { + if old.(bool) != new.(bool) { + return true + } + return false + }, resourceImageChangeEnabled), + customdiff.IfValueChange("link_to", func(old, new, meta interface{}) bool { + if old.(int) != new.(int) { + return true + } + return false + }, resourceImageLink), + customdiff.IfValueChange("name", func(old, new, meta interface{}) bool { + if old.(string) != new.(string) && old.(string) != "" { + return true + } + return false + }, resourceImageEditName), + customdiff.IfValueChange("shared_with", func(old, new, meta interface{}) bool { + o := old.([]interface{}) + n := new.([]interface{}) + + if len(o) != len(n) { + return true + } else if len(o) == 0 { + return false + } + count := 0 + for i, v := range n { + if v.(int) == o[i].(int) { + count++ + } + } + if count == 0 { + return true + } + return false + }, resourceImageShare), + customdiff.IfValueChange("computeci_id", func(old, new, meta interface{}) bool { + if old.(int) != new.(int) { + return true + } + return false + }, resourceImageChangeComputeci), + customdiff.IfValueChange("enabled_stacks", func(old, new, meta interface{}) bool { + o := old.([]interface{}) + n := new.([]interface{}) + + if len(o) != len(n) { + return true + } else if len(o) == 0 { + return false + } + count := 0 + for i, v := range n { + if v.(string) == o[i].(string) { + count++ + } + } + if count == 0 { + return true + } + return false + }, resourceImageUpdateNodes), + ), + + Schema: resourceVirtualImageSchemaMake(), + } +} diff --git a/decort/utility_image.go b/decort/utility_image.go index e23afd3..3b20243 100644 --- a/decort/utility_image.go +++ b/decort/utility_image.go @@ -55,7 +55,7 @@ func utilityImageCheckPresence(d *schema.ResourceData, m interface{}) (*Image, e image := &Image{} if err := json.Unmarshal([]byte(resp), image); err != nil { - return nil, errors.New(fmt.Sprintf(resp, " ", image)) + return nil, errors.New(fmt.Sprint("Can not unmarshall data to image: ", resp, " ", image)) } return image, nil diff --git a/decort/utility_image_list.go b/decort/utility_image_list.go index 13bcc49..bc80677 100644 --- a/decort/utility_image_list.go +++ b/decort/utility_image_list.go @@ -53,7 +53,7 @@ func utilityImageListCheckPresence(d *schema.ResourceData, m interface{}) (Image urlValues.Add("size", strconv.Itoa(size.(int))) } - log.Debugf("utilityGridListCheckPresence: load image list") + log.Debugf("utilityImageListCheckPresence: load image list") imageListRaw, err := controller.decortAPICall("POST", imageListGetAPI, urlValues) if err != nil { return nil, err diff --git a/decort/utility_image_list_stacks.go b/decort/utility_image_list_stacks.go new file mode 100644 index 0000000..c2ebd10 --- /dev/null +++ b/decort/utility_image_list_stacks.go @@ -0,0 +1,56 @@ +/* +Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved. +Author: Stanislav Solovev, , + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration +Technology platfom. + +Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates. +*/ + +package decort + +import ( + "encoding/json" + "net/url" + "strconv" + + log "github.com/sirupsen/logrus" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func utilityImageListStacksCheckPresence(d *schema.ResourceData, m interface{}) (ImageListStacks, error) { + imageListStacks := ImageListStacks{} + controller := m.(*ControllerCfg) + urlValues := &url.Values{} + + urlValues.Add("imageId", strconv.Itoa(d.Get("image_id").(int))) + + log.Debugf("utilityImageListStacksCheckPresence: load image list") + imageListRaw, err := controller.decortAPICall("POST", imageListStacksApi, urlValues) + if err != nil { + return nil, err + } + + err = json.Unmarshal([]byte(imageListRaw), &imageListStacks) + if err != nil { + return nil, err + } + + return imageListStacks, nil +} diff --git a/go.mod b/go.mod index 047b709..6221e3f 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.15 require ( github.com/dgrijalva/jwt-go v3.2.0+incompatible - github.com/google/uuid v1.1.2 + github.com/google/uuid v1.3.0 github.com/hashicorp/terraform-plugin-docs v0.5.1 github.com/hashicorp/terraform-plugin-sdk v1.16.0 github.com/sirupsen/logrus v1.7.0 diff --git a/go.sum b/go.sum index 93b8bde..2f6b550 100644 --- a/go.sum +++ b/go.sum @@ -171,6 +171,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=