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