diff --git a/.gitignore b/.gitignore index 2e7cefe..66a0b7a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ decort/vendor/ +examples/ +url_scrapping/ 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..cba38ed --- /dev/null +++ b/decort/data_source_grid.go @@ -0,0 +1,100 @@ +/* +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 ( + "strconv" + + "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(strconv.Itoa(grid.Id)) + 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..3a420da --- /dev/null +++ b/decort/data_source_grid_list.go @@ -0,0 +1,128 @@ +/* +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 flattenGridList(gl GridList) []map[string]interface{} { + res := make([]map[string]interface{}, 0) + for _, item := range gl { + 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 +} + +func dataSourceGridListRead(d *schema.ResourceData, m interface{}) error { + gridList, err := utilityGridListCheckPresence(d, m) + if err != nil { + return err + } + id := uuid.New() + d.SetId(id.String()) + 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..c7e7bd6 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,275 @@ 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" + "strconv" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/helper/validation" ) +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 { - name := d.Get("name").(string) - // rg_id, rgid_set := d.GetOk("rg_id") - accId, accSet := d.GetOk("account_id") - - 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) + image, err := utilityImageCheckPresence(d, m) if err != nil { - return err - } - 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 } + d.SetId(strconv.Itoa(image.Guid)) + flattenImage(d, image) - // 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 - } - } + return nil +} - return fmt.Errorf("Cannot find Image name %s", name) +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", + }, + "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, + 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, + 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", + }, + "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, + 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, + }, + } } func dataSourceImage() *schema.Resource { @@ -89,57 +307,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..ad6f6d9 --- /dev/null +++ b/decort/data_source_image_list.go @@ -0,0 +1,140 @@ +/* +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 flattenImageList(il ImageList) []map[string]interface{} { + res := make([]map[string]interface{}, 0) + for _, item := range il { + 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 +} + +func dataSourceImageListRead(d *schema.ResourceData, m interface{}) error { + imageList, err := utilityImageListCheckPresence(d, m) + if err != nil { + return err + } + id := uuid.New() + d.SetId(id.String()) + 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: dataSourceImageSchemaMake(), + }, + }, + } + + 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/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 c49e7da..7673e3e 100644 --- a/decort/models_api.go +++ b/decort/models_api.go @@ -667,3 +667,112 @@ type SshKeyConfig struct { SshKey string UserShell string } + +//////////////////// +// 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 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"` + 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 + +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" + +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 322c9fd..f7f26a5 100644 --- a/decort/provider.go +++ b/decort/provider.go @@ -99,22 +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_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_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 new file mode 100644 index 0000000..6e206c4 --- /dev/null +++ b/decort/resource_image.go @@ -0,0 +1,738 @@ +/* +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 ( + "errors" + "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 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 := 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 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", 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)) + } + + 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 + } + + 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 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("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("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 +} + +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 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))) + } + + _, 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 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("resourceVirtualImageLink: 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("targetId", strconv.Itoa(d.Get("link_to").(int))) + _, err := c.decortAPICall("POST", imageLinkAPI, urlValues) + if err != nil { + return err + } + + return nil +} + +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))) + 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 + } + temp = "[" + temp + "]" + urlValues.Add("accounts", temp) + _, err := c.decortAPICall("POST", imageShareAPI, urlValues) + if err != nil { + return err + } + + 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 + } + + _, err := c.decortAPICall("POST", api, urlValues) + if err != nil { + return err + } + + 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 + } + temp = "[" + temp + "]" + urlValues.Add("enabledStacks", temp) + _, err := c.decortAPICall("POST", imageUpdateNodesAPI, 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, + Description: "Name of the rescue disk", + }, + "url": { + Type: schema.TypeString, + Required: true, + Description: "URL where to download media from", + }, + "gid": { + Type: schema.TypeInt, + Required: true, + Description: "grid (platform) ID where this template should be create in", + }, + "boot_type": { + Type: schema.TypeString, + Required: true, + Description: "Boot type of image bios or uefi", + }, + "image_type": { + Type: schema.TypeString, + Required: 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, + 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", + }, + "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, + 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{ + "guid": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeInt, + Computed: true, + }, + "timestamp": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + } +} + +func resourceImage() *schema.Resource { + return &schema.Resource{ + SchemaVersion: 1, + + Create: resourceImageCreate, + 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("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: 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_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 new file mode 100644 index 0000000..3b20243 --- /dev/null +++ b/decort/utility_image.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" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func utilityImageCheckPresence(d *schema.ResourceData, m interface{}) (*Image, error) { + controller := m.(*ControllerCfg) + urlValues := &url.Values{} + + 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 + } + + if resp == "" { + return nil, nil + } + + image := &Image{} + if err := json.Unmarshal([]byte(resp), image); err != nil { + 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 new file mode 100644 index 0000000..bc80677 --- /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("utilityImageListCheckPresence: 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 +} 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=