| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | package cloudstack | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2018-01-22 15:32:33 -08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2016-11-16 10:30:32 +01:00
										 |  |  | 	"math/rand" | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2016-11-16 10:30:32 +01:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-19 16:18:44 -08:00
										 |  |  | 	"github.com/hashicorp/packer/helper/multistep" | 
					
						
							| 
									
										
										
										
											2017-04-04 13:39:01 -07:00
										 |  |  | 	"github.com/hashicorp/packer/packer" | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 	"github.com/xanzy/go-cloudstack/cloudstack" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-16 10:30:32 +01:00
										 |  |  | type stepSetupNetworking struct { | 
					
						
							|  |  |  | 	privatePort int | 
					
						
							|  |  |  | 	publicPort  int | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-22 15:31:41 -08:00
										 |  |  | func (s *stepSetupNetworking) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 	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...") | 
					
						
							| 
									
										
										
										
											2017-07-17 09:05:12 +02:00
										 |  |  | 		state.Put("commPort", config.Comm.Port()) | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 		ui.Message("Networking has been setup!") | 
					
						
							|  |  |  | 		return multistep.ActionContinue | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-22 10:17:46 +01:00
										 |  |  | 	if config.PublicPort != 0 { | 
					
						
							|  |  |  | 		s.publicPort = config.PublicPort | 
					
						
							| 
									
										
										
										
											2018-07-24 18:05:42 +01:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		// Generate a random public port used to configure our port forward. | 
					
						
							|  |  |  | 		rand.Seed(time.Now().UnixNano()) | 
					
						
							|  |  |  | 		s.publicPort = 50000 + rand.Intn(10000) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-07-17 09:05:12 +02:00
										 |  |  | 	state.Put("commPort", s.publicPort) | 
					
						
							| 
									
										
										
										
											2016-11-16 10:30:32 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Set the currently configured port to be the private port. | 
					
						
							|  |  |  | 	s.privatePort = config.Comm.Port() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 	// Retrieve the instance ID from the previously saved state. | 
					
						
							|  |  |  | 	instanceID, ok := state.Get("instance_id").(string) | 
					
						
							|  |  |  | 	if !ok || instanceID == "" { | 
					
						
							| 
									
										
										
										
											2017-07-14 07:11:30 +02:00
										 |  |  | 		err := fmt.Errorf("Could not retrieve instance_id from state!") | 
					
						
							|  |  |  | 		state.Put("error", err) | 
					
						
							|  |  |  | 		ui.Error(err.Error()) | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 		return multistep.ActionHalt | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	network, _, err := client.Network.GetNetworkByID( | 
					
						
							|  |  |  | 		config.Network, | 
					
						
							|  |  |  | 		cloudstack.WithProject(config.Project), | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-07-14 07:11:30 +02:00
										 |  |  | 		err := fmt.Errorf("Failed to retrieve the network object: %s", err) | 
					
						
							|  |  |  | 		state.Put("error", err) | 
					
						
							|  |  |  | 		ui.Error(err.Error()) | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 		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) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-10 04:11:52 +09:00
										 |  |  | 		p.SetZoneid(config.Zone) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 		// Associate a new public IP address. | 
					
						
							|  |  |  | 		ipAddr, err := client.Address.AssociateIpAddress(p) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2017-07-14 07:11:30 +02:00
										 |  |  | 			err := fmt.Errorf("Failed to associate public IP address: %s", err) | 
					
						
							|  |  |  | 			state.Put("error", err) | 
					
						
							|  |  |  | 			ui.Error(err.Error()) | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 			return multistep.ActionHalt | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Set the IP address and it's ID. | 
					
						
							|  |  |  | 		config.PublicIPAddress = ipAddr.Id | 
					
						
							| 
									
										
										
										
											2017-07-17 09:05:12 +02:00
										 |  |  | 		state.Put("ipaddress", ipAddr.Ipaddress) | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Store the IP address ID. | 
					
						
							|  |  |  | 		state.Put("ip_address_id", ipAddr.Id) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ui.Message("Creating port forward...") | 
					
						
							|  |  |  | 	p := client.Firewall.NewCreatePortForwardingRuleParams( | 
					
						
							|  |  |  | 		config.PublicIPAddress, | 
					
						
							| 
									
										
										
										
											2016-11-16 10:30:32 +01:00
										 |  |  | 		s.privatePort, | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 		"TCP", | 
					
						
							| 
									
										
										
										
											2016-11-16 10:30:32 +01:00
										 |  |  | 		s.publicPort, | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 		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 { | 
					
						
							| 
									
										
										
										
											2017-07-14 07:11:30 +02:00
										 |  |  | 		err := fmt.Errorf("Failed to create port forward: %s", err) | 
					
						
							|  |  |  | 		state.Put("error", err) | 
					
						
							|  |  |  | 		ui.Error(err.Error()) | 
					
						
							|  |  |  | 		return multistep.ActionHalt | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Store the port forward ID. | 
					
						
							|  |  |  | 	state.Put("port_forward_id", forward.Id) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-16 13:06:51 +02:00
										 |  |  | 	if config.PreventFirewallChanges { | 
					
						
							|  |  |  | 		ui.Message("Networking has been setup (without firewall changes)!") | 
					
						
							|  |  |  | 		return multistep.ActionContinue | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if network.Vpcid != "" { | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 		ui.Message("Creating network ACL rule...") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if network.Aclid == "" { | 
					
						
							| 
									
										
										
										
											2017-07-14 07:11:30 +02:00
										 |  |  | 			err := fmt.Errorf("Failed to configure the firewall: no ACL connected to the VPC network") | 
					
						
							|  |  |  | 			state.Put("error", err) | 
					
						
							|  |  |  | 			ui.Error(err.Error()) | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 			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) | 
					
						
							| 
									
										
										
										
											2016-11-16 10:30:32 +01:00
										 |  |  | 		p.SetStartport(s.privatePort) | 
					
						
							|  |  |  | 		p.SetEndport(s.privatePort) | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 		p.SetTraffictype("ingress") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Create the network ACL rule. | 
					
						
							|  |  |  | 		aclRule, err := client.NetworkACL.CreateNetworkACL(p) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2017-07-14 07:11:30 +02:00
										 |  |  | 			err := fmt.Errorf("Failed to create network ACL rule: %s", err) | 
					
						
							|  |  |  | 			state.Put("error", err) | 
					
						
							|  |  |  | 			ui.Error(err.Error()) | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 			return multistep.ActionHalt | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Store the network ACL rule ID. | 
					
						
							|  |  |  | 		state.Put("network_acl_rule_id", aclRule.Id) | 
					
						
							| 
									
										
										
										
											2018-07-16 13:06:51 +02:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 		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) | 
					
						
							| 
									
										
										
										
											2016-11-16 10:30:32 +01:00
										 |  |  | 		p.SetStartport(s.publicPort) | 
					
						
							|  |  |  | 		p.SetEndport(s.publicPort) | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		fwRule, err := client.Firewall.CreateFirewallRule(p) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2017-07-14 07:11:30 +02:00
										 |  |  | 			err := fmt.Errorf("Failed to create firewall rule: %s", err) | 
					
						
							|  |  |  | 			state.Put("error", err) | 
					
						
							|  |  |  | 			ui.Error(err.Error()) | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 			return multistep.ActionHalt | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Store the firewall rule ID. | 
					
						
							|  |  |  | 		state.Put("firewall_rule_id", fwRule.Id) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ui.Message("Networking has been setup!") | 
					
						
							|  |  |  | 	return multistep.ActionContinue | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Cleanup any resources that may have been created during the Run phase. | 
					
						
							|  |  |  | func (s *stepSetupNetworking) Cleanup(state multistep.StateBag) { | 
					
						
							|  |  |  | 	client := state.Get("client").(*cloudstack.CloudStackClient) | 
					
						
							|  |  |  | 	ui := state.Get("ui").(packer.Ui) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ui.Say("Cleanup networking...") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if fwRuleID, ok := state.Get("firewall_rule_id").(string); ok && fwRuleID != "" { | 
					
						
							|  |  |  | 		// Create a new parameter struct. | 
					
						
							|  |  |  | 		p := client.Firewall.NewDeleteFirewallRuleParams(fwRuleID) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-13 07:49:46 +00:00
										 |  |  | 		ui.Message("Deleting firewall rule...") | 
					
						
							| 
									
										
										
										
											2016-01-11 12:22:41 +01:00
										 |  |  | 		if _, err := client.Firewall.DeleteFirewallRule(p); err != nil { | 
					
						
							|  |  |  | 			// This is a very poor way to be told the ID does no longer exist :( | 
					
						
							|  |  |  | 			if !strings.Contains(err.Error(), fmt.Sprintf( | 
					
						
							|  |  |  | 				"Invalid parameter id value=%s due to incorrect long value format, "+ | 
					
						
							|  |  |  | 					"or entity does not exist", fwRuleID)) { | 
					
						
							|  |  |  | 				ui.Error(fmt.Sprintf("Error deleting firewall rule: %s", err)) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if aclRuleID, ok := state.Get("network_acl_rule_id").(string); ok && aclRuleID != "" { | 
					
						
							|  |  |  | 		// Create a new parameter struct. | 
					
						
							|  |  |  | 		p := client.NetworkACL.NewDeleteNetworkACLParams(aclRuleID) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ui.Message("Deleting network ACL rule...") | 
					
						
							|  |  |  | 		if _, err := client.NetworkACL.DeleteNetworkACL(p); err != nil { | 
					
						
							|  |  |  | 			// This is a very poor way to be told the ID does no longer exist :( | 
					
						
							|  |  |  | 			if !strings.Contains(err.Error(), fmt.Sprintf( | 
					
						
							|  |  |  | 				"Invalid parameter id value=%s due to incorrect long value format, "+ | 
					
						
							|  |  |  | 					"or entity does not exist", aclRuleID)) { | 
					
						
							|  |  |  | 				ui.Error(fmt.Sprintf("Error deleting network ACL rule: %s", err)) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if forwardID, ok := state.Get("port_forward_id").(string); ok && forwardID != "" { | 
					
						
							|  |  |  | 		// Create a new parameter struct. | 
					
						
							|  |  |  | 		p := client.Firewall.NewDeletePortForwardingRuleParams(forwardID) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ui.Message("Deleting port forward...") | 
					
						
							|  |  |  | 		if _, err := client.Firewall.DeletePortForwardingRule(p); err != nil { | 
					
						
							|  |  |  | 			// This is a very poor way to be told the ID does no longer exist :( | 
					
						
							|  |  |  | 			if !strings.Contains(err.Error(), fmt.Sprintf( | 
					
						
							|  |  |  | 				"Invalid parameter id value=%s due to incorrect long value format, "+ | 
					
						
							|  |  |  | 					"or entity does not exist", forwardID)) { | 
					
						
							|  |  |  | 				ui.Error(fmt.Sprintf("Error deleting port forward: %s", err)) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ipAddrID, ok := state.Get("ip_address_id").(string); ok && ipAddrID != "" { | 
					
						
							|  |  |  | 		// Create a new parameter struct. | 
					
						
							|  |  |  | 		p := client.Address.NewDisassociateIpAddressParams(ipAddrID) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ui.Message("Releasing public IP address...") | 
					
						
							|  |  |  | 		if _, err := client.Address.DisassociateIpAddress(p); err != nil { | 
					
						
							|  |  |  | 			// This is a very poor way to be told the ID does no longer exist :( | 
					
						
							|  |  |  | 			if !strings.Contains(err.Error(), fmt.Sprintf( | 
					
						
							|  |  |  | 				"Invalid parameter id value=%s due to incorrect long value format, "+ | 
					
						
							|  |  |  | 					"or entity does not exist", ipAddrID)) { | 
					
						
							|  |  |  | 				ui.Error(fmt.Sprintf("Error releasing public IP address: %s", err)) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ui.Message("Networking has been cleaned!") | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } |