github.com/danrjohnson/terraform@v0.7.0-rc2.0.20160627135212-d0fc1fa086ff/builtin/providers/cloudstack/resource_cloudstack_network.go (about) 1 package cloudstack 2 3 import ( 4 "fmt" 5 "log" 6 "net" 7 "strconv" 8 "strings" 9 10 "github.com/hashicorp/terraform/helper/schema" 11 "github.com/xanzy/go-cloudstack/cloudstack" 12 ) 13 14 const none = "none" 15 16 func resourceCloudStackNetwork() *schema.Resource { 17 aclidSchema := &schema.Schema{ 18 Type: schema.TypeString, 19 Optional: true, 20 Default: none, 21 ConflictsWith: []string{"aclid"}, 22 } 23 24 aclidSchema.StateFunc = func(v interface{}) string { 25 value := v.(string) 26 27 if value == none { 28 aclidSchema.ForceNew = true 29 } 30 31 return value 32 } 33 34 return &schema.Resource{ 35 Create: resourceCloudStackNetworkCreate, 36 Read: resourceCloudStackNetworkRead, 37 Update: resourceCloudStackNetworkUpdate, 38 Delete: resourceCloudStackNetworkDelete, 39 40 Schema: map[string]*schema.Schema{ 41 "name": &schema.Schema{ 42 Type: schema.TypeString, 43 Required: true, 44 }, 45 46 "display_text": &schema.Schema{ 47 Type: schema.TypeString, 48 Optional: true, 49 Computed: true, 50 }, 51 52 "cidr": &schema.Schema{ 53 Type: schema.TypeString, 54 Required: true, 55 ForceNew: true, 56 }, 57 58 "gateway": &schema.Schema{ 59 Type: schema.TypeString, 60 Optional: true, 61 Computed: true, 62 ForceNew: true, 63 }, 64 65 "startip": &schema.Schema{ 66 Type: schema.TypeString, 67 Optional: true, 68 Computed: true, 69 ForceNew: true, 70 }, 71 72 "endip": &schema.Schema{ 73 Type: schema.TypeString, 74 Optional: true, 75 Computed: true, 76 ForceNew: true, 77 }, 78 79 "network_offering": &schema.Schema{ 80 Type: schema.TypeString, 81 Required: true, 82 }, 83 84 "vlan": &schema.Schema{ 85 Type: schema.TypeInt, 86 Optional: true, 87 ForceNew: true, 88 }, 89 90 "vpc_id": &schema.Schema{ 91 Type: schema.TypeString, 92 Optional: true, 93 Computed: true, 94 ForceNew: true, 95 }, 96 97 "vpc": &schema.Schema{ 98 Type: schema.TypeString, 99 Optional: true, 100 ForceNew: true, 101 Deprecated: "Please use the `vpc_id` field instead", 102 }, 103 104 "acl_id": aclidSchema, 105 106 "aclid": &schema.Schema{ 107 Type: schema.TypeString, 108 Optional: true, 109 Deprecated: "Please use the `acl_id` field instead", 110 }, 111 112 "project": &schema.Schema{ 113 Type: schema.TypeString, 114 Optional: true, 115 ForceNew: true, 116 }, 117 118 "zone": &schema.Schema{ 119 Type: schema.TypeString, 120 Required: true, 121 ForceNew: true, 122 }, 123 124 "tags": tagsSchema(), 125 }, 126 } 127 } 128 129 func resourceCloudStackNetworkCreate(d *schema.ResourceData, meta interface{}) error { 130 cs := meta.(*cloudstack.CloudStackClient) 131 132 name := d.Get("name").(string) 133 134 // Retrieve the network_offering ID 135 networkofferingid, e := retrieveID(cs, "network_offering", d.Get("network_offering").(string)) 136 if e != nil { 137 return e.Error() 138 } 139 140 // Retrieve the zone ID 141 zoneid, e := retrieveID(cs, "zone", d.Get("zone").(string)) 142 if e != nil { 143 return e.Error() 144 } 145 146 // Compute/set the display text 147 displaytext, ok := d.GetOk("display_text") 148 if !ok { 149 displaytext = name 150 } 151 // Create a new parameter struct 152 p := cs.Network.NewCreateNetworkParams(displaytext.(string), name, networkofferingid, zoneid) 153 154 m, err := parseCIDR(d) 155 if err != nil { 156 return err 157 } 158 159 // Set the needed IP config 160 p.SetStartip(m["startip"]) 161 p.SetGateway(m["gateway"]) 162 p.SetEndip(m["endip"]) 163 p.SetNetmask(m["netmask"]) 164 165 if vlan, ok := d.GetOk("vlan"); ok { 166 p.SetVlan(strconv.Itoa(vlan.(int))) 167 } 168 169 // Check is this network needs to be created in a VPC 170 vpc, ok := d.GetOk("vpc_id") 171 if !ok { 172 vpc, ok = d.GetOk("vpc") 173 } 174 if ok { 175 // Retrieve the vpc ID 176 vpcid, e := retrieveID( 177 cs, 178 "vpc", 179 vpc.(string), 180 cloudstack.WithProject(d.Get("project").(string)), 181 ) 182 if e != nil { 183 return e.Error() 184 } 185 186 // Set the vpcid 187 p.SetVpcid(vpcid) 188 189 // Since we're in a VPC, check if we want to assiciate an ACL list 190 aclid, ok := d.GetOk("acl_id") 191 if !ok { 192 aclid, ok = d.GetOk("acl") 193 } 194 if ok && aclid != none { 195 // Set the acl ID 196 p.SetAclid(aclid.(string)) 197 } 198 } 199 200 // If there is a project supplied, we retrieve and set the project id 201 if err := setProjectid(p, cs, d); err != nil { 202 return err 203 } 204 205 // Create the new network 206 r, err := cs.Network.CreateNetwork(p) 207 if err != nil { 208 return fmt.Errorf("Error creating network %s: %s", name, err) 209 } 210 211 d.SetId(r.Id) 212 213 err = setTags(cs, d, "network") 214 if err != nil { 215 return fmt.Errorf("Error setting tags: %s", err) 216 } 217 218 return resourceCloudStackNetworkRead(d, meta) 219 } 220 221 func resourceCloudStackNetworkRead(d *schema.ResourceData, meta interface{}) error { 222 cs := meta.(*cloudstack.CloudStackClient) 223 224 // Get the virtual machine details 225 n, count, err := cs.Network.GetNetworkByID( 226 d.Id(), 227 cloudstack.WithProject(d.Get("project").(string)), 228 ) 229 if err != nil { 230 if count == 0 { 231 log.Printf( 232 "[DEBUG] Network %s does no longer exist", d.Get("name").(string)) 233 d.SetId("") 234 return nil 235 } 236 237 return err 238 } 239 240 d.Set("name", n.Name) 241 d.Set("display_text", n.Displaytext) 242 d.Set("cidr", n.Cidr) 243 d.Set("gateway", n.Gateway) 244 245 _, vpcID := d.GetOk("vpc_id") 246 _, vpc := d.GetOk("vpc") 247 if vpcID || vpc { 248 d.Set("vpc_id", n.Vpcid) 249 250 // Since we're in a VPC, also update the ACL ID. If we don't 251 // have an ACL ID make sure we set the default value instead. 252 if n.Aclid == "" { 253 n.Aclid = none 254 } 255 d.Set("acl_id", n.Aclid) 256 } 257 258 // Read the tags and store them in a map 259 tags := make(map[string]interface{}) 260 for item := range n.Tags { 261 tags[n.Tags[item].Key] = n.Tags[item].Value 262 } 263 d.Set("tags", tags) 264 265 setValueOrID(d, "network_offering", n.Networkofferingname, n.Networkofferingid) 266 setValueOrID(d, "project", n.Project, n.Projectid) 267 setValueOrID(d, "zone", n.Zonename, n.Zoneid) 268 269 return nil 270 } 271 272 func resourceCloudStackNetworkUpdate(d *schema.ResourceData, meta interface{}) error { 273 cs := meta.(*cloudstack.CloudStackClient) 274 name := d.Get("name").(string) 275 276 // Create a new parameter struct 277 p := cs.Network.NewUpdateNetworkParams(d.Id()) 278 279 // Check if the name or display text is changed 280 if d.HasChange("name") || d.HasChange("display_text") { 281 p.SetName(name) 282 283 // Compute/set the display text 284 displaytext := d.Get("display_text").(string) 285 if displaytext == "" { 286 displaytext = name 287 } 288 p.SetDisplaytext(displaytext) 289 } 290 291 // Check if the cidr is changed 292 if d.HasChange("cidr") { 293 p.SetGuestvmcidr(d.Get("cidr").(string)) 294 } 295 296 // Check if the network offering is changed 297 if d.HasChange("network_offering") { 298 // Retrieve the network_offering ID 299 networkofferingid, e := retrieveID(cs, "network_offering", d.Get("network_offering").(string)) 300 if e != nil { 301 return e.Error() 302 } 303 // Set the new network offering 304 p.SetNetworkofferingid(networkofferingid) 305 } 306 307 // Update the network 308 _, err := cs.Network.UpdateNetwork(p) 309 if err != nil { 310 return fmt.Errorf( 311 "Error updating network %s: %s", name, err) 312 } 313 314 // Replace the ACL if the ID has changed 315 if d.HasChange("acl_id") || d.HasChange("acl") { 316 aclid, ok := d.GetOk("acl_id") 317 if !ok { 318 aclid, ok = d.GetOk("acl") 319 } 320 if !ok { 321 return fmt.Errorf("Replacing the ACL requires a valid ACL ID") 322 } 323 324 p := cs.NetworkACL.NewReplaceNetworkACLListParams(aclid.(string)) 325 p.SetNetworkid(d.Id()) 326 327 _, err := cs.NetworkACL.ReplaceNetworkACLList(p) 328 if err != nil { 329 return fmt.Errorf("Error replacing ACL: %s", err) 330 } 331 } 332 333 // Update tags if they have changed 334 if d.HasChange("tags") { 335 err = setTags(cs, d, "network") 336 if err != nil { 337 return fmt.Errorf("Error updating tags: %s", err) 338 } 339 } 340 341 return resourceCloudStackNetworkRead(d, meta) 342 } 343 344 func resourceCloudStackNetworkDelete(d *schema.ResourceData, meta interface{}) error { 345 cs := meta.(*cloudstack.CloudStackClient) 346 347 // Create a new parameter struct 348 p := cs.Network.NewDeleteNetworkParams(d.Id()) 349 350 // Delete the network 351 _, err := cs.Network.DeleteNetwork(p) 352 if err != nil { 353 // This is a very poor way to be told the ID does no longer exist :( 354 if strings.Contains(err.Error(), fmt.Sprintf( 355 "Invalid parameter id value=%s due to incorrect long value format, "+ 356 "or entity does not exist", d.Id())) { 357 return nil 358 } 359 360 return fmt.Errorf("Error deleting network %s: %s", d.Get("name").(string), err) 361 } 362 return nil 363 } 364 365 func parseCIDR(d *schema.ResourceData) (map[string]string, error) { 366 m := make(map[string]string, 4) 367 368 cidr := d.Get("cidr").(string) 369 ip, ipnet, err := net.ParseCIDR(cidr) 370 if err != nil { 371 return nil, fmt.Errorf("Unable to parse cidr %s: %s", cidr, err) 372 } 373 374 msk := ipnet.Mask 375 sub := ip.Mask(msk) 376 377 m["netmask"] = fmt.Sprintf("%d.%d.%d.%d", msk[0], msk[1], msk[2], msk[3]) 378 379 if gateway, ok := d.GetOk("gateway"); ok { 380 m["gateway"] = gateway.(string) 381 } else { 382 m["gateway"] = fmt.Sprintf("%d.%d.%d.%d", sub[0], sub[1], sub[2], sub[3]+1) 383 } 384 385 if startip, ok := d.GetOk("startip"); ok { 386 m["startip"] = startip.(string) 387 } else { 388 m["startip"] = fmt.Sprintf("%d.%d.%d.%d", sub[0], sub[1], sub[2], sub[3]+2) 389 } 390 391 if endip, ok := d.GetOk("endip"); ok { 392 m["endip"] = endip.(string) 393 } else { 394 m["endip"] = fmt.Sprintf("%d.%d.%d.%d", 395 sub[0]+(0xff-msk[0]), sub[1]+(0xff-msk[1]), sub[2]+(0xff-msk[2]), sub[3]+(0xff-msk[3]-1)) 396 } 397 398 return m, nil 399 }