providers/openstack/compute.go (159 lines of code) (raw):

// Copyright 2018 The Terraformer Authors. // // 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. package openstack import ( "log" "sort" "strconv" "strings" "github.com/GoogleCloudPlatform/terraformer/terraformutils" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" "github.com/gophercloud/gophercloud/pagination" ) type ComputeGenerator struct { OpenStackService } // createResources iterate on all openstack_compute_instance_v2 func (g *ComputeGenerator) createResources(list *pagination.Pager, volclient *gophercloud.ServiceClient) []terraformutils.Resource { resources := []terraformutils.Resource{} err := list.EachPage(func(page pagination.Page) (bool, error) { servers, err := servers.ExtractServers(page) if err != nil { return false, err } for _, s := range servers { var bds = []map[string]interface{}{} var vol []volumes.Volume t := map[string]interface{}{} if volclient != nil { for _, av := range s.AttachedVolumes { onevol, err := volumes.Get(volclient, av.ID).Extract() if err == nil { vol = append(vol, *onevol) } } sort.SliceStable(vol, func(i, j int) bool { return vol[i].Attachments[0].Device < vol[j].Attachments[0].Device }) var bindex = 0 var dependsOn = "" for _, v := range vol { if v.Bootable == "true" && v.VolumeImageMetadata != nil { bds = append(bds, map[string]interface{}{ "source_type": "image", "uuid": v.VolumeImageMetadata["image_id"], "volume_size": strconv.Itoa(v.Size), "boot_index": strconv.Itoa(bindex), "destination_type": "volume", "delete_on_termination": "false", }) bindex++ } else { tv := map[string]interface{}{} if dependsOn != "" { tv["depends_on"] = []string{dependsOn} } name := s.Name + strings.ReplaceAll(v.Attachments[0].Device, "/dev/", "") rid := s.ID + "/" + v.ID resource := terraformutils.NewResource( rid, name, "openstack_compute_volume_attach_v2", "openstack", map[string]string{}, []string{}, tv, ) dependsOn = "openstack_compute_volume_attach_v2." + terraformutils.TfSanitize(name) tv["instance_name"] = terraformutils.TfSanitize(s.Name) if v.Name == "" { v.Name = v.ID } tv["volume_name"] = terraformutils.TfSanitize(v.Name) resources = append(resources, resource) } } } if len(bds) > 0 { t = map[string]interface{}{"block_device": bds} } resource := terraformutils.NewResource( s.ID, s.Name, "openstack_compute_instance_v2", "openstack", map[string]string{}, []string{}, t, ) resources = append(resources, resource) } return true, nil }) if err != nil { log.Println(err) } return resources } // Generate TerraformResources from OpenStack API, func (g *ComputeGenerator) InitResources() error { opts, err := openstack.AuthOptionsFromEnv() if err != nil { return err } provider, err := openstack.AuthenticatedClient(opts) if err != nil { return err } client, err := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{ Region: g.GetArgs()["region"].(string), }) if err != nil { return err } list := servers.List(client, nil) volclient, err := openstack.NewBlockStorageV3(provider, gophercloud.EndpointOpts{ Region: g.GetArgs()["region"].(string)}) if err != nil { log.Println("VolumeImageMetadata requires blockStorage API v3") volclient = nil } g.Resources = g.createResources(&list, volclient) return nil } func (g *ComputeGenerator) PostConvertHook() error { for i, r := range g.Resources { if r.InstanceInfo.Type == "openstack_compute_volume_attach_v2" { g.Resources[i].Item["volume_id"] = "${openstack_blockstorage_volume_v3." + r.AdditionalFields["volume_name"].(string) + ".id}" g.Resources[i].Item["instance_id"] = "${openstack_compute_instance_v2." + r.AdditionalFields["instance_name"].(string) + ".id}" delete(g.Resources[i].Item, "volume_name") delete(g.Resources[i].Item, "instance_name") delete(g.Resources[i].Item, "device") } if r.InstanceInfo.Type != "openstack_compute_instance_v2" { continue } // Copy "all_metadata.%" to "metadata.%" for k, v := range g.Resources[i].InstanceState.Attributes { if strings.HasPrefix(k, "all_metadata") { newKey := strings.Replace(k, "all_metadata", "metadata", 1) g.Resources[i].InstanceState.Attributes[newKey] = v } } // Replace "all_metadata" to "metadata" // because "all_metadata" field cannot be set as resource argument for k, v := range g.Resources[i].Item { if strings.HasPrefix(k, "all_metadata") { newKey := strings.Replace(k, "all_metadata", "metadata", 1) g.Resources[i].Item[newKey] = v delete(g.Resources[i].Item, k) } } if r.AdditionalFields["block_device"] != nil { bds := r.AdditionalFields["block_device"].([]map[string]interface{}) for bi, bd := range bds { for k, v := range bd { g.Resources[i].InstanceState.Attributes["block_device."+strconv.Itoa(bi)+"."+k] = v.(string) } } g.Resources[i].InstanceState.Attributes["block_device.#"] = strconv.Itoa(len(bds)) } } return nil }