github.com/richardbowden/terraform@v0.6.12-0.20160901200758-30ea22c25211/builtin/providers/openstack/resource_openstack_networking_network_v2.go (about) 1 package openstack 2 3 import ( 4 "fmt" 5 "log" 6 "strconv" 7 "time" 8 9 "github.com/hashicorp/terraform/helper/resource" 10 "github.com/hashicorp/terraform/helper/schema" 11 12 "github.com/rackspace/gophercloud" 13 "github.com/rackspace/gophercloud/openstack/networking/v2/networks" 14 ) 15 16 func resourceNetworkingNetworkV2() *schema.Resource { 17 return &schema.Resource{ 18 Create: resourceNetworkingNetworkV2Create, 19 Read: resourceNetworkingNetworkV2Read, 20 Update: resourceNetworkingNetworkV2Update, 21 Delete: resourceNetworkingNetworkV2Delete, 22 Importer: &schema.ResourceImporter{ 23 State: schema.ImportStatePassthrough, 24 }, 25 26 Schema: map[string]*schema.Schema{ 27 "region": &schema.Schema{ 28 Type: schema.TypeString, 29 Required: true, 30 ForceNew: true, 31 DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""), 32 }, 33 "name": &schema.Schema{ 34 Type: schema.TypeString, 35 Optional: true, 36 ForceNew: false, 37 }, 38 "admin_state_up": &schema.Schema{ 39 Type: schema.TypeString, 40 Optional: true, 41 ForceNew: false, 42 Computed: true, 43 }, 44 "shared": &schema.Schema{ 45 Type: schema.TypeString, 46 Optional: true, 47 ForceNew: false, 48 Computed: true, 49 }, 50 "tenant_id": &schema.Schema{ 51 Type: schema.TypeString, 52 Optional: true, 53 ForceNew: true, 54 Computed: true, 55 }, 56 "value_specs": &schema.Schema{ 57 Type: schema.TypeMap, 58 Optional: true, 59 ForceNew: true, 60 }, 61 }, 62 } 63 } 64 65 // NetworkCreateOpts contains all teh values needed to create a new network. 66 type NetworkCreateOpts struct { 67 AdminStateUp *bool 68 Name string 69 Shared *bool 70 TenantID string 71 ValueSpecs map[string]string 72 } 73 74 // ToNetworkCreateMpa casts a networkCreateOpts struct to a map. 75 func (opts NetworkCreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) { 76 n := make(map[string]interface{}) 77 78 if opts.AdminStateUp != nil { 79 n["admin_state_up"] = &opts.AdminStateUp 80 } 81 if opts.Name != "" { 82 n["name"] = opts.Name 83 } 84 if opts.Shared != nil { 85 n["shared"] = &opts.Shared 86 } 87 if opts.TenantID != "" { 88 n["tenant_id"] = opts.TenantID 89 } 90 91 if opts.ValueSpecs != nil { 92 for k, v := range opts.ValueSpecs { 93 n[k] = v 94 } 95 } 96 97 return map[string]interface{}{"network": n}, nil 98 } 99 100 func resourceNetworkingNetworkV2Create(d *schema.ResourceData, meta interface{}) error { 101 config := meta.(*Config) 102 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 103 if err != nil { 104 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 105 } 106 107 createOpts := NetworkCreateOpts{ 108 Name: d.Get("name").(string), 109 TenantID: d.Get("tenant_id").(string), 110 ValueSpecs: networkValueSpecs(d), 111 } 112 113 asuRaw := d.Get("admin_state_up").(string) 114 if asuRaw != "" { 115 asu, err := strconv.ParseBool(asuRaw) 116 if err != nil { 117 return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'") 118 } 119 createOpts.AdminStateUp = &asu 120 } 121 122 sharedRaw := d.Get("shared").(string) 123 if sharedRaw != "" { 124 shared, err := strconv.ParseBool(sharedRaw) 125 if err != nil { 126 return fmt.Errorf("shared, if provided, must be either 'true' or 'false': %v", err) 127 } 128 createOpts.Shared = &shared 129 } 130 131 log.Printf("[DEBUG] Create Options: %#v", createOpts) 132 n, err := networks.Create(networkingClient, createOpts).Extract() 133 if err != nil { 134 return fmt.Errorf("Error creating OpenStack Neutron network: %s", err) 135 } 136 log.Printf("[INFO] Network ID: %s", n.ID) 137 138 log.Printf("[DEBUG] Waiting for Network (%s) to become available", n.ID) 139 140 stateConf := &resource.StateChangeConf{ 141 Pending: []string{"BUILD"}, 142 Target: []string{"ACTIVE"}, 143 Refresh: waitForNetworkActive(networkingClient, n.ID), 144 Timeout: 2 * time.Minute, 145 Delay: 5 * time.Second, 146 MinTimeout: 3 * time.Second, 147 } 148 149 _, err = stateConf.WaitForState() 150 151 d.SetId(n.ID) 152 153 return resourceNetworkingNetworkV2Read(d, meta) 154 } 155 156 func resourceNetworkingNetworkV2Read(d *schema.ResourceData, meta interface{}) error { 157 config := meta.(*Config) 158 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 159 if err != nil { 160 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 161 } 162 163 n, err := networks.Get(networkingClient, d.Id()).Extract() 164 if err != nil { 165 return CheckDeleted(d, err, "network") 166 } 167 168 log.Printf("[DEBUG] Retreived Network %s: %+v", d.Id(), n) 169 170 d.Set("name", n.Name) 171 d.Set("admin_state_up", strconv.FormatBool(n.AdminStateUp)) 172 d.Set("shared", strconv.FormatBool(n.Shared)) 173 d.Set("tenant_id", n.TenantID) 174 175 return nil 176 } 177 178 func resourceNetworkingNetworkV2Update(d *schema.ResourceData, meta interface{}) error { 179 config := meta.(*Config) 180 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 181 if err != nil { 182 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 183 } 184 185 var updateOpts networks.UpdateOpts 186 if d.HasChange("name") { 187 updateOpts.Name = d.Get("name").(string) 188 } 189 if d.HasChange("admin_state_up") { 190 asuRaw := d.Get("admin_state_up").(string) 191 if asuRaw != "" { 192 asu, err := strconv.ParseBool(asuRaw) 193 if err != nil { 194 return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'") 195 } 196 updateOpts.AdminStateUp = &asu 197 } 198 } 199 if d.HasChange("shared") { 200 sharedRaw := d.Get("shared").(string) 201 if sharedRaw != "" { 202 shared, err := strconv.ParseBool(sharedRaw) 203 if err != nil { 204 return fmt.Errorf("shared, if provided, must be either 'true' or 'false': %v", err) 205 } 206 updateOpts.Shared = &shared 207 } 208 } 209 210 log.Printf("[DEBUG] Updating Network %s with options: %+v", d.Id(), updateOpts) 211 212 _, err = networks.Update(networkingClient, d.Id(), updateOpts).Extract() 213 if err != nil { 214 return fmt.Errorf("Error updating OpenStack Neutron Network: %s", err) 215 } 216 217 return resourceNetworkingNetworkV2Read(d, meta) 218 } 219 220 func resourceNetworkingNetworkV2Delete(d *schema.ResourceData, meta interface{}) error { 221 config := meta.(*Config) 222 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 223 if err != nil { 224 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 225 } 226 227 stateConf := &resource.StateChangeConf{ 228 Pending: []string{"ACTIVE"}, 229 Target: []string{"DELETED"}, 230 Refresh: waitForNetworkDelete(networkingClient, d.Id()), 231 Timeout: 2 * time.Minute, 232 Delay: 5 * time.Second, 233 MinTimeout: 3 * time.Second, 234 } 235 236 _, err = stateConf.WaitForState() 237 if err != nil { 238 return fmt.Errorf("Error deleting OpenStack Neutron Network: %s", err) 239 } 240 241 d.SetId("") 242 return nil 243 } 244 245 func waitForNetworkActive(networkingClient *gophercloud.ServiceClient, networkId string) resource.StateRefreshFunc { 246 return func() (interface{}, string, error) { 247 n, err := networks.Get(networkingClient, networkId).Extract() 248 if err != nil { 249 return nil, "", err 250 } 251 252 log.Printf("[DEBUG] OpenStack Neutron Network: %+v", n) 253 if n.Status == "DOWN" || n.Status == "ACTIVE" { 254 return n, "ACTIVE", nil 255 } 256 257 return n, n.Status, nil 258 } 259 } 260 261 func waitForNetworkDelete(networkingClient *gophercloud.ServiceClient, networkId string) resource.StateRefreshFunc { 262 return func() (interface{}, string, error) { 263 log.Printf("[DEBUG] Attempting to delete OpenStack Network %s.\n", networkId) 264 265 n, err := networks.Get(networkingClient, networkId).Extract() 266 if err != nil { 267 errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) 268 if !ok { 269 return n, "ACTIVE", err 270 } 271 if errCode.Actual == 404 { 272 log.Printf("[DEBUG] Successfully deleted OpenStack Network %s", networkId) 273 return n, "DELETED", nil 274 } 275 } 276 277 err = networks.Delete(networkingClient, networkId).ExtractErr() 278 if err != nil { 279 errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) 280 if !ok { 281 return n, "ACTIVE", err 282 } 283 if errCode.Actual == 404 { 284 log.Printf("[DEBUG] Successfully deleted OpenStack Network %s", networkId) 285 return n, "DELETED", nil 286 } 287 } 288 289 log.Printf("[DEBUG] OpenStack Network %s still active.\n", networkId) 290 return n, "ACTIVE", nil 291 } 292 } 293 294 func networkValueSpecs(d *schema.ResourceData) map[string]string { 295 m := make(map[string]string) 296 for key, val := range d.Get("value_specs").(map[string]interface{}) { 297 m[key] = val.(string) 298 } 299 return m 300 }