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