From edf7728cb50e9cc43a8d1ebf29b8b7fd94ece7f7 Mon Sep 17 00:00:00 2001 From: stSolo Date: Sat, 5 Mar 2022 12:31:51 +0300 Subject: [PATCH 1/3] Implement images resource --- decort/models_api.go | 28 +++ decort/provider.go | 1 + decort/resource_image.go | 380 +++++++++++++++++++++++++++++++++++++++ decort/utility_image.go | 55 ++++++ 4 files changed, 464 insertions(+) create mode 100644 decort/resource_image.go create mode 100644 decort/utility_image.go diff --git a/decort/models_api.go b/decort/models_api.go index c49e7da..b2e8a62 100644 --- a/decort/models_api.go +++ b/decort/models_api.go @@ -667,3 +667,31 @@ type SshKeyConfig struct { SshKey string UserShell string } + +// +// images api +// +const imageCreateAPI = "/restmachine/cloudapi/image/create" +const imageGetAPI = "/restmachine/cloudapi/image/get" +const imageDeleteAPI = "/restmachine/cloudapi/image/delete" +const imageEditNameAPI = "/restmachine/cloudapi/image/rename" +const imageLinkAPI = "/restmachine/cloudapi/image/link" + +type Image struct { + ImageId int `json:"imageId"` + 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"` + 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"` +} diff --git a/decort/provider.go b/decort/provider.go index 322c9fd..d85c12a 100644 --- a/decort/provider.go +++ b/decort/provider.go @@ -106,6 +106,7 @@ func Provider() *schema.Provider { "decort_pfw": resourcePfw(), "decort_k8s": resourceK8s(), "decort_k8s_wg": resourceK8sWg(), + "decort_image": resourceImage(), }, DataSourcesMap: map[string]*schema.Resource{ diff --git a/decort/resource_image.go b/decort/resource_image.go new file mode 100644 index 0000000..ea85a85 --- /dev/null +++ b/decort/resource_image.go @@ -0,0 +1,380 @@ +/* +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" + "strings" + + "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" +) + +func resourceImageCreate(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("url", d.Get("url").(string)) + urlValues.Add("gid", strconv.Itoa(d.Get("gid").(int))) + urlValues.Add("boottype", d.Get("boot_type").(string)) + urlValues.Add("imagetype", d.Get("image_type").(string)) + + tstr := strings.Join(d.Get("drivers").([]string), ",") + tstr = "[" + tstr + "]" + urlValues.Add("drivers", tstr) + + if hotresize, ok := d.GetOk("hot_resize"); ok { + urlValues.Add("hotresize", strconv.FormatBool(hotresize.(bool))) + } + 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", accountId.(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("poolName", poolName.(string)) + } + if architecture, ok := d.GetOk("architecture"); ok { + urlValues.Add("architecture", architecture.(string)) + } + + imageId, err := controller.decortAPICall("POST", imageCreateAPI, 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("image_id", image.ImageId) + + return nil +} + +func resourceImageRead(d *schema.ResourceData, m interface{}) error { + log.Debugf("resourceImageRead: called for %s id: %s", d.Get("name").(string), d.Id()) + + image, err := utilityImageCheckPresence(d, m) + if image == nil { + d.SetId("") + return err + } + + d.Set("image_id", image.ImageId) + d.Set("name", image.Name) + d.Set("url", image.Url) + d.Set("gid", image.Gid) + 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("username", image.Username) + d.Set("password", image.Password) + d.Set("account_id", image.AccountId) + d.Set("username_DL", image.UsernameDL) + d.Set("password_DL", image.PasswordDL) + d.Set("sep_id", image.SepId) + d.Set("pool_name", image.PoolName) + d.Set("architecture", image.Architecture) + + return nil +} + +func resourceImageDelete(d *schema.ResourceData, m interface{}) error { + log.Debugf("resourceImageDelete: 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", imageDeleteAPI, urlValues) + if err != nil { + return err + } + d.SetId("") + + return nil +} + +func resourceImageExists(d *schema.ResourceData, m interface{}) (bool, error) { + log.Debugf("resourceImageExists: called for %s, id: %s", d.Get("name").(string), d.Id()) + + image, err := utilityImageCheckPresence(d, m) + if image == nil { + if err != nil { + return false, err + } + return false, nil + } + + return true, nil +} + +func resourceImageEditName(d *schema.ResourceDiff, m interface{}) error { + log.Debugf("resourceImageEditName: 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)) + _, err := c.decortAPICall("POST", imageEditNameAPI, urlValues) + if err != nil { + return err + } + return nil +} + +func resourceImageLink(d *schema.ResourceDiff, m interface{}) error { + log.Debugf("resourceImageLink: 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))) + _, err := c.decortAPICall("POST", imageLinkAPI, urlValues) + if err != nil { + return err + } + + return nil +} + +func resourceImageSchemaMake() map[string]*schema.Schema { + return 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", + }, + "boot_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"bios", "uefi"}, false), + 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, + ForceNew: true, + MinItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + Description: "List of types of compute suitable for image. Example: [ \"KVM_X86\" ]", + }, + "hot_resize": { + Type: schema.TypeBool, + Optional: true, + Description: "Does this machine supports hot resize", + }, + "username": { + Type: schema.TypeString, + Optional: true, + Description: "Optional username for the image", + }, + "password": { + Type: schema.TypeString, + Optional: true, + Description: "Optional password for the image", + }, + "account_id": { + Type: schema.TypeInt, + Optional: true, + Description: "AccountId to make the image exclusive", + }, + "username_DL": { + Type: schema.TypeString, + Optional: true, + Description: "username for upload binary media", + }, + "password_DL": { + Type: schema.TypeString, + Optional: true, + Description: "password for upload binary media", + }, + "sep_id": { + Type: schema.TypeInt, + Optional: true, + Description: "storage endpoint provider ID", + }, + "pool_name": { + Type: schema.TypeString, + Optional: 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", + }, + "image_id": { + Type: schema.TypeInt, + Computed: true, + Description: "image id", + }, + "permanently": { + Type: schema.TypeBool, + Optional: true, + Description: "Whether to completely delete the image", + }, + "virtual": { + Type: schema.TypeMap, + Optional: 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: "", + }, + }, + }, + }, + "link": { + Type: schema.TypeMap, + Optional: true, + Description: "Link virtual image to another image in the platform", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "image_id": { + Type: schema.TypeInt, + Required: true, + Description: "ID of the virtual image", + }, + "target_id": { + Type: schema.TypeInt, + Required: true, + Description: "ID of real image to link this virtual image to", + }, + }, + }, + }, + } +} + +func resourceImage() *schema.Resource { + return &schema.Resource{ + SchemaVersion: 1, + + Create: resourceImageCreate, + Read: resourceImageRead, + 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("name", func(old, new, meta interface{}) bool { + return !(old.(string) == new.(string)) + }, 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) { + return true + } + return false + }, resourceImageLink), + ), + + Schema: resourceImageSchemaMake(), + } +} diff --git a/decort/utility_image.go b/decort/utility_image.go new file mode 100644 index 0000000..60090d3 --- /dev/null +++ b/decort/utility_image.go @@ -0,0 +1,55 @@ +/* +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" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func utilityImageCheckPresence(d *schema.ResourceData, m interface{}) (*Image, error) { + controller := m.(*ControllerCfg) + urlValues := &url.Values{} + + urlValues.Add("imageId", strconv.Itoa(d.Get("image_id").(int))) + resp, err := controller.decortAPICall("POST", imageGetAPI, urlValues) + if err != nil { + return nil, err + } + + if resp == "" { + return nil, nil + } + + var image *Image + if err := json.Unmarshal([]byte(resp), image); err != nil { + return nil, err + } + + return image, nil +} From 9379289e582cca0d7e34cbb6ae222a2bfe215665 Mon Sep 17 00:00:00 2001 From: stSolo Date: Thu, 10 Mar 2022 20:19:56 +0300 Subject: [PATCH 2/3] Fix bugs, add grid, grid list, image, image list data --- .gitignore | 1 + Makefile | 39 ++++++ decort/data_source_grid.go | 98 +++++++++++++ decort/data_source_grid_list.go | 124 ++++++++++++++++ decort/data_source_image.go | 233 ++++++++++++++++++------------- decort/data_source_image_list.go | 106 ++++++++++++++ decort/models_api.go | 36 ++++- decort/provider.go | 15 +- decort/resource_image.go | 93 +++++++----- decort/utility_grid.go | 62 ++++++++ decort/utility_grid_list.go | 61 ++++++++ decort/utility_image.go | 13 +- decort/utility_image_list.go | 68 +++++++++ 13 files changed, 803 insertions(+), 146 deletions(-) create mode 100644 Makefile create mode 100644 decort/data_source_grid.go create mode 100644 decort/data_source_grid_list.go create mode 100644 decort/data_source_image_list.go create mode 100644 decort/utility_grid.go create mode 100644 decort/utility_grid_list.go create mode 100644 decort/utility_image_list.go diff --git a/.gitignore b/.gitignore index 2e7cefe..b293db0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ decort/vendor/ +examples/ terraform-provider-decort* diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7dfe1d4 --- /dev/null +++ b/Makefile @@ -0,0 +1,39 @@ +TEST?=$$(go list ./... | grep -v 'vendor') +HOSTNAME=digitalenergy.online +NAMESPACE=decort +NAME=terraform-provider-decort +#BINARY=terraform-provider-${NAME} +BINARY=${NAME}.exe +VERSION=0.2 +#OS_ARCH=darwin_amd64 +OS_ARCH=windows_amd64 + +default: install + +build: + go build -o ${BINARY} + +release: + GOOS=darwin GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_darwin_amd64 + GOOS=freebsd GOARCH=386 go build -o ./bin/${BINARY}_${VERSION}_freebsd_386 + GOOS=freebsd GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_freebsd_amd64 + GOOS=freebsd GOARCH=arm go build -o ./bin/${BINARY}_${VERSION}_freebsd_arm + GOOS=linux GOARCH=386 go build -o ./bin/${BINARY}_${VERSION}_linux_386 + GOOS=linux GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_linux_amd64 + GOOS=linux GOARCH=arm go build -o ./bin/${BINARY}_${VERSION}_linux_arm + GOOS=openbsd GOARCH=386 go build -o ./bin/${BINARY}_${VERSION}_openbsd_386 + GOOS=openbsd GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_openbsd_amd64 + GOOS=solaris GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_solaris_amd64 + GOOS=windows GOARCH=386 go build -o ./bin/${BINARY}_${VERSION}_windows_386 + GOOS=windows GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_windows_amd64 + +install: build + mkdir -p ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${OS_ARCH} + mv ${BINARY} ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${OS_ARCH} + +test: + go test -i $(TEST) || exit 1 + echo $(TEST) | xargs -t -n4 go test $(TESTARGS) -timeout=30s -parallel=4 + +testacc: + TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout 120m \ No newline at end of file diff --git a/decort/data_source_grid.go b/decort/data_source_grid.go new file mode 100644 index 0000000..ef33329 --- /dev/null +++ b/decort/data_source_grid.go @@ -0,0 +1,98 @@ +/* +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/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func flattenGrid(d *schema.ResourceData, grid *Grid) { + d.Set("name", grid.Name) + d.Set("flag", grid.Flag) + d.Set("gid", grid.Gid) + d.Set("guid", grid.Guid) + d.Set("location_code", grid.LocationCode) + d.Set("id", grid.Id) + return +} + +func dataSourceGridRead(d *schema.ResourceData, m interface{}) error { + grid, err := utilityGridCheckPresence(d, m) + if err != nil { + return err + } + d.SetId("1234") + flattenGrid(d, grid) + + return nil +} + +func dataSourceGetGridSchemaMake() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "grid_id": { + Type: schema.TypeInt, + Required: true, + }, + "flag": { + Type: schema.TypeString, + Computed: true, + }, + "gid": { + Type: schema.TypeInt, + Computed: true, + }, + "guid": { + Type: schema.TypeInt, + Computed: true, + }, + "id": { + Type: schema.TypeInt, + Computed: true, + }, + "location_code": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + } +} + +func dataSourceGrid() *schema.Resource { + return &schema.Resource{ + SchemaVersion: 1, + + Read: dataSourceGridRead, + + Timeouts: &schema.ResourceTimeout{ + Read: &Timeout30s, + Default: &Timeout60s, + }, + + Schema: dataSourceGetGridSchemaMake(), + } +} diff --git a/decort/data_source_grid_list.go b/decort/data_source_grid_list.go new file mode 100644 index 0000000..04e0e96 --- /dev/null +++ b/decort/data_source_grid_list.go @@ -0,0 +1,124 @@ +/* +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/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func flattenGridList(gl GridList) []map[string]interface{} { + res := make([]map[string]interface{}, len(gl), len(gl)) + 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 + res = append(res, temp) + } + return res +} + +func dataSourceGridListRead(d *schema.ResourceData, m interface{}) error { + gridList, err := utilityGridListCheckPresence(d, m) + if err != nil { + return err + } + d.SetId("1234") + d.Set("items", flattenGridList(gridList)) + + return nil +} + +func dataSourceGridListSchemaMake() map[string]*schema.Schema { + rets := map[string]*schema.Schema{ + "page": { + Type: schema.TypeInt, + Optional: true, + Description: "page number", + }, + "size": { + Type: schema.TypeInt, + Optional: true, + Description: "page size", + }, + "items": { + Type: schema.TypeList, + Computed: true, + Description: "grid list", + Elem: &schema.Resource{ + Schema: dataSourceGridSchemaMake(), + }, + }, + } + + return rets +} + +func dataSourceGridSchemaMake() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "flag": { + Type: schema.TypeString, + Computed: true, + }, + "gid": { + Type: schema.TypeInt, + Computed: true, + }, + "guid": { + Type: schema.TypeInt, + Computed: true, + }, + "id": { + Type: schema.TypeInt, + Computed: true, + }, + "location_code": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + } +} + +func dataSourceGridList() *schema.Resource { + return &schema.Resource{ + SchemaVersion: 1, + + Read: dataSourceGridListRead, + + Timeouts: &schema.ResourceTimeout{ + Read: &Timeout30s, + Default: &Timeout60s, + }, + + Schema: dataSourceGridListSchemaMake(), + } +} diff --git a/decort/data_source_image.go b/decort/data_source_image.go index c0b6369..82c73bc 100644 --- a/decort/data_source_image.go +++ b/decort/data_source_image.go @@ -1,6 +1,6 @@ /* -Copyright (c) 2019-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved. -Author: Sergey Shubin, , +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. @@ -25,57 +25,151 @@ Visit https://github.com/rudecs/terraform-provider-decort for full source code p package decort import ( - "encoding/json" - "fmt" - "net/url" - - log "github.com/sirupsen/logrus" - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/helper/validation" ) -func dataSourceImageRead(d *schema.ResourceData, m interface{}) error { - name := d.Get("name").(string) - // rg_id, rgid_set := d.GetOk("rg_id") - accId, accSet := d.GetOk("account_id") +func flattenImage(d *schema.ResourceData, image *Image) { + d.Set("name", image.Name) + 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("sep_id", image.SepId) + return +} - controller := m.(*ControllerCfg) - url_values := &url.Values{} - if accSet { - url_values.Add("accountId", fmt.Sprintf("%d", accId.(int))) - } - body_string, err := controller.decortAPICall("POST", ImagesListAPI, url_values) +func dataSourceImageRead(d *schema.ResourceData, m interface{}) error { + image, err := utilityImageCheckPresence(d, m) if err != nil { return err } + d.SetId("1234") + flattenImage(d, image) - log.Debugf("dataSourceImageRead: ready to decode response body from %s", ImagesListAPI) - model := ImagesListResp{} - err = json.Unmarshal([]byte(body_string), &model) - if err != nil { - return err - } + return nil +} - // log.Printf("%#v", model) - log.Debugf("dataSourceImageRead: traversing decoded JSON of length %d", len(model)) - for index, item := range model { - // need to match Image by name - if item.Name == name { - log.Debugf("dataSourceImageRead: index %d, matched name %s", index, item.Name) - d.SetId(fmt.Sprintf("%d", item.ID)) - d.Set("account_id", item.AccountID) - d.Set("arch", item.Arch) - d.Set("sep_id", item.SepID) - d.Set("pool", item.Pool) - d.Set("status", item.Status) - d.Set("size", item.Size) - // d.Set("field_name", value) - return nil - } +func dataSourceImageSchemaMake() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + Description: "Name of the rescue disk", + }, + "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\" ]", + }, + "hot_resize": { + Type: schema.TypeBool, + Computed: true, + Description: "Does this machine supports hot resize", + }, + "username": { + Type: schema.TypeString, + Computed: true, + Description: "Optional username for the image", + }, + "password": { + Type: schema.TypeString, + Computed: true, + Description: "Optional password for the image", + }, + "account_id": { + Type: schema.TypeInt, + Computed: true, + Description: "AccountId to make the image exclusive", + }, + "username_dl": { + Type: schema.TypeString, + Computed: true, + Description: "username for upload binary media", + }, + "password_dl": { + Type: schema.TypeString, + Computed: true, + Description: "password for upload binary media", + }, + "sep_id": { + Type: schema.TypeInt, + Computed: true, + Description: "storage endpoint provider ID", + }, + "pool_name": { + Type: schema.TypeString, + Computed: true, + Description: "pool for image create", + }, + "architecture": { + Type: schema.TypeString, + Computed: true, + Description: "binary architecture of this image, one of X86_64 of PPC64_LE", + }, + "image_id": { + Type: schema.TypeInt, + Required: true, + Description: "image id", + }, + "permanently": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether to completely delete the image", + }, + "bootable": { + Type: schema.TypeBool, + Computed: true, + Description: "Does this image boot OS", + }, + "virtual": { + Type: schema.TypeMap, + 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: "", + }, + }, + }, + }, + "link_to": { + Type: schema.TypeInt, + Computed: true, + Description: "", + }, } - - return fmt.Errorf("Cannot find Image name %s", name) } func dataSourceImage() *schema.Resource { @@ -89,57 +183,6 @@ func dataSourceImage() *schema.Resource { Default: &Timeout60s, }, - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - Description: "Name of the image to locate. This parameter is case sensitive.", - }, - - "account_id": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntAtLeast(1), - Description: "Optional ID of the account to limit image search to.", - }, - - "arch": { - Type: schema.TypeString, - Computed: true, - Description: "Binary architecture of this image.", - }, - - "sep_id": { - Type: schema.TypeInt, - Computed: true, - Description: "Storage end-point provider serving this image.", - }, - - /* - "sep_type": { - Type: schema.TypeString, - Computed: true, - Description: "Type of the storage end-point provider serving this image.", - }, - */ - - "pool": { - Type: schema.TypeString, - Computed: true, - Description: "Pool where this image is located.", - }, - - "size": { - Type: schema.TypeInt, - Computed: true, - Description: "Size of the image in GB.", - }, - - "status": { - Type: schema.TypeString, - Computed: true, - Description: "Current model status of this image.", - }, - }, + Schema: dataSourceImageSchemaMake(), } } diff --git a/decort/data_source_image_list.go b/decort/data_source_image_list.go new file mode 100644 index 0000000..0d611ec --- /dev/null +++ b/decort/data_source_image_list.go @@ -0,0 +1,106 @@ +/* +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/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func flattenImageList(il ImageList) []map[string]interface{} { + res := make([]map[string]interface{}, len(il), len(il)) + 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 + res = append(res, temp) + } + return res +} + +func dataSourceImageListRead(d *schema.ResourceData, m interface{}) error { + imageList, err := utilityImageListCheckPresence(d, m) + if err != nil { + return err + } + d.SetId("1234") + d.Set("items", flattenImageList(imageList)) + + return nil +} + +func dataSourceImageListSchemaMake() map[string]*schema.Schema { + rets := map[string]*schema.Schema{ + "sep_id": { + Type: schema.TypeInt, + Optional: true, + Description: "filter images by storage endpoint provider ID", + }, + "shared_with": { + Type: schema.TypeInt, + Optional: true, + Description: "filter images by account ID availability", + }, + "page": { + Type: schema.TypeInt, + Optional: true, + Description: "page number", + }, + "size": { + Type: schema.TypeInt, + Optional: true, + Description: "page size", + }, + "items": { + Type: schema.TypeList, + Computed: true, + Description: "image list", + Elem: &schema.Resource{ + Schema: resourceImageSchemaMake(), + }, + }, + } + + return rets +} + +func dataSourceImageList() *schema.Resource { + return &schema.Resource{ + SchemaVersion: 1, + + Read: dataSourceImageListRead, + + Timeouts: &schema.ResourceTimeout{ + Read: &Timeout30s, + Default: &Timeout60s, + }, + + Schema: dataSourceImageListSchemaMake(), + } +} diff --git a/decort/models_api.go b/decort/models_api.go index b2e8a62..a7af6ca 100644 --- a/decort/models_api.go +++ b/decort/models_api.go @@ -668,24 +668,27 @@ type SshKeyConfig struct { UserShell string } -// +///////////////// // images api -// -const imageCreateAPI = "/restmachine/cloudapi/image/create" -const imageGetAPI = "/restmachine/cloudapi/image/get" -const imageDeleteAPI = "/restmachine/cloudapi/image/delete" +//////////////////// +const imageCreateAPI = "/restmachine/cloudbroker/image/createImage" +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" type Image struct { - ImageId int `json:"imageId"` + ImageId int `json:"id"` Name string `json:"name"` Url string `json:"url"` Gid int `json:"gid"` - Boottype string `json:"boottype"` + 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"` @@ -695,3 +698,22 @@ type Image struct { PoolName string `json:"poolName"` Architecture string `json:"architecture"` } + +type ImageList []Image + +///////////// +////GRID +//////////////// +const GridListGetAPI = "/restmachine/cloudbroker/grid/list" +const GridGetAPI = "/restmachine/cloudbroker/grid/get" + +type Grid struct { + Flag string `json:"flag"` + Gid int `json:"gid"` + Guid int `json:"guid"` + Id int `json:"id"` + LocationCode string `json:"locationCode"` + Name string `json:"name"` +} + +type GridList []Grid diff --git a/decort/provider.go b/decort/provider.go index d85c12a..d5cf0e2 100644 --- a/decort/provider.go +++ b/decort/provider.go @@ -110,12 +110,15 @@ func Provider() *schema.Provider { }, DataSourcesMap: map[string]*schema.Resource{ - "decort_account": dataSourceAccount(), - "decort_resgroup": dataSourceResgroup(), - "decort_kvmvm": dataSourceCompute(), - "decort_image": dataSourceImage(), - "decort_disk": dataSourceDisk(), - "decort_vins": dataSourceVins(), + "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_pfw": dataSourcePfw(), }, diff --git a/decort/resource_image.go b/decort/resource_image.go index ea85a85..21f6877 100644 --- a/decort/resource_image.go +++ b/decort/resource_image.go @@ -27,9 +27,7 @@ package decort import ( "net/url" "strconv" - "strings" - "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,9 +44,14 @@ func resourceImageCreate(d *schema.ResourceData, m interface{}) error { urlValues.Add("boottype", d.Get("boot_type").(string)) urlValues.Add("imagetype", d.Get("image_type").(string)) - tstr := strings.Join(d.Get("drivers").([]string), ",") - tstr = "[" + tstr + "]" - urlValues.Add("drivers", tstr) + tstr := d.Get("drivers").([]interface{}) + temp := "" + for _, str := range tstr { + s := "\"" + str.(string) + "\"" + temp = temp + s + } + temp = "[" + temp + "]" + urlValues.Add("drivers", temp) if hotresize, ok := d.GetOk("hot_resize"); ok { urlValues.Add("hotresize", strconv.FormatBool(hotresize.(bool))) @@ -65,10 +68,10 @@ func resourceImageCreate(d *schema.ResourceData, m interface{}) error { if accountId, ok := d.GetOk("account_id"); ok { urlValues.Add("accountId", strconv.Itoa(accountId.(int))) } - if usernameDL, ok := d.GetOk("username_DL"); ok { + if usernameDL, ok := d.GetOk("username_dl"); ok { urlValues.Add("usernameDL", usernameDL.(string)) } - if passwordDL, ok := d.GetOk("password_DL"); ok { + if passwordDL, ok := d.GetOk("password_dl"); ok { urlValues.Add("passwordDL", passwordDL.(string)) } if sepId, ok := d.GetOk("sep_id"); ok { @@ -95,7 +98,8 @@ func resourceImageCreate(d *schema.ResourceData, m interface{}) error { } d.SetId(strconv.Itoa(image.ImageId)) - d.Set("image_id", image.ImageId) + d.Set("bootable", image.Bootable) + //d.Set("image_id", image.ImageId) return nil } @@ -120,11 +124,12 @@ func resourceImageRead(d *schema.ResourceData, m interface{}) error { 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_DL", image.PasswordDL) + d.Set("username_dl", image.UsernameDL) + d.Set("password_dl", image.PasswordDL) d.Set("sep_id", image.SepId) d.Set("pool_name", image.PoolName) d.Set("architecture", image.Architecture) + d.Set("bootable", image.Bootable) return nil } @@ -143,6 +148,7 @@ 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 permanently, ok := d.GetOk("permanently"); ok { urlValues.Add("permanently", strconv.FormatBool(permanently.(bool))) } @@ -198,6 +204,34 @@ 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()) + 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)) + } + 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 bootable, ok := d.GetOk("bootable"); ok { + urlValues.Add("bootable", strconv.FormatBool(bootable.(bool))) + } + if hotresize, ok := d.GetOk("hot_resize"); ok { + urlValues.Add("hotresize", strconv.FormatBool(hotresize.(bool))) + } + _, err := c.decortAPICall("POST", imageEditAPI, urlValues) + if err != nil { + return err + } + return nil +} + func resourceImageSchemaMake() map[string]*schema.Schema { return map[string]*schema.Schema{ "name": { @@ -235,12 +269,9 @@ func resourceImageSchemaMake() map[string]*schema.Schema { "drivers": { Type: schema.TypeList, Required: true, - ForceNew: true, MinItems: 1, Elem: &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, }, Description: "List of types of compute suitable for image. Example: [ \"KVM_X86\" ]", }, @@ -264,12 +295,12 @@ func resourceImageSchemaMake() map[string]*schema.Schema { Optional: true, Description: "AccountId to make the image exclusive", }, - "username_DL": { + "username_dl": { Type: schema.TypeString, Optional: true, Description: "username for upload binary media", }, - "password_DL": { + "password_dl": { Type: schema.TypeString, Optional: true, Description: "password for upload binary media", @@ -300,6 +331,11 @@ func resourceImageSchemaMake() map[string]*schema.Schema { Optional: true, Description: "Whether to completely delete the image", }, + "bootable": { + Type: schema.TypeBool, + Optional: true, + Description: "Does this image boot OS", + }, "virtual": { Type: schema.TypeMap, Optional: true, @@ -319,24 +355,10 @@ func resourceImageSchemaMake() map[string]*schema.Schema { }, }, }, - "link": { - Type: schema.TypeMap, + "link_to": { + Type: schema.TypeInt, Optional: true, - Description: "Link virtual image to another image in the platform", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "image_id": { - Type: schema.TypeInt, - Required: true, - Description: "ID of the virtual image", - }, - "target_id": { - Type: schema.TypeInt, - Required: true, - Description: "ID of real image to link this virtual image to", - }, - }, - }, + Description: "", }, } } @@ -347,6 +369,7 @@ func resourceImage() *schema.Resource { Create: resourceImageCreate, Read: resourceImageRead, + Update: resourceImageEdit, Delete: resourceImageDelete, Exists: resourceImageExists, @@ -361,7 +384,7 @@ func resourceImage() *schema.Resource { Delete: &Timeout60s, Default: &Timeout60s, }, - CustomizeDiff: customdiff.All( + /*CustomizeDiff: customdiff.All( customdiff.IfValueChange("name", func(old, new, meta interface{}) bool { return !(old.(string) == new.(string)) }, resourceImageEditName), @@ -373,7 +396,7 @@ func resourceImage() *schema.Resource { } return false }, resourceImageLink), - ), + ),*/ Schema: resourceImageSchemaMake(), } diff --git a/decort/utility_grid.go b/decort/utility_grid.go new file mode 100644 index 0000000..2131cde --- /dev/null +++ b/decort/utility_grid.go @@ -0,0 +1,62 @@ +/* +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" + "errors" + "fmt" + "net/url" + "strconv" + + log "github.com/sirupsen/logrus" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func utilityGridCheckPresence(d *schema.ResourceData, m interface{}) (*Grid, error) { + grid := &Grid{} + controller := m.(*ControllerCfg) + urlValues := &url.Values{} + + if gridId, ok := d.GetOk("grid_id"); ok { + urlValues.Add("gridId", strconv.Itoa(gridId.(int))) + } else { + return nil, errors.New(fmt.Sprintf("grid_id is required")) + } + + log.Debugf("utilityGridCheckPresence: load grid") + gridRaw, err := controller.decortAPICall("POST", GridGetAPI, urlValues) + if err != nil { + return nil, err + } + + err = json.Unmarshal([]byte(gridRaw), grid) + if err != nil { + return nil, err + } + + return grid, nil +} diff --git a/decort/utility_grid_list.go b/decort/utility_grid_list.go new file mode 100644 index 0000000..202f6ad --- /dev/null +++ b/decort/utility_grid_list.go @@ -0,0 +1,61 @@ +/* +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 utilityGridListCheckPresence(d *schema.ResourceData, m interface{}) (GridList, error) { + gridList := GridList{} + controller := m.(*ControllerCfg) + urlValues := &url.Values{} + + if page, ok := d.GetOk("page"); ok { + urlValues.Add("page", strconv.Itoa(page.(int))) + } + if size, ok := d.GetOk("size"); ok { + urlValues.Add("size", strconv.Itoa(size.(int))) + } + + log.Debugf("utilityGridListCheckPresence: load grid list") + gridListRaw, err := controller.decortAPICall("POST", GridListGetAPI, urlValues) + if err != nil { + return nil, err + } + + err = json.Unmarshal([]byte(gridListRaw), &gridList) + if err != nil { + return nil, err + } + + return gridList, nil +} diff --git a/decort/utility_image.go b/decort/utility_image.go index 60090d3..e23afd3 100644 --- a/decort/utility_image.go +++ b/decort/utility_image.go @@ -26,6 +26,8 @@ package decort import ( "encoding/json" + "errors" + "fmt" "net/url" "strconv" @@ -36,7 +38,12 @@ func utilityImageCheckPresence(d *schema.ResourceData, m interface{}) (*Image, e controller := m.(*ControllerCfg) urlValues := &url.Values{} - urlValues.Add("imageId", strconv.Itoa(d.Get("image_id").(int))) + if (strconv.Itoa(d.Get("image_id").(int))) != "0" { + urlValues.Add("imageId", strconv.Itoa(d.Get("image_id").(int))) + } else { + urlValues.Add("imageId", d.Id()) + } + resp, err := controller.decortAPICall("POST", imageGetAPI, urlValues) if err != nil { return nil, err @@ -46,9 +53,9 @@ func utilityImageCheckPresence(d *schema.ResourceData, m interface{}) (*Image, e return nil, nil } - var image *Image + image := &Image{} if err := json.Unmarshal([]byte(resp), image); err != nil { - return nil, err + return nil, errors.New(fmt.Sprintf(resp, " ", image)) } return image, nil diff --git a/decort/utility_image_list.go b/decort/utility_image_list.go new file mode 100644 index 0000000..13bcc49 --- /dev/null +++ b/decort/utility_image_list.go @@ -0,0 +1,68 @@ +/* +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 utilityImageListCheckPresence(d *schema.ResourceData, m interface{}) (ImageList, error) { + imageList := ImageList{} + controller := m.(*ControllerCfg) + urlValues := &url.Values{} + + if sepId, ok := d.GetOk("sep_id"); ok { + urlValues.Add("sepId", strconv.Itoa(sepId.(int))) + } + if sharedWith, ok := d.GetOk("shared_with"); ok { + urlValues.Add("sharedWith", strconv.Itoa(sharedWith.(int))) + } + + if page, ok := d.GetOk("page"); ok { + urlValues.Add("page", strconv.Itoa(page.(int))) + } + if size, ok := d.GetOk("size"); ok { + urlValues.Add("size", strconv.Itoa(size.(int))) + } + + log.Debugf("utilityGridListCheckPresence: load image list") + imageListRaw, err := controller.decortAPICall("POST", imageListGetAPI, urlValues) + if err != nil { + return nil, err + } + + err = json.Unmarshal([]byte(imageListRaw), &imageList) + if err != nil { + return nil, err + } + + return imageList, nil +} From bf179b9d128de8771e852b31f5ede56cdacd39e3 Mon Sep 17 00:00:00 2001 From: stSolo Date: Fri, 18 Mar 2022 20:49:16 +0300 Subject: [PATCH 3/3] Add some resources and data-resources, fix bugs --- .gitignore | 1 + decort/data_source_grid.go | 4 +- decort/data_source_grid_list.go | 22 +- decort/data_source_image.go | 160 +++++++- decort/data_source_image_list.go | 56 ++- decort/data_source_image_list_stacks.go | 182 +++++++++ decort/models_api.go | 107 ++++-- decort/provider.go | 38 +- decort/resource_cdrom_image.go | 449 ++++++++++++++++++++++ decort/resource_delete_images.go | 131 +++++++ decort/resource_image.go | 485 ++++++++++++++++++++---- decort/resource_virtual_image.go | 402 ++++++++++++++++++++ decort/utility_image.go | 2 +- decort/utility_image_list.go | 2 +- decort/utility_image_list_stacks.go | 56 +++ go.mod | 2 +- go.sum | 2 + 17 files changed, 1943 insertions(+), 158 deletions(-) create mode 100644 decort/data_source_image_list_stacks.go create mode 100644 decort/resource_cdrom_image.go create mode 100644 decort/resource_delete_images.go create mode 100644 decort/resource_virtual_image.go create mode 100644 decort/utility_image_list_stacks.go 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=