github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/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/gophercloud/gophercloud" 13 "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/provider" 14 "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" 15 ) 16 17 func resourceNetworkingNetworkV2() *schema.Resource { 18 return &schema.Resource{ 19 Create: resourceNetworkingNetworkV2Create, 20 Read: resourceNetworkingNetworkV2Read, 21 Update: resourceNetworkingNetworkV2Update, 22 Delete: resourceNetworkingNetworkV2Delete, 23 Importer: &schema.ResourceImporter{ 24 State: schema.ImportStatePassthrough, 25 }, 26 27 Timeouts: &schema.ResourceTimeout{ 28 Create: schema.DefaultTimeout(10 * time.Minute), 29 Delete: schema.DefaultTimeout(10 * time.Minute), 30 }, 31 32 Schema: map[string]*schema.Schema{ 33 "region": &schema.Schema{ 34 Type: schema.TypeString, 35 Required: true, 36 ForceNew: true, 37 DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""), 38 }, 39 "name": &schema.Schema{ 40 Type: schema.TypeString, 41 Optional: true, 42 ForceNew: false, 43 }, 44 "admin_state_up": &schema.Schema{ 45 Type: schema.TypeString, 46 Optional: true, 47 ForceNew: false, 48 Computed: true, 49 }, 50 "shared": &schema.Schema{ 51 Type: schema.TypeString, 52 Optional: true, 53 ForceNew: false, 54 Computed: true, 55 }, 56 "tenant_id": &schema.Schema{ 57 Type: schema.TypeString, 58 Optional: true, 59 ForceNew: true, 60 Computed: true, 61 }, 62 "segments": &schema.Schema{ 63 Type: schema.TypeList, 64 Optional: true, 65 ForceNew: true, 66 Elem: &schema.Resource{ 67 Schema: map[string]*schema.Schema{ 68 "physical_network": &schema.Schema{ 69 Type: schema.TypeString, 70 Optional: true, 71 ForceNew: true, 72 }, 73 "network_type": &schema.Schema{ 74 Type: schema.TypeString, 75 Optional: true, 76 ForceNew: true, 77 }, 78 "segmentation_id": &schema.Schema{ 79 Type: schema.TypeInt, 80 Optional: true, 81 ForceNew: true, 82 }, 83 }, 84 }, 85 }, 86 "value_specs": &schema.Schema{ 87 Type: schema.TypeMap, 88 Optional: true, 89 ForceNew: true, 90 }, 91 }, 92 } 93 } 94 95 func resourceNetworkingNetworkV2Create(d *schema.ResourceData, meta interface{}) error { 96 config := meta.(*Config) 97 networkingClient, err := config.networkingV2Client(GetRegion(d)) 98 if err != nil { 99 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 100 } 101 102 createOpts := NetworkCreateOpts{ 103 networks.CreateOpts{ 104 Name: d.Get("name").(string), 105 TenantID: d.Get("tenant_id").(string), 106 }, 107 MapValueSpecs(d), 108 } 109 110 asuRaw := d.Get("admin_state_up").(string) 111 if asuRaw != "" { 112 asu, err := strconv.ParseBool(asuRaw) 113 if err != nil { 114 return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'") 115 } 116 createOpts.AdminStateUp = &asu 117 } 118 119 sharedRaw := d.Get("shared").(string) 120 if sharedRaw != "" { 121 shared, err := strconv.ParseBool(sharedRaw) 122 if err != nil { 123 return fmt.Errorf("shared, if provided, must be either 'true' or 'false': %v", err) 124 } 125 createOpts.Shared = &shared 126 } 127 128 segments := resourceNetworkingNetworkV2Segments(d) 129 130 n := &networks.Network{} 131 if len(segments) > 0 { 132 providerCreateOpts := provider.CreateOptsExt{ 133 CreateOptsBuilder: createOpts, 134 Segments: segments, 135 } 136 log.Printf("[DEBUG] Create Options: %#v", providerCreateOpts) 137 n, err = networks.Create(networkingClient, providerCreateOpts).Extract() 138 } else { 139 log.Printf("[DEBUG] Create Options: %#v", createOpts) 140 n, err = networks.Create(networkingClient, createOpts).Extract() 141 } 142 143 if err != nil { 144 return fmt.Errorf("Error creating OpenStack Neutron network: %s", err) 145 } 146 147 log.Printf("[INFO] Network ID: %s", n.ID) 148 149 log.Printf("[DEBUG] Waiting for Network (%s) to become available", n.ID) 150 151 stateConf := &resource.StateChangeConf{ 152 Pending: []string{"BUILD"}, 153 Target: []string{"ACTIVE"}, 154 Refresh: waitForNetworkActive(networkingClient, n.ID), 155 Timeout: d.Timeout(schema.TimeoutCreate), 156 Delay: 5 * time.Second, 157 MinTimeout: 3 * time.Second, 158 } 159 160 _, err = stateConf.WaitForState() 161 162 d.SetId(n.ID) 163 164 return resourceNetworkingNetworkV2Read(d, meta) 165 } 166 167 func resourceNetworkingNetworkV2Read(d *schema.ResourceData, meta interface{}) error { 168 config := meta.(*Config) 169 networkingClient, err := config.networkingV2Client(GetRegion(d)) 170 if err != nil { 171 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 172 } 173 174 n, err := networks.Get(networkingClient, d.Id()).Extract() 175 if err != nil { 176 return CheckDeleted(d, err, "network") 177 } 178 179 log.Printf("[DEBUG] Retrieved Network %s: %+v", d.Id(), n) 180 181 d.Set("name", n.Name) 182 d.Set("admin_state_up", strconv.FormatBool(n.AdminStateUp)) 183 d.Set("shared", strconv.FormatBool(n.Shared)) 184 d.Set("tenant_id", n.TenantID) 185 d.Set("region", GetRegion(d)) 186 187 return nil 188 } 189 190 func resourceNetworkingNetworkV2Update(d *schema.ResourceData, meta interface{}) error { 191 config := meta.(*Config) 192 networkingClient, err := config.networkingV2Client(GetRegion(d)) 193 if err != nil { 194 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 195 } 196 197 var updateOpts networks.UpdateOpts 198 if d.HasChange("name") { 199 updateOpts.Name = d.Get("name").(string) 200 } 201 if d.HasChange("admin_state_up") { 202 asuRaw := d.Get("admin_state_up").(string) 203 if asuRaw != "" { 204 asu, err := strconv.ParseBool(asuRaw) 205 if err != nil { 206 return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'") 207 } 208 updateOpts.AdminStateUp = &asu 209 } 210 } 211 if d.HasChange("shared") { 212 sharedRaw := d.Get("shared").(string) 213 if sharedRaw != "" { 214 shared, err := strconv.ParseBool(sharedRaw) 215 if err != nil { 216 return fmt.Errorf("shared, if provided, must be either 'true' or 'false': %v", err) 217 } 218 updateOpts.Shared = &shared 219 } 220 } 221 222 log.Printf("[DEBUG] Updating Network %s with options: %+v", d.Id(), updateOpts) 223 224 _, err = networks.Update(networkingClient, d.Id(), updateOpts).Extract() 225 if err != nil { 226 return fmt.Errorf("Error updating OpenStack Neutron Network: %s", err) 227 } 228 229 return resourceNetworkingNetworkV2Read(d, meta) 230 } 231 232 func resourceNetworkingNetworkV2Delete(d *schema.ResourceData, meta interface{}) error { 233 config := meta.(*Config) 234 networkingClient, err := config.networkingV2Client(GetRegion(d)) 235 if err != nil { 236 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 237 } 238 239 stateConf := &resource.StateChangeConf{ 240 Pending: []string{"ACTIVE"}, 241 Target: []string{"DELETED"}, 242 Refresh: waitForNetworkDelete(networkingClient, d.Id()), 243 Timeout: d.Timeout(schema.TimeoutDelete), 244 Delay: 5 * time.Second, 245 MinTimeout: 3 * time.Second, 246 } 247 248 _, err = stateConf.WaitForState() 249 if err != nil { 250 return fmt.Errorf("Error deleting OpenStack Neutron Network: %s", err) 251 } 252 253 d.SetId("") 254 return nil 255 } 256 257 func resourceNetworkingNetworkV2Segments(d *schema.ResourceData) (providerSegments []provider.Segment) { 258 segments := d.Get("segments").([]interface{}) 259 for _, v := range segments { 260 var segment provider.Segment 261 segmentMap := v.(map[string]interface{}) 262 263 if v, ok := segmentMap["physical_network"].(string); ok { 264 segment.PhysicalNetwork = v 265 } 266 267 if v, ok := segmentMap["network_type"].(string); ok { 268 segment.NetworkType = v 269 } 270 271 if v, ok := segmentMap["segmentation_id"].(int); ok { 272 segment.SegmentationID = v 273 } 274 275 providerSegments = append(providerSegments, segment) 276 } 277 return 278 } 279 280 func waitForNetworkActive(networkingClient *gophercloud.ServiceClient, networkId string) resource.StateRefreshFunc { 281 return func() (interface{}, string, error) { 282 n, err := networks.Get(networkingClient, networkId).Extract() 283 if err != nil { 284 return nil, "", err 285 } 286 287 log.Printf("[DEBUG] OpenStack Neutron Network: %+v", n) 288 if n.Status == "DOWN" || n.Status == "ACTIVE" { 289 return n, "ACTIVE", nil 290 } 291 292 return n, n.Status, nil 293 } 294 } 295 296 func waitForNetworkDelete(networkingClient *gophercloud.ServiceClient, networkId string) resource.StateRefreshFunc { 297 return func() (interface{}, string, error) { 298 log.Printf("[DEBUG] Attempting to delete OpenStack Network %s.\n", networkId) 299 300 n, err := networks.Get(networkingClient, networkId).Extract() 301 if err != nil { 302 if _, ok := err.(gophercloud.ErrDefault404); ok { 303 log.Printf("[DEBUG] Successfully deleted OpenStack Network %s", networkId) 304 return n, "DELETED", nil 305 } 306 return n, "ACTIVE", err 307 } 308 309 err = networks.Delete(networkingClient, networkId).ExtractErr() 310 if err != nil { 311 if _, ok := err.(gophercloud.ErrDefault404); ok { 312 log.Printf("[DEBUG] Successfully deleted OpenStack Network %s", networkId) 313 return n, "DELETED", nil 314 } 315 if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok { 316 if errCode.Actual == 409 { 317 return n, "ACTIVE", nil 318 } 319 } 320 return n, "ACTIVE", err 321 } 322 323 log.Printf("[DEBUG] OpenStack Network %s still active.\n", networkId) 324 return n, "ACTIVE", nil 325 } 326 }