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