github.com/GoogleCloudPlatform/terraformer@v0.8.18/providers/openstack/compute.go (about) 1 // Copyright 2018 The Terraformer Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package openstack 16 17 import ( 18 "log" 19 "sort" 20 "strconv" 21 "strings" 22 23 "github.com/GoogleCloudPlatform/terraformer/terraformutils" 24 "github.com/gophercloud/gophercloud" 25 "github.com/gophercloud/gophercloud/openstack" 26 "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" 27 "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" 28 "github.com/gophercloud/gophercloud/pagination" 29 ) 30 31 type ComputeGenerator struct { 32 OpenStackService 33 } 34 35 // createResources iterate on all openstack_compute_instance_v2 36 func (g *ComputeGenerator) createResources(list *pagination.Pager, volclient *gophercloud.ServiceClient) []terraformutils.Resource { 37 resources := []terraformutils.Resource{} 38 39 err := list.EachPage(func(page pagination.Page) (bool, error) { 40 servers, err := servers.ExtractServers(page) 41 if err != nil { 42 return false, err 43 } 44 45 for _, s := range servers { 46 var bds = []map[string]interface{}{} 47 var vol []volumes.Volume 48 t := map[string]interface{}{} 49 if volclient != nil { 50 for _, av := range s.AttachedVolumes { 51 onevol, err := volumes.Get(volclient, av.ID).Extract() 52 if err == nil { 53 vol = append(vol, *onevol) 54 } 55 } 56 57 sort.SliceStable(vol, func(i, j int) bool { 58 return vol[i].Attachments[0].Device < vol[j].Attachments[0].Device 59 }) 60 61 var bindex = 0 62 var dependsOn = "" 63 for _, v := range vol { 64 if v.Bootable == "true" && v.VolumeImageMetadata != nil { 65 bds = append(bds, map[string]interface{}{ 66 "source_type": "image", 67 "uuid": v.VolumeImageMetadata["image_id"], 68 "volume_size": strconv.Itoa(v.Size), 69 "boot_index": strconv.Itoa(bindex), 70 "destination_type": "volume", 71 "delete_on_termination": "false", 72 }) 73 bindex++ 74 } else { 75 tv := map[string]interface{}{} 76 if dependsOn != "" { 77 tv["depends_on"] = []string{dependsOn} 78 } 79 80 name := s.Name + strings.ReplaceAll(v.Attachments[0].Device, "/dev/", "") 81 rid := s.ID + "/" + v.ID 82 resource := terraformutils.NewResource( 83 rid, 84 name, 85 "openstack_compute_volume_attach_v2", 86 "openstack", 87 map[string]string{}, 88 []string{}, 89 tv, 90 ) 91 dependsOn = "openstack_compute_volume_attach_v2." + terraformutils.TfSanitize(name) 92 tv["instance_name"] = terraformutils.TfSanitize(s.Name) 93 if v.Name == "" { 94 v.Name = v.ID 95 } 96 tv["volume_name"] = terraformutils.TfSanitize(v.Name) 97 resources = append(resources, resource) 98 } 99 } 100 } 101 102 if len(bds) > 0 { 103 t = map[string]interface{}{"block_device": bds} 104 } 105 106 resource := terraformutils.NewResource( 107 s.ID, 108 s.Name, 109 "openstack_compute_instance_v2", 110 "openstack", 111 map[string]string{}, 112 []string{}, 113 t, 114 ) 115 116 resources = append(resources, resource) 117 } 118 119 return true, nil 120 }) 121 if err != nil { 122 log.Println(err) 123 } 124 return resources 125 } 126 127 // Generate TerraformResources from OpenStack API, 128 func (g *ComputeGenerator) InitResources() error { 129 opts, err := openstack.AuthOptionsFromEnv() 130 if err != nil { 131 return err 132 } 133 134 provider, err := openstack.AuthenticatedClient(opts) 135 if err != nil { 136 return err 137 } 138 139 client, err := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{ 140 Region: g.GetArgs()["region"].(string), 141 }) 142 if err != nil { 143 return err 144 } 145 146 list := servers.List(client, nil) 147 volclient, err := openstack.NewBlockStorageV3(provider, gophercloud.EndpointOpts{ 148 Region: g.GetArgs()["region"].(string)}) 149 if err != nil { 150 log.Println("VolumeImageMetadata requires blockStorage API v3") 151 volclient = nil 152 } 153 g.Resources = g.createResources(&list, volclient) 154 155 return nil 156 } 157 158 func (g *ComputeGenerator) PostConvertHook() error { 159 for i, r := range g.Resources { 160 if r.InstanceInfo.Type == "openstack_compute_volume_attach_v2" { 161 g.Resources[i].Item["volume_id"] = "${openstack_blockstorage_volume_v3." + r.AdditionalFields["volume_name"].(string) + ".id}" 162 g.Resources[i].Item["instance_id"] = "${openstack_compute_instance_v2." + r.AdditionalFields["instance_name"].(string) + ".id}" 163 delete(g.Resources[i].Item, "volume_name") 164 delete(g.Resources[i].Item, "instance_name") 165 delete(g.Resources[i].Item, "device") 166 } 167 if r.InstanceInfo.Type != "openstack_compute_instance_v2" { 168 continue 169 } 170 171 // Copy "all_metadata.%" to "metadata.%" 172 for k, v := range g.Resources[i].InstanceState.Attributes { 173 if strings.HasPrefix(k, "all_metadata") { 174 newKey := strings.Replace(k, "all_metadata", "metadata", 1) 175 g.Resources[i].InstanceState.Attributes[newKey] = v 176 } 177 } 178 // Replace "all_metadata" to "metadata" 179 // because "all_metadata" field cannot be set as resource argument 180 for k, v := range g.Resources[i].Item { 181 if strings.HasPrefix(k, "all_metadata") { 182 newKey := strings.Replace(k, "all_metadata", "metadata", 1) 183 g.Resources[i].Item[newKey] = v 184 delete(g.Resources[i].Item, k) 185 } 186 } 187 if r.AdditionalFields["block_device"] != nil { 188 bds := r.AdditionalFields["block_device"].([]map[string]interface{}) 189 for bi, bd := range bds { 190 for k, v := range bd { 191 g.Resources[i].InstanceState.Attributes["block_device."+strconv.Itoa(bi)+"."+k] = v.(string) 192 } 193 } 194 195 g.Resources[i].InstanceState.Attributes["block_device.#"] = strconv.Itoa(len(bds)) 196 } 197 } 198 199 return nil 200 }