func createPortForward(
	d *schema.ResourceData,
	meta interface{},
	forward map[string]interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	// Make sure all required parameters are there
	if err := verifyPortForwardParams(d, forward); err != nil {
		return err
	}

	virtualmachine, ok := forward["virtual_machine_id"]
	if !ok {
		virtualmachine, ok = forward["virtual_machine"]
	}
	if !ok {
		return errors.New(
			"Either `virtual_machine_id` or [deprecated] `virtual_machine` must be provided.")
	}

	// Retrieve the virtual_machine ID
	virtualmachineid, e := retrieveID(
		cs,
		"virtual_machine",
		virtualmachine.(string),
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if e != nil {
		return e.Error()
	}

	vm, _, err := cs.VirtualMachine.GetVirtualMachineByID(
		virtualmachineid,
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		return err
	}

	// Create a new parameter struct
	p := cs.Firewall.NewCreatePortForwardingRuleParams(d.Id(), forward["private_port"].(int),
		forward["protocol"].(string), forward["public_port"].(int), vm.Id)

	// Set the network ID of the default network, needed when public IP address
	// is not associated with any Guest network yet (VPC case)
	p.SetNetworkid(vm.Nic[0].Networkid)

	// Do not open the firewall automatically in any case
	p.SetOpenfirewall(false)

	r, err := cs.Firewall.CreatePortForwardingRule(p)
	if err != nil {
		return err
	}

	forward["uuid"] = r.Id

	return nil
}
func resourceCloudStackDiskRead(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	// Get the volume details
	v, count, err := cs.Volume.GetVolumeByID(
		d.Id(),
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		if count == 0 {
			d.SetId("")
			return nil
		}

		return err
	}

	d.Set("name", v.Name)
	d.Set("attach", v.Attached != "")           // If attached this contains a timestamp when attached
	d.Set("size", int(v.Size/(1024*1024*1024))) // Needed to get GB's again

	setValueOrID(d, "disk_offering", v.Diskofferingname, v.Diskofferingid)
	setValueOrID(d, "project", v.Project, v.Projectid)
	setValueOrID(d, "zone", v.Zonename, v.Zoneid)

	if v.Attached != "" {
		// Get the virtual machine details
		vm, _, err := cs.VirtualMachine.GetVirtualMachineByID(
			v.Virtualmachineid,
			cloudstack.WithProject(d.Get("project").(string)),
		)
		if err != nil {
			return err
		}

		// Get the guest OS type details
		os, _, err := cs.GuestOS.GetOsTypeByID(vm.Guestosid)
		if err != nil {
			return err
		}

		// Get the guest OS category details
		c, _, err := cs.GuestOS.GetOsCategoryByID(os.Oscategoryid)
		if err != nil {
			return err
		}

		d.Set("device", retrieveDeviceName(v.Deviceid, c.Name))
		setValueOrID(d, "virtual_machine", v.Vmname, v.Virtualmachineid)
	}

	return nil
}
func resourceCloudStackInstanceRead(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	// Get the virtual machine details
	vm, count, err := cs.VirtualMachine.GetVirtualMachineByID(
		d.Id(),
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		if count == 0 {
			log.Printf("[DEBUG] Instance %s does no longer exist", d.Get("name").(string))
			d.SetId("")
			return nil
		}

		return err
	}

	// Update the config
	d.Set("name", vm.Name)
	d.Set("display_name", vm.Displayname)
	d.Set("network_id", vm.Nic[0].Networkid)
	d.Set("ip_address", vm.Nic[0].Ipaddress)
	d.Set("group", vm.Group)

	setValueOrID(d, "service_offering", vm.Serviceofferingname, vm.Serviceofferingid)
	setValueOrID(d, "template", vm.Templatename, vm.Templateid)
	setValueOrID(d, "project", vm.Project, vm.Projectid)
	setValueOrID(d, "zone", vm.Zonename, vm.Zoneid)

	return nil
}
// associatePublicIPAddress associates a new IP and sets the address and it's ID.
func (lb *loadBalancer) associatePublicIPAddress() error {
	glog.V(4).Infof("Allocate new IP for load balancer: %v", lb.name)
	// If a network belongs to a VPC, the IP address needs to be associated with
	// the VPC instead of with the network.
	network, count, err := lb.Network.GetNetworkByID(lb.networkID, cloudstack.WithProject(lb.projectID))
	if err != nil {
		if count == 0 {
			return fmt.Errorf("could not find network %v", lb.networkID)
		}
		return fmt.Errorf("error retrieving network: %v", err)
	}

	p := lb.Address.NewAssociateIpAddressParams()

	if network.Vpcid != "" {
		p.SetVpcid(network.Vpcid)
	} else {
		p.SetNetworkid(lb.networkID)
	}

	if lb.projectID != "" {
		p.SetProjectid(lb.projectID)
	}

	// Associate a new IP address
	r, err := lb.Address.AssociateIpAddress(p)
	if err != nil {
		return fmt.Errorf("error associating new IP address: %v", err)
	}

	lb.ipAddr = r.Ipaddress
	lb.ipAddrID = r.Id

	return nil
}
func resourceCloudStackNetworkACLRead(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	// Get the network ACL list details
	f, count, err := cs.NetworkACL.GetNetworkACLListByID(
		d.Id(),
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		if count == 0 {
			log.Printf(
				"[DEBUG] Network ACL list %s does no longer exist", d.Get("name").(string))
			d.SetId("")
			return nil
		}

		return err
	}

	d.Set("name", f.Name)
	d.Set("description", f.Description)
	d.Set("vpc_id", f.Vpcid)

	return nil
}
func resourceCloudStackStaticNATRead(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	// Get the IP address details
	ip, count, err := cs.Address.GetPublicIpAddressByID(
		d.Id(),
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		if count == 0 {
			log.Printf("[DEBUG] IP address with ID %s no longer exists", d.Id())
			d.SetId("")
			return nil
		}

		return err
	}

	if !ip.Isstaticnat {
		log.Printf("[DEBUG] Static NAT is no longer enabled for IP address with ID %s", d.Id())
		d.SetId("")
		return nil
	}

	d.Set("virtual_machine_id", ip.Virtualmachineid)
	d.Set("vm_guest_ip", ip.Vmipaddress)

	setValueOrID(d, "project", ip.Project, ip.Projectid)

	return nil
}
func resourceCloudStackIPAddressRead(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	// Get the IP address details
	ip, count, err := cs.Address.GetPublicIpAddressByID(
		d.Id(),
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		if count == 0 {
			log.Printf(
				"[DEBUG] IP address with ID %s is no longer associated", d.Id())
			d.SetId("")
			return nil
		}

		return err
	}

	// Updated the IP address
	d.Set("ip_address", ip.Ipaddress)

	if _, ok := d.GetOk("network_id"); ok {
		d.Set("network_id", ip.Associatednetworkid)
	}

	if _, ok := d.GetOk("vpc_id"); ok {
		d.Set("vpc_id", ip.Vpcid)
	}

	setValueOrID(d, "project", ip.Project, ip.Projectid)

	return nil
}
func resourceCloudStackAffinityGroupRead(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	log.Printf("[DEBUG] Rerieving affinity group %s", d.Get("name").(string))

	// Get the affinity group details
	ag, count, err := cs.AffinityGroup.GetAffinityGroupByID(
		d.Id(),
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		if count == 0 {
			log.Printf("[DEBUG] Affinity group %s does not longer exist", d.Get("name").(string))
			d.SetId("")
			return nil
		}

		return err
	}

	// Update the config
	d.Set("name", ag.Name)
	d.Set("description", ag.Description)
	d.Set("type", ag.Type)

	return nil
}
func resourceCloudStackLoadBalancerRuleRead(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	// Get the load balancer details
	lb, count, err := cs.LoadBalancer.GetLoadBalancerRuleByID(
		d.Id(),
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		if count == 0 {
			log.Printf("[DEBUG] Load balancer rule %s does no longer exist", d.Get("name").(string))
			d.SetId("")
			return nil
		}

		return err
	}

	d.Set("algorithm", lb.Algorithm)
	d.Set("public_port", lb.Publicport)
	d.Set("private_port", lb.Privateport)
	d.Set("ip_address_id", lb.Publicipid)

	// Only set network if user specified it to avoid spurious diffs
	if _, ok := d.GetOk("network_id"); ok {
		d.Set("network_id", lb.Networkid)
	}

	setValueOrID(d, "project", lb.Project, lb.Projectid)

	return nil
}
func resourceCloudStackDiskRead(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	// Get the volume details
	v, count, err := cs.Volume.GetVolumeByID(
		d.Id(),
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		if count == 0 {
			d.SetId("")
			return nil
		}

		return err
	}

	d.Set("name", v.Name)
	d.Set("attach", v.Attached != "")           // If attached this contains a timestamp when attached
	d.Set("size", int(v.Size/(1024*1024*1024))) // Needed to get GB's again

	setValueOrID(d, "disk_offering", v.Diskofferingname, v.Diskofferingid)
	setValueOrID(d, "project", v.Project, v.Projectid)
	setValueOrID(d, "zone", v.Zonename, v.Zoneid)

	if v.Attached != "" {
		d.Set("device_id", int(v.Deviceid))
		d.Set("virtual_machine_id", v.Virtualmachineid)
	}

	return nil
}
func resourceCloudStackSecurityGroupRuleRead(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	// Get the security group details
	sg, count, err := cs.SecurityGroup.GetSecurityGroupByID(
		d.Id(),
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		if count == 0 {
			log.Printf("[DEBUG] Security group %s does not longer exist", d.Get("name").(string))
			d.SetId("")
			return nil
		}

		return err
	}

	// Make a map of all the rule indexes so we can easily find a rule
	sgRules := append(sg.Ingressrule, sg.Egressrule...)
	ruleIndex := make(map[string]int, len(sgRules))
	for idx, r := range sgRules {
		ruleIndex[r.Ruleid] = idx
	}

	// Create an empty schema.Set to hold all rules
	rules := resourceCloudStackSecurityGroupRule().Schema["rule"].ZeroValue().(*schema.Set)

	// Read all rules that are configured
	if rs := d.Get("rule").(*schema.Set); rs.Len() > 0 {
		for _, rule := range rs.List() {
			rule := rule.(map[string]interface{})

			// First get any existing values
			cidrList, cidrListOK := rule["cidr_list"].(*schema.Set)
			usgList, usgListOk := rule["user_security_group_list"].(*schema.Set)

			// Then reset the values to a new empty set
			rule["cidr_list"] = &schema.Set{F: schema.HashString}
			rule["user_security_group_list"] = &schema.Set{F: schema.HashString}

			if cidrListOK && cidrList.Len() > 0 {
				for _, cidr := range cidrList.List() {
					readSecurityGroupRule(sg, ruleIndex, rule, cidr.(string))
				}
			}

			if usgListOk && usgList.Len() > 0 {
				for _, usg := range usgList.List() {
					readSecurityGroupRule(sg, ruleIndex, rule, usg.(string))
				}
			}

			rules.Add(rule)
		}
	}

	return nil
}
func resourceCloudStackVPCRead(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	// Get the VPC details
	v, count, err := cs.VPC.GetVPCByID(
		d.Id(),
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		if count == 0 {
			log.Printf(
				"[DEBUG] VPC %s does no longer exist", d.Get("name").(string))
			d.SetId("")
			return nil
		}

		return err
	}

	d.Set("name", v.Name)
	d.Set("display_text", v.Displaytext)
	d.Set("cidr", v.Cidr)
	d.Set("network_domain", v.Networkdomain)

	// Get the VPC offering details
	o, _, err := cs.VPC.GetVPCOfferingByID(v.Vpcofferingid)
	if err != nil {
		return err
	}

	setValueOrID(d, "vpc_offering", o.Name, v.Vpcofferingid)
	setValueOrID(d, "project", v.Project, v.Projectid)
	setValueOrID(d, "zone", v.Zonename, v.Zoneid)

	// Create a new parameter struct
	p := cs.Address.NewListPublicIpAddressesParams()
	p.SetVpcid(d.Id())
	p.SetIssourcenat(true)

	// If there is a project supplied, we retrieve and set the project id
	if err := setProjectid(p, cs, d); err != nil {
		return err
	}

	// Get the source NAT IP assigned to the VPC
	l, err := cs.Address.ListPublicIpAddresses(p)
	if err != nil {
		return err
	}

	if l.Count != 1 {
		return fmt.Errorf("Unexpected number (%d) of source NAT IPs returned", l.Count)
	}

	d.Set("source_nat_ip", l.PublicIpAddresses[0].Ipaddress)

	return nil
}
func createPortForward(d *schema.ResourceData, meta interface{}, forward map[string]interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	// Make sure all required parameters are there
	if err := verifyPortForwardParams(d, forward); err != nil {
		return err
	}

	vm, _, err := cs.VirtualMachine.GetVirtualMachineByID(
		forward["virtual_machine_id"].(string),
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		return err
	}

	// Create a new parameter struct
	p := cs.Firewall.NewCreatePortForwardingRuleParams(d.Id(), forward["private_port"].(int),
		forward["protocol"].(string), forward["public_port"].(int), vm.Id)

	if vmGuestIP, ok := forward["vm_guest_ip"]; ok {
		p.SetVmguestip(vmGuestIP.(string))

		// Set the network ID based on the guest IP, needed when the public IP address
		// is not associated with any network yet
	NICS:
		for _, nic := range vm.Nic {
			if vmGuestIP.(string) == nic.Ipaddress {
				p.SetNetworkid(nic.Networkid)
				break NICS
			}
			for _, ip := range nic.Secondaryip {
				if vmGuestIP.(string) == ip.Ipaddress {
					p.SetNetworkid(nic.Networkid)
					break NICS
				}
			}
		}
	} else {
		// If no guest IP is configured, use the primary NIC
		p.SetNetworkid(vm.Nic[0].Networkid)
	}

	// Do not open the firewall automatically in any case
	p.SetOpenfirewall(false)

	r, err := cs.Firewall.CreatePortForwardingRule(p)
	if err != nil {
		return err
	}

	forward["uuid"] = r.Id

	return nil
}
func resourceCloudStackDiskDetach(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	// Check if the volume is actually attached, before detaching
	if attached, err := isAttached(d, meta); err != nil || !attached {
		return err
	}

	// Create a new parameter struct
	p := cs.Volume.NewDetachVolumeParams()

	// Set the volume ID
	p.SetId(d.Id())

	// Detach the currently attached volume
	if _, err := cs.Volume.DetachVolume(p); err != nil {
		// Retrieve the virtual_machine ID
		virtualmachineid, e := retrieveID(
			cs,
			"virtual_machine",
			d.Get("virtual_machine").(string),
			cloudstack.WithProject(d.Get("project").(string)),
		)
		if e != nil {
			return e.Error()
		}

		// Create a new parameter struct
		pd := cs.VirtualMachine.NewStopVirtualMachineParams(virtualmachineid)

		// Stop the virtual machine in order to be able to detach the disk
		if _, err := cs.VirtualMachine.StopVirtualMachine(pd); err != nil {
			return err
		}

		// Try again to detach the currently attached volume
		if _, err := cs.Volume.DetachVolume(p); err != nil {
			return err
		}

		// Create a new parameter struct
		pu := cs.VirtualMachine.NewStartVirtualMachineParams(virtualmachineid)

		// Start the virtual machine again
		if _, err := cs.VirtualMachine.StartVirtualMachine(pu); err != nil {
			return err
		}
	}

	return nil
}
func isAttached(d *schema.ResourceData, meta interface{}) (bool, error) {
	cs := meta.(*cloudstack.CloudStackClient)

	// Get the volume details
	v, _, err := cs.Volume.GetVolumeByID(
		d.Id(),
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		return false, err
	}

	return v.Attached != "", nil
}
func resourceCloudStackNetworkRead(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	// Get the virtual machine details
	n, count, err := cs.Network.GetNetworkByID(
		d.Id(),
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		if count == 0 {
			log.Printf(
				"[DEBUG] Network %s does no longer exist", d.Get("name").(string))
			d.SetId("")
			return nil
		}

		return err
	}

	d.Set("name", n.Name)
	d.Set("display_text", n.Displaytext)
	d.Set("cidr", n.Cidr)
	d.Set("gateway", n.Gateway)

	_, vpcID := d.GetOk("vpc_id")
	_, vpc := d.GetOk("vpc")
	if vpcID || vpc {
		d.Set("vpc_id", n.Vpcid)

		// Since we're in a VPC, also update the ACL ID. If we don't
		// have an ACL ID make sure we set the default value instead.
		if n.Aclid == "" {
			n.Aclid = none
		}
		d.Set("acl_id", n.Aclid)
	}

	// Read the tags and store them in a map
	tags := make(map[string]interface{})
	for item := range n.Tags {
		tags[n.Tags[item].Key] = n.Tags[item].Value
	}
	d.Set("tags", tags)

	setValueOrID(d, "network_offering", n.Networkofferingname, n.Networkofferingid)
	setValueOrID(d, "project", n.Project, n.Projectid)
	setValueOrID(d, "zone", n.Zonename, n.Zoneid)

	return nil
}
func resourceCloudStackStaticNATCreate(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	ipaddressid := d.Get("ip_address_id").(string)

	vm, _, err := cs.VirtualMachine.GetVirtualMachineByID(
		d.Get("virtual_machine_id").(string),
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		return err
	}

	// Create a new parameter struct
	p := cs.NAT.NewEnableStaticNatParams(ipaddressid, vm.Id)

	if vmGuestIP, ok := d.GetOk("vm_guest_ip"); ok {
		p.SetVmguestip(vmGuestIP.(string))

		// Set the network ID based on the guest IP, needed when the public IP address
		// is not associated with any network yet
	NICS:
		for _, nic := range vm.Nic {
			if vmGuestIP.(string) == nic.Ipaddress {
				p.SetNetworkid(nic.Networkid)
				break NICS
			}
			for _, ip := range nic.Secondaryip {
				if vmGuestIP.(string) == ip.Ipaddress {
					p.SetNetworkid(nic.Networkid)
					break NICS
				}
			}
		}
	} else {
		// If no guest IP is configured, use the primary NIC
		p.SetNetworkid(vm.Nic[0].Networkid)
	}

	_, err = cs.NAT.EnableStaticNat(p)
	if err != nil {
		return fmt.Errorf("Error enabling static NAT: %s", err)
	}

	d.SetId(ipaddressid)

	return resourceCloudStackStaticNATRead(d, meta)
}
func resourceCloudStackDiskAttach(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	// First check if the disk isn't already attached
	if attached, err := isAttached(d, meta); err != nil || attached {
		return err
	}

	// Retrieve the virtual_machine ID
	virtualmachineid, e := retrieveID(
		cs,
		"virtual_machine",
		d.Get("virtual_machine").(string),
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if e != nil {
		return e.Error()
	}

	// Create a new parameter struct
	p := cs.Volume.NewAttachVolumeParams(d.Id(), virtualmachineid)

	if device, ok := d.GetOk("device"); ok {
		// Retrieve the device ID
		deviceid := retrieveDeviceID(device.(string))
		if deviceid == -1 {
			return fmt.Errorf("Device %s is not a valid device", device.(string))
		}

		// Set the device ID
		p.SetDeviceid(deviceid)
	}

	// Attach the new volume
	r, err := Retry(4, retryableAttachVolumeFunc(cs, p))
	if err != nil {
		return err
	}

	d.SetId(r.(*cloudstack.AttachVolumeResponse).Id)

	return nil
}
func resourceCloudStackStaticNATExists(d *schema.ResourceData, meta interface{}) (bool, error) {
	cs := meta.(*cloudstack.CloudStackClient)

	// Get the IP address details
	ip, count, err := cs.Address.GetPublicIpAddressByID(
		d.Id(),
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		if count == 0 {
			log.Printf("[DEBUG] IP address with ID %s no longer exists", d.Id())
			return false, nil
		}

		return false, err
	}

	return ip.Isstaticnat, nil
}
func resourceCloudStackPortForwardCreate(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	ipaddress, ok := d.GetOk("ip_address_id")
	if !ok {
		ipaddress, ok = d.GetOk("ipaddress")
	}
	if !ok {
		return errors.New("Either `ip_address_id` or [deprecated] `ipaddress` must be provided.")
	}

	// Retrieve the ipaddress ID
	ipaddressid, e := retrieveID(
		cs,
		"ip_address",
		ipaddress.(string),
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if e != nil {
		return e.Error()
	}

	// We need to set this upfront in order to be able to save a partial state
	d.SetId(ipaddressid)

	// Create all forwards that are configured
	if nrs := d.Get("forward").(*schema.Set); nrs.Len() > 0 {
		// Create an empty schema.Set to hold all forwards
		forwards := resourceCloudStackPortForward().Schema["forward"].ZeroValue().(*schema.Set)

		err := createPortForwards(d, meta, forwards, nrs)

		// We need to update this first to preserve the correct state
		d.Set("forward", forwards)

		if err != nil {
			return err
		}
	}

	return resourceCloudStackPortForwardRead(d, meta)
}
func resourceCloudStackTemplateRead(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	// Get the template details
	t, count, err := cs.Template.GetTemplateByID(
		d.Id(),
		"executable",
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		if count == 0 {
			log.Printf(
				"[DEBUG] Template %s no longer exists", d.Get("name").(string))
			d.SetId("")
			return nil
		}

		return err
	}

	d.Set("name", t.Name)
	d.Set("display_text", t.Displaytext)
	d.Set("format", t.Format)
	d.Set("hypervisor", t.Hypervisor)
	d.Set("is_dynamically_scalable", t.Isdynamicallyscalable)
	d.Set("is_extractable", t.Isextractable)
	d.Set("is_featured", t.Isfeatured)
	d.Set("is_public", t.Ispublic)
	d.Set("password_enabled", t.Passwordenabled)
	d.Set("is_ready", t.Isready)

	setValueOrID(d, "os_type", t.Ostypename, t.Ostypeid)
	setValueOrID(d, "project", t.Project, t.Projectid)
	setValueOrID(d, "zone", t.Zonename, t.Zoneid)

	return nil
}
func resourceCloudStackInstanceCreate(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	// Retrieve the service_offering ID
	serviceofferingid, e := retrieveID(cs, "service_offering", d.Get("service_offering").(string))
	if e != nil {
		return e.Error()
	}

	// Retrieve the zone ID
	zoneid, e := retrieveID(cs, "zone", d.Get("zone").(string))
	if e != nil {
		return e.Error()
	}

	// Retrieve the zone object
	zone, _, err := cs.Zone.GetZoneByID(zoneid)
	if err != nil {
		return err
	}

	// Retrieve the template ID
	templateid, e := retrieveTemplateID(cs, zone.Id, d.Get("template").(string))
	if e != nil {
		return e.Error()
	}

	// Create a new parameter struct
	p := cs.VirtualMachine.NewDeployVirtualMachineParams(serviceofferingid, templateid, zone.Id)

	// Set the name
	name, hasName := d.GetOk("name")
	if hasName {
		p.SetName(name.(string))
	}

	// Set the display name
	if displayname, ok := d.GetOk("display_name"); ok {
		p.SetDisplayname(displayname.(string))
	} else if hasName {
		p.SetDisplayname(name.(string))
	}

	if zone.Networktype == "Advanced" {
		network, ok := d.GetOk("network_id")
		if !ok {
			network, ok = d.GetOk("network")
		}
		if !ok {
			return errors.New(
				"Either `network_id` or [deprecated] `network` must be provided when using a zone with network type `advanced`.")
		}

		// Retrieve the network ID
		networkid, e := retrieveID(
			cs,
			"network",
			network.(string),
			cloudstack.WithProject(d.Get("project").(string)),
		)
		if e != nil {
			return e.Error()
		}

		// Set the default network ID
		p.SetNetworkids([]string{networkid})
	}

	// If there is a ipaddres supplied, add it to the parameter struct
	ipaddress, ok := d.GetOk("ip_address")
	if !ok {
		ipaddress, ok = d.GetOk("ipaddress")
	}
	if ok {
		p.SetIpaddress(ipaddress.(string))
	}

	if ags := d.Get("affinity_group_ids").(*schema.Set); ags.Len() > 0 {
		var groups []string

		for _, group := range ags.List() {
			groups = append(groups, group.(string))
		}

		p.SetAffinitygroupids(groups)
	}

	// If there is a project supplied, we retrieve and set the project id
	if err := setProjectid(p, cs, d); err != nil {
		return err
	}

	// If a keypair is supplied, add it to the parameter struct
	if keypair, ok := d.GetOk("keypair"); ok {
		p.SetKeypair(keypair.(string))
	}

	// If the user data contains any info, it needs to be base64 encoded and
	// added to the parameter struct
	if userData, ok := d.GetOk("user_data"); ok {
		ud := base64.StdEncoding.EncodeToString([]byte(userData.(string)))

		// deployVirtualMachine uses POST by default, so max userdata is 32K
		maxUD := 32768

		if cs.HTTPGETOnly {
			// deployVirtualMachine using GET instead, so max userdata is 2K
			maxUD = 2048
		}

		if len(ud) > maxUD {
			return fmt.Errorf(
				"The supplied user_data contains %d bytes after encoding, "+
					"this exeeds the limit of %d bytes", len(ud), maxUD)
		}

		p.SetUserdata(ud)
	}

	// If there is a group supplied, add it to the parameter struct
	if group, ok := d.GetOk("group"); ok {
		p.SetGroup(group.(string))
	}

	// If there is a root_disk_size supplied, add it to the parameter struct
	if rootdisksize, ok := d.GetOk("root_disk_size"); ok {
		p.SetRootdisksize(int64(rootdisksize.(int)))
	}

	// Create the new instance
	r, err := cs.VirtualMachine.DeployVirtualMachine(p)
	if err != nil {
		return fmt.Errorf("Error creating the new instance %s: %s", name, err)
	}

	d.SetId(r.Id)

	// Set the connection info for any configured provisioners
	d.SetConnInfo(map[string]string{
		"host":     r.Nic[0].Ipaddress,
		"password": r.Password,
	})

	return resourceCloudStackInstanceRead(d, meta)
}
Example #23
0
func (s *stepPrepareConfig) Run(state multistep.StateBag) multistep.StepAction {
	client := state.Get("client").(*cloudstack.CloudStackClient)
	config := state.Get("config").(*Config)
	ui := state.Get("ui").(packer.Ui)

	ui.Say("Preparing config...")

	var err error
	var errs *packer.MultiError

	// First get the project and zone UUID's so we can use them in other calls when needed.
	if config.Project != "" && !isUUID(config.Project) {
		config.Project, _, err = client.Project.GetProjectID(config.Project)
		if err != nil {
			errs = packer.MultiErrorAppend(errs, &retrieveErr{"project", config.Project, err})
		}
	}

	if config.UserDataFile != "" {
		userdata, err := ioutil.ReadFile(config.UserDataFile)
		if err != nil {
			errs = packer.MultiErrorAppend(errs, fmt.Errorf("problem reading user data file: %s", err))
		}
		config.UserData = string(userdata)
	}

	if !isUUID(config.Zone) {
		config.Zone, _, err = client.Zone.GetZoneID(config.Zone)
		if err != nil {
			errs = packer.MultiErrorAppend(errs, &retrieveErr{"zone", config.Zone, err})
		}
	}

	// Then try to get the remaining UUID's.
	if config.DiskOffering != "" && !isUUID(config.DiskOffering) {
		config.DiskOffering, _, err = client.DiskOffering.GetDiskOfferingID(config.DiskOffering)
		if err != nil {
			errs = packer.MultiErrorAppend(errs, &retrieveErr{"disk offering", config.DiskOffering, err})
		}
	}

	if config.PublicIPAddress != "" && !isUUID(config.PublicIPAddress) {
		// Save the public IP address before replacing it with it's UUID.
		config.hostAddress = config.PublicIPAddress

		p := client.Address.NewListPublicIpAddressesParams()
		p.SetIpaddress(config.PublicIPAddress)

		if config.Project != "" {
			p.SetProjectid(config.Project)
		}

		ipAddrs, err := client.Address.ListPublicIpAddresses(p)
		if err != nil {
			errs = packer.MultiErrorAppend(errs, &retrieveErr{"IP address", config.PublicIPAddress, err})
		}
		if err == nil && ipAddrs.Count != 1 {
			errs = packer.MultiErrorAppend(errs, &retrieveErr{"IP address", config.PublicIPAddress, ipAddrs})
		}
		if err == nil && ipAddrs.Count == 1 {
			config.PublicIPAddress = ipAddrs.PublicIpAddresses[0].Id
		}
	}

	if !isUUID(config.Network) {
		config.Network, _, err = client.Network.GetNetworkID(config.Network, cloudstack.WithProject(config.Project))
		if err != nil {
			errs = packer.MultiErrorAppend(errs, &retrieveErr{"network", config.Network, err})
		}
	}

	if !isUUID(config.ServiceOffering) {
		config.ServiceOffering, _, err = client.ServiceOffering.GetServiceOfferingID(config.ServiceOffering)
		if err != nil {
			errs = packer.MultiErrorAppend(errs, &retrieveErr{"service offering", config.ServiceOffering, err})
		}
	}

	if config.SourceISO != "" {
		if isUUID(config.SourceISO) {
			config.instanceSource = config.SourceISO
		} else {
			config.instanceSource, _, err = client.ISO.GetIsoID(config.SourceISO, "executable", config.Zone)
			if err != nil {
				errs = packer.MultiErrorAppend(errs, &retrieveErr{"ISO", config.SourceISO, err})
			}
		}
	}

	if config.SourceTemplate != "" {
		if isUUID(config.SourceTemplate) {
			config.instanceSource = config.SourceTemplate
		} else {
			config.instanceSource, _, err = client.Template.GetTemplateID(config.SourceTemplate, "executable", config.Zone)
			if err != nil {
				errs = packer.MultiErrorAppend(errs, &retrieveErr{"template", config.SourceTemplate, err})
			}
		}
	}

	if !isUUID(config.TemplateOS) {
		p := client.GuestOS.NewListOsTypesParams()
		p.SetDescription(config.TemplateOS)

		types, err := client.GuestOS.ListOsTypes(p)
		if err != nil {
			errs = packer.MultiErrorAppend(errs, &retrieveErr{"OS type", config.TemplateOS, err})
		}
		if err == nil && types.Count != 1 {
			errs = packer.MultiErrorAppend(errs, &retrieveErr{"OS type", config.TemplateOS, types})
		}
		if err == nil && types.Count == 1 {
			config.TemplateOS = types.OsTypes[0].Id
		}
	}

	// This is needed because nil is not always nil. When returning *packer.MultiError(nil)
	// as an error interface, that interface will no longer be equal to nil but it will be
	// an interface with type *packer.MultiError and value nil which is different then a
	// nil interface.
	if errs != nil && len(errs.Errors) > 0 {
		ui.Error(errs.Error())
		return multistep.ActionHalt
	}

	ui.Message("Config has been prepared!")

	return multistep.ActionContinue
}
func createSecurityGroupRules(d *schema.ResourceData, meta interface{}, rules *schema.Set, nrs *schema.Set) error {
	cs := meta.(*cloudstack.CloudStackClient)
	var errs *multierror.Error

	var wg sync.WaitGroup
	wg.Add(nrs.Len())

	sem := make(chan struct{}, d.Get("parallelism").(int))
	for _, rule := range nrs.List() {
		// Put in a tiny sleep here to avoid DoS'ing the API
		time.Sleep(500 * time.Millisecond)

		go func(rule map[string]interface{}) {
			defer wg.Done()
			sem <- struct{}{}

			// Make sure all required parameters are there
			if err := verifySecurityGroupRuleParams(d, rule); err != nil {
				errs = multierror.Append(errs, err)
				return
			}

			var p authorizeSecurityGroupParams

			if cidrList, ok := rule["cidr_list"].(*schema.Set); ok && cidrList.Len() > 0 {
				for _, cidr := range cidrList.List() {
					// Create a new parameter struct
					switch rule["traffic_type"].(string) {
					case "ingress":
						p = cs.SecurityGroup.NewAuthorizeSecurityGroupIngressParams()
					case "egress":
						p = cs.SecurityGroup.NewAuthorizeSecurityGroupEgressParams()
					}

					p.SetSecuritygroupid(d.Id())
					p.SetCidrlist([]string{cidr.(string)})

					// Create a single rule
					err := createSecurityGroupRule(d, meta, rule, p, cidr.(string))
					if err != nil {
						errs = multierror.Append(errs, err)
					}
				}
			}

			if usgList, ok := rule["user_security_group_list"].(*schema.Set); ok && usgList.Len() > 0 {
				for _, usg := range usgList.List() {
					sg, _, err := cs.SecurityGroup.GetSecurityGroupByName(
						usg.(string),
						cloudstack.WithProject(d.Get("project").(string)),
					)
					if err != nil {
						errs = multierror.Append(errs, err)
						continue
					}

					// Create a new parameter struct
					switch rule["traffic_type"].(string) {
					case "ingress":
						p = cs.SecurityGroup.NewAuthorizeSecurityGroupIngressParams()
					case "egress":
						p = cs.SecurityGroup.NewAuthorizeSecurityGroupEgressParams()
					}

					p.SetSecuritygroupid(d.Id())
					p.SetUsersecuritygrouplist(map[string]string{sg.Account: usg.(string)})

					// Create a single rule
					err = createSecurityGroupRule(d, meta, rule, p, usg.(string))
					if err != nil {
						errs = multierror.Append(errs, err)
					}
				}
			}

			// If we have at least one UUID, we need to save the rule
			if len(rule["uuids"].(map[string]interface{})) > 0 {
				rules.Add(rule)
			}

			<-sem
		}(rule.(map[string]interface{}))
	}

	wg.Wait()

	return errs.ErrorOrNil()
}
func resourceCloudStackIPAddressCreate(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	if err := verifyIPAddressParams(d); err != nil {
		return err
	}

	// Create a new parameter struct
	p := cs.Address.NewAssociateIpAddressParams()

	network, ok := d.GetOk("network_id")
	if !ok {
		network, ok = d.GetOk("network")
	}
	if ok {
		// Retrieve the network ID
		networkid, e := retrieveID(
			cs,
			"network",
			network.(string),
			cloudstack.WithProject(d.Get("project").(string)),
		)
		if e != nil {
			return e.Error()
		}

		// Set the networkid
		p.SetNetworkid(networkid)
	}

	vpc, ok := d.GetOk("vpc_id")
	if !ok {
		vpc, ok = d.GetOk("vpc")
	}
	if ok {
		// Retrieve the vpc ID
		vpcid, e := retrieveID(
			cs,
			"vpc",
			vpc.(string),
			cloudstack.WithProject(d.Get("project").(string)),
		)
		if e != nil {
			return e.Error()
		}

		// Set the vpcid
		p.SetVpcid(vpcid)
	}

	// If there is a project supplied, we retrieve and set the project id
	if err := setProjectid(p, cs, d); err != nil {
		return err
	}

	// Associate a new IP address
	r, err := cs.Address.AssociateIpAddress(p)
	if err != nil {
		return fmt.Errorf("Error associating a new IP address: %s", err)
	}

	d.SetId(r.Id)

	return resourceCloudStackIPAddressRead(d, meta)
}
func resourceCloudStackNetworkACLRuleRead(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	// First check if the ACL itself still exists
	_, count, err := cs.NetworkACL.GetNetworkACLListByID(
		d.Id(),
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		if count == 0 {
			log.Printf(
				"[DEBUG] Network ACL list %s does no longer exist", d.Id())
			d.SetId("")
			return nil
		}

		return err
	}

	// Get all the rules from the running environment
	p := cs.NetworkACL.NewListNetworkACLsParams()
	p.SetAclid(d.Id())
	p.SetListall(true)

	l, err := cs.NetworkACL.ListNetworkACLs(p)
	if err != nil {
		return err
	}

	// Make a map of all the rules so we can easily find a rule
	ruleMap := make(map[string]*cloudstack.NetworkACL, l.Count)
	for _, r := range l.NetworkACLs {
		ruleMap[r.Id] = r
	}

	// Create an empty schema.Set to hold all rules
	rules := resourceCloudStackNetworkACLRule().Schema["rule"].ZeroValue().(*schema.Set)

	// Read all rules that are configured
	if rs := d.Get("rule").(*schema.Set); rs.Len() > 0 {
		for _, rule := range rs.List() {
			rule := rule.(map[string]interface{})
			uuids := rule["uuids"].(map[string]interface{})

			if rule["protocol"].(string) == "icmp" {
				id, ok := uuids["icmp"]
				if !ok {
					continue
				}

				// Get the rule
				r, ok := ruleMap[id.(string)]
				if !ok {
					delete(uuids, "icmp")
					continue
				}

				// Delete the known rule so only unknown rules remain in the ruleMap
				delete(ruleMap, id.(string))

				// Create a set with all CIDR's
				cidrs := &schema.Set{F: schema.HashString}
				for _, cidr := range strings.Split(r.Cidrlist, ",") {
					cidrs.Add(cidr)
				}

				// Update the values
				rule["action"] = strings.ToLower(r.Action)
				rule["protocol"] = r.Protocol
				rule["icmp_type"] = r.Icmptype
				rule["icmp_code"] = r.Icmpcode
				rule["traffic_type"] = strings.ToLower(r.Traffictype)
				rule["cidr_list"] = cidrs
				rules.Add(rule)
			}

			if rule["protocol"].(string) == "all" {
				id, ok := uuids["all"]
				if !ok {
					continue
				}

				// Get the rule
				r, ok := ruleMap[id.(string)]
				if !ok {
					delete(uuids, "all")
					continue
				}

				// Delete the known rule so only unknown rules remain in the ruleMap
				delete(ruleMap, id.(string))

				// Create a set with all CIDR's
				cidrs := &schema.Set{F: schema.HashString}
				for _, cidr := range strings.Split(r.Cidrlist, ",") {
					cidrs.Add(cidr)
				}

				// Update the values
				rule["action"] = strings.ToLower(r.Action)
				rule["protocol"] = r.Protocol
				rule["traffic_type"] = strings.ToLower(r.Traffictype)
				rule["cidr_list"] = cidrs
				rules.Add(rule)
			}

			// If protocol is tcp or udp, loop through all ports
			if rule["protocol"].(string) == "tcp" || rule["protocol"].(string) == "udp" {
				if ps := rule["ports"].(*schema.Set); ps.Len() > 0 {

					// Create an empty schema.Set to hold all ports
					ports := &schema.Set{F: schema.HashString}

					// Loop through all ports and retrieve their info
					for _, port := range ps.List() {
						id, ok := uuids[port.(string)]
						if !ok {
							continue
						}

						// Get the rule
						r, ok := ruleMap[id.(string)]
						if !ok {
							delete(uuids, port.(string))
							continue
						}

						// Delete the known rule so only unknown rules remain in the ruleMap
						delete(ruleMap, id.(string))

						// Create a set with all CIDR's
						cidrs := &schema.Set{F: schema.HashString}
						for _, cidr := range strings.Split(r.Cidrlist, ",") {
							cidrs.Add(cidr)
						}

						// Update the values
						rule["action"] = strings.ToLower(r.Action)
						rule["protocol"] = r.Protocol
						rule["traffic_type"] = strings.ToLower(r.Traffictype)
						rule["cidr_list"] = cidrs
						ports.Add(port)
					}

					// If there is at least one port found, add this rule to the rules set
					if ports.Len() > 0 {
						rule["ports"] = ports
						rules.Add(rule)
					}
				}
			}
		}
	}

	// If this is a managed firewall, add all unknown rules into dummy rules
	managed := d.Get("managed").(bool)
	if managed && len(ruleMap) > 0 {
		for uuid := range ruleMap {
			// We need to create and add a dummy value to a schema.Set as the
			// cidr_list is a required field and thus needs a value
			cidrs := &schema.Set{F: schema.HashString}
			cidrs.Add(uuid)

			// Make a dummy rule to hold the unknown UUID
			rule := map[string]interface{}{
				"cidr_list": cidrs,
				"protocol":  uuid,
				"uuids":     map[string]interface{}{uuid: uuid},
			}

			// Add the dummy rule to the rules set
			rules.Add(rule)
		}
	}

	if rules.Len() > 0 {
		d.Set("rule", rules)
	} else if !managed {
		d.SetId("")
	}

	return nil
}
func (s *stepSetupNetworking) Run(state multistep.StateBag) multistep.StepAction {
	client := state.Get("client").(*cloudstack.CloudStackClient)
	config := state.Get("config").(*Config)
	ui := state.Get("ui").(packer.Ui)

	ui.Say("Setup networking...")

	if config.UseLocalIPAddress {
		ui.Message("Using the local IP address...")
		ui.Message("Networking has been setup!")
		return multistep.ActionContinue
	}

	// Generate a random public port used to configure our port forward.
	rand.Seed(time.Now().UnixNano())
	s.publicPort = 50000 + rand.Intn(10000)

	// Set the currently configured port to be the private port.
	s.privatePort = config.Comm.Port()

	// Set the SSH or WinRM port to be the randomly generated public port.
	switch config.Comm.Type {
	case "ssh":
		config.Comm.SSHPort = s.publicPort
	case "winrm":
		config.Comm.WinRMPort = s.publicPort
	}

	// Retrieve the instance ID from the previously saved state.
	instanceID, ok := state.Get("instance_id").(string)
	if !ok || instanceID == "" {
		ui.Error("Could not retrieve instance_id from state!")
		return multistep.ActionHalt
	}

	network, _, err := client.Network.GetNetworkByID(
		config.Network,
		cloudstack.WithProject(config.Project),
	)
	if err != nil {
		ui.Error(fmt.Sprintf("Failed to retrieve the network object: %s", err))
		return multistep.ActionHalt
	}

	if config.PublicIPAddress == "" {
		ui.Message("Associating public IP address...")
		p := client.Address.NewAssociateIpAddressParams()

		if config.Project != "" {
			p.SetProjectid(config.Project)
		}

		if network.Vpcid != "" {
			p.SetVpcid(network.Vpcid)
		} else {
			p.SetNetworkid(network.Id)
		}

		// Associate a new public IP address.
		ipAddr, err := client.Address.AssociateIpAddress(p)
		if err != nil {
			ui.Error(fmt.Sprintf("Failed to associate public IP address: %s", err))
			return multistep.ActionHalt
		}

		// Set the IP address and it's ID.
		config.PublicIPAddress = ipAddr.Id
		config.hostAddress = ipAddr.Ipaddress

		// Store the IP address ID.
		state.Put("ip_address_id", ipAddr.Id)
	}

	ui.Message("Creating port forward...")
	p := client.Firewall.NewCreatePortForwardingRuleParams(
		config.PublicIPAddress,
		s.privatePort,
		"TCP",
		s.publicPort,
		instanceID,
	)

	// Configure the port forward.
	p.SetNetworkid(network.Id)
	p.SetOpenfirewall(false)

	// Create the port forward.
	forward, err := client.Firewall.CreatePortForwardingRule(p)
	if err != nil {
		ui.Error(fmt.Sprintf("Failed to create port forward: %s", err))
	}

	// Store the port forward ID.
	state.Put("port_forward_id", forward.Id)

	if network.Vpcid != "" {
		ui.Message("Creating network ACL rule...")

		if network.Aclid == "" {
			ui.Error("Failed to configure the firewall: no ACL connected to the VPC network")
			return multistep.ActionHalt
		}

		// Create a new parameter struct.
		p := client.NetworkACL.NewCreateNetworkACLParams("TCP")

		// Configure the network ACL rule.
		p.SetAclid(network.Aclid)
		p.SetAction("allow")
		p.SetCidrlist(config.CIDRList)
		p.SetStartport(s.privatePort)
		p.SetEndport(s.privatePort)
		p.SetTraffictype("ingress")

		// Create the network ACL rule.
		aclRule, err := client.NetworkACL.CreateNetworkACL(p)
		if err != nil {
			ui.Error(fmt.Sprintf("Failed to create network ACL rule: %s", err))
			return multistep.ActionHalt
		}

		// Store the network ACL rule ID.
		state.Put("network_acl_rule_id", aclRule.Id)
	} else {
		ui.Message("Creating firewall rule...")

		// Create a new parameter struct.
		p := client.Firewall.NewCreateFirewallRuleParams(config.PublicIPAddress, "TCP")

		// Configure the firewall rule.
		p.SetCidrlist(config.CIDRList)
		p.SetStartport(s.publicPort)
		p.SetEndport(s.publicPort)

		fwRule, err := client.Firewall.CreateFirewallRule(p)
		if err != nil {
			ui.Error(fmt.Sprintf("Failed to create firewall rule: %s", err))
			return multistep.ActionHalt
		}

		// Store the firewall rule ID.
		state.Put("firewall_rule_id", fwRule.Id)
	}

	ui.Message("Networking has been setup!")

	return multistep.ActionContinue
}
func resourceCloudStackPortForwardRead(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	// First check if the IP address is still associated
	_, count, err := cs.Address.GetPublicIpAddressByID(
		d.Id(),
		cloudstack.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		if count == 0 {
			log.Printf(
				"[DEBUG] IP address with ID %s is no longer associated", d.Id())
			d.SetId("")
			return nil
		}

		return err
	}

	// Get all the forwards from the running environment
	p := cs.Firewall.NewListPortForwardingRulesParams()
	p.SetIpaddressid(d.Id())
	p.SetListall(true)

	if err := setProjectid(p, cs, d); err != nil {
		return err
	}

	l, err := cs.Firewall.ListPortForwardingRules(p)
	if err != nil {
		return err
	}

	// Make a map of all the forwards so we can easily find a forward
	forwardMap := make(map[string]*cloudstack.PortForwardingRule, l.Count)
	for _, f := range l.PortForwardingRules {
		forwardMap[f.Id] = f
	}

	// Create an empty schema.Set to hold all forwards
	forwards := resourceCloudStackPortForward().Schema["forward"].ZeroValue().(*schema.Set)

	// Read all forwards that are configured
	if rs := d.Get("forward").(*schema.Set); rs.Len() > 0 {
		for _, forward := range rs.List() {
			forward := forward.(map[string]interface{})

			id, ok := forward["uuid"]
			if !ok || id.(string) == "" {
				continue
			}

			// Get the forward
			f, ok := forwardMap[id.(string)]
			if !ok {
				forward["uuid"] = ""
				continue
			}

			// Delete the known rule so only unknown rules remain in the ruleMap
			delete(forwardMap, id.(string))

			privPort, err := strconv.Atoi(f.Privateport)
			if err != nil {
				return err
			}

			pubPort, err := strconv.Atoi(f.Publicport)
			if err != nil {
				return err
			}

			// Update the values
			forward["protocol"] = f.Protocol
			forward["private_port"] = privPort
			forward["public_port"] = pubPort
			forward["virtual_machine_id"] = f.Virtualmachineid
			forward["vm_guest_ip"] = f.Vmguestip

			forwards.Add(forward)
		}
	}

	// If this is a managed resource, add all unknown forwards to dummy forwards
	managed := d.Get("managed").(bool)
	if managed && len(forwardMap) > 0 {
		for uuid := range forwardMap {
			// Make a dummy forward to hold the unknown UUID
			forward := map[string]interface{}{
				"protocol":           uuid,
				"private_port":       0,
				"public_port":        0,
				"virtual_machine_id": uuid,
				"uuid":               uuid,
			}

			// Add the dummy forward to the forwards set
			forwards.Add(forward)
		}
	}

	if forwards.Len() > 0 {
		d.Set("forward", forwards)
	} else if !managed {
		d.SetId("")
	}

	return nil
}
func resourceCloudStackNetworkCreate(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cloudstack.CloudStackClient)

	name := d.Get("name").(string)

	// Retrieve the network_offering ID
	networkofferingid, e := retrieveID(cs, "network_offering", d.Get("network_offering").(string))
	if e != nil {
		return e.Error()
	}

	// Retrieve the zone ID
	zoneid, e := retrieveID(cs, "zone", d.Get("zone").(string))
	if e != nil {
		return e.Error()
	}

	// Compute/set the display text
	displaytext, ok := d.GetOk("display_text")
	if !ok {
		displaytext = name
	}
	// Create a new parameter struct
	p := cs.Network.NewCreateNetworkParams(displaytext.(string), name, networkofferingid, zoneid)

	m, err := parseCIDR(d)
	if err != nil {
		return err
	}

	// Set the needed IP config
	p.SetStartip(m["startip"])
	p.SetGateway(m["gateway"])
	p.SetEndip(m["endip"])
	p.SetNetmask(m["netmask"])

	if vlan, ok := d.GetOk("vlan"); ok {
		p.SetVlan(strconv.Itoa(vlan.(int)))
	}

	// Check is this network needs to be created in a VPC
	vpc, ok := d.GetOk("vpc_id")
	if !ok {
		vpc, ok = d.GetOk("vpc")
	}
	if ok {
		// Retrieve the vpc ID
		vpcid, e := retrieveID(
			cs,
			"vpc",
			vpc.(string),
			cloudstack.WithProject(d.Get("project").(string)),
		)
		if e != nil {
			return e.Error()
		}

		// Set the vpcid
		p.SetVpcid(vpcid)

		// Since we're in a VPC, check if we want to assiciate an ACL list
		aclid, ok := d.GetOk("acl_id")
		if !ok {
			aclid, ok = d.GetOk("acl")
		}
		if ok {
			// Set the acl ID
			p.SetAclid(aclid.(string))
		}
	}

	// If there is a project supplied, we retrieve and set the project id
	if err := setProjectid(p, cs, d); err != nil {
		return err
	}

	// Create the new network
	r, err := cs.Network.CreateNetwork(p)
	if err != nil {
		return fmt.Errorf("Error creating network %s: %s", name, err)
	}

	d.SetId(r.Id)

	err = setTags(cs, d, "network")
	if err != nil {
		return fmt.Errorf("Error setting tags: %s", err)
	}

	return resourceCloudStackNetworkRead(d, meta)
}