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