vagrant-cloudstack 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.travis.yml +13 -9
  4. data/CHANGELOG.md +30 -0
  5. data/Docker/Dockerfile +5 -8
  6. data/Docker/Dockerfile.chefdk_0_17 +2 -2
  7. data/Docker/Dockerfile.latest_dependencies +2 -2
  8. data/Docker/README.md +63 -35
  9. data/Gemfile +2 -2
  10. data/Gemfile.lock +307 -0
  11. data/README.md +3 -3
  12. data/Rakefile +2 -2
  13. data/build_rpm.sh +1 -1
  14. data/functional-tests/rsync/Vagrantfile.advanced_networking +1 -0
  15. data/functional-tests/vmlifecycle/Vagrantfile.advanced_networking +5 -7
  16. data/functional-tests/vmlifecycle/vmlifecycle_spec.rb +14 -2
  17. data/lib/vagrant-cloudstack/action/read_rdp_info.rb +9 -43
  18. data/lib/vagrant-cloudstack/action/read_ssh_info.rb +10 -44
  19. data/lib/vagrant-cloudstack/action/read_transport_info.rb +59 -0
  20. data/lib/vagrant-cloudstack/action/read_winrm_info.rb +10 -44
  21. data/lib/vagrant-cloudstack/action/run_instance.rb +607 -498
  22. data/lib/vagrant-cloudstack/action/terminate_instance.rb +17 -41
  23. data/lib/vagrant-cloudstack/config.rb +41 -166
  24. data/lib/vagrant-cloudstack/exceptions/exceptions.rb +7 -2
  25. data/lib/vagrant-cloudstack/service/cloudstack_resource_service.rb +17 -5
  26. data/lib/vagrant-cloudstack/version.rb +1 -1
  27. data/spec/spec_helper.rb +45 -0
  28. data/spec/vagrant-cloudstack/action/retrieve_public_ip_port_spec.rb +94 -0
  29. data/spec/vagrant-cloudstack/action/run_instance_spec.rb +609 -0
  30. data/spec/vagrant-cloudstack/action/terminate_instance_spec.rb +248 -0
  31. data/spec/vagrant-cloudstack/config_spec.rb +7 -7
  32. data/vagrant-cloudstack.gemspec +2 -1
  33. metadata +22 -10
  34. data/bootstrap.key +0 -27
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Vagrant Cloudstack Provider
2
2
 
3
- [![Build Status](https://travis-ci.org/MissionCriticalCloud/vagrant-cloudstack.png?branch=master)](https://travis-ci.org/missioncriticalcloud/vagrant-cloudstack)
3
+ [![Build Status](https://travis-ci.org/MissionCriticalCloud/vagrant-cloudstack.png?branch=master)](https://travis-ci.org/MissionCriticalCloud/vagrant-cloudstack)
4
4
  [![Gem Version](https://badge.fury.io/rb/vagrant-cloudstack.png)](http://badge.fury.io/rb/vagrant-cloudstack)
5
- [![Code climate](https://codeclimate.com/github/MissionCriticalCloud/vagrant-cloudstack.png)](https://codeclimate.com/github/missioncriticalcloud/vagrant-cloudstack)
5
+ [![Code climate](https://codeclimate.com/github/MissionCriticalCloud/vagrant-cloudstack.png)](https://codeclimate.com/github/MissionCriticalCloud/vagrant-cloudstack)
6
6
  [![Coverage Status](https://coveralls.io/repos/github/MissionCriticalCloud/vagrant-cloudstack/badge.svg?branch=master)](https://coveralls.io/github/MissionCriticalCloud/vagrant-cloudstack?branch=master)
7
- [![Gem Version](https://badge.fury.io/rb/vagrant-cloudstack.svg)](http://badge.fury.io/rb/vagrant-cloudstack)
7
+ [![BCH compliance](https://bettercodehub.com/edge/badge/MissionCriticalCloud/vagrant-cloudstack?branch=master)](https://bettercodehub.com/)
8
8
 
9
9
  This is a fork of [mitchellh AWS Provider](https://github.com/mitchellh/vagrant-aws/).
10
10
 
data/Rakefile CHANGED
@@ -5,6 +5,7 @@ require 'rspec/core/rake_task'
5
5
  RSpec::Core::RakeTask.new(:functionaltest) do |t|
6
6
  t.pattern = "*_spec.rb"
7
7
  t.rspec_opts = "-fd"
8
+ t.verbose = false
8
9
  end
9
10
 
10
11
  # Immediately sync all stdout so that tools like buildbot can
@@ -101,6 +102,5 @@ namespace :functional_tests do
101
102
  end
102
103
  end
103
104
  end
104
-
105
-
106
105
  end
106
+
data/build_rpm.sh CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/bin/bash
2
- VERSION=1.4.0
2
+ VERSION=1.5.0
3
3
  mkdir -p /tmp/vagrant-cloudstack-build_rpm.$$/vagrant-cloudstack-$VERSION
4
4
  cp -r . /tmp/vagrant-cloudstack-build_rpm.$$/vagrant-cloudstack-$VERSION/
5
5
  tar -C /tmp/vagrant-cloudstack-build_rpm.$$/ -czf ~/rpmbuild/SOURCES/vagrant-cloudstack-$VERSION.tar.gz vagrant-cloudstack-$VERSION
@@ -21,6 +21,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
21
21
  cloudstack.scheme = 'https'
22
22
  cloudstack.api_key = ENV['CLOUDSTACK_API_KEY']
23
23
  cloudstack.secret_key = ENV['CLOUDSTACK_SECRET_KEY']
24
+ cloudstack.expunge_on_destroy = ENV['EXPUNGE_ON_DESTROY']=="true"
24
25
 
25
26
  cloudstack.zone_name = ENV['ZONE_NAME']
26
27
  cloudstack.network_name = ENV['NETWORK_NAME']
@@ -26,6 +26,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |global_config|
26
26
  config.vm.box = options['template']
27
27
 
28
28
  config.vm.communicator = options['communicator']
29
+ config.winrm.retry_delay = 30
29
30
  config.vm.synced_folder ".", "/vagrant", type: "rsync",
30
31
  rsync__exclude: [".git/", "vendor"], disabled: options['rsync_disabled']
31
32
  config.vm.provider :cloudstack do |cloudstack, override|
@@ -52,14 +53,11 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |global_config|
52
53
  cloudstack.pf_trusted_networks = ENV['SOURCE_CIDR']
53
54
  cloudstack.pf_open_firewall = false
54
55
 
55
- unless ENV['SSH_KEY'].nil?
56
- cloudstack.ssh_key = ENV['SSH_KEY']
57
- cloudstack.ssh_user = ENV['SSH_USER']
58
- end
59
56
 
60
- unless ENV['WINDOWS_USER'].nil?
61
- cloudstack.vm_user = ENV['WINDOWS_USER']
62
- end
57
+ cloudstack.ssh_key = ENV['SSH_KEY'] unless ENV['SSH_KEY'].nil?
58
+ cloudstack.ssh_user = ENV['SSH_USER'] unless ENV['SSH_USER'].nil?
59
+ cloudstack.vm_user = ENV['WINDOWS_USER'] unless ENV['WINDOWS_USER'].nil?
60
+
63
61
  end
64
62
  end
65
63
  end
@@ -1,13 +1,25 @@
1
+ def which(cmd)
2
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
3
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
4
+ exts.each { |ext|
5
+ exe = File.join(path, "#{cmd}#{ext}")
6
+ return exe if File.executable?(exe) && !File.directory?(exe)
7
+ }
8
+ end
9
+ return nil
10
+ end
11
+ insert_tee_log = ' 2>&1 | tee -a vagrant.log ' if which('tee')
12
+
1
13
  describe 'VM Life Cycle' do
2
14
  it 'starts Linux and Windows VM' do
3
- expect(`vagrant up`).to include(
15
+ expect(`vagrant up #{insert_tee_log}`).to include(
4
16
  'linux-box: Machine is booted and ready for use!',
5
17
  'windows-box: Machine is booted and ready for use!'
6
18
  )
7
19
  expect($?.exitstatus).to eq(0)
8
20
  end
9
21
  it 'destroys Linux and Windows VM' do
10
- expect(`vagrant destroy --force`).to include('Done removing resources')
22
+ expect(`vagrant destroy --force #{insert_tee_log}`).to include('Done removing resources')
11
23
  expect($?.exitstatus).to eq(0)
12
24
  end
13
25
  end
@@ -1,3 +1,4 @@
1
+ require_relative 'read_transport_info.rb'
1
2
  require 'log4r'
2
3
 
3
4
  module VagrantPlugins
@@ -5,10 +6,12 @@ module VagrantPlugins
5
6
  module Action
6
7
  # This action reads the WinRM info for the machine and puts it into the
7
8
  # `:machine_winrm_info` key in the environment.
8
- class ReadRdpInfo
9
+ class ReadRdpInfo < ReadTransportInfo
9
10
  def initialize(app, env)
10
11
  @app = app
11
12
  @logger = Log4r::Logger.new("vagrant_cloudstack::action::read_rdp_info")
13
+
14
+ @public_port_fieldname = 'pf_public_rdp_port'
12
15
  end
13
16
 
14
17
  def call(env)
@@ -18,57 +21,20 @@ module VagrantPlugins
18
21
  end
19
22
 
20
23
  def read_rdp_info(cloudstack, machine)
21
- return nil if machine.id.nil?
22
-
23
- # Find the machine
24
- server = cloudstack.servers.get(machine.id)
25
- if server.nil?
26
- # The machine can't be found
27
- @logger.info("Machine couldn't be found, assuming it got destroyed.")
28
- machine.id = nil
29
- return nil
30
- end
24
+ return nil if (server = find_server(cloudstack, machine)).nil?
31
25
 
32
26
  # Get the Port forwarding config
33
27
  domain = machine.provider_config.domain_id
34
28
  domain_config = machine.provider_config.get_domain_config(domain)
35
29
 
36
- pf_ip_address_id = domain_config.pf_ip_address_id
37
- pf_ip_address = domain_config.pf_ip_address
38
- pf_public_rdp_port = domain_config.pf_public_rdp_port
39
-
40
- if pf_public_rdp_port.nil?
41
- pf_public_rdp_port_file = machine.data_dir.join('pf_public_rdp_port')
42
- if pf_public_rdp_port_file.file?
43
- File.read(pf_public_rdp_port_file).each_line do |line|
44
- pf_public_rdp_port = line.strip
45
- end
46
- domain_config.pf_public_rdp_port = pf_public_rdp_port
47
- end
48
- end
49
-
50
- if not pf_ip_address and pf_ip_address_id and pf_public_rdp_port
51
- begin
52
- response = cloudstack.list_public_ip_addresses({:id => pf_ip_address_id})
53
- rescue Fog::Compute::Cloudstack::Error => e
54
- raise Errors::FogError, :message => e.message
55
- end
56
-
57
- if response["listpublicipaddressesresponse"]["count"] == 0
58
- @logger.info("IP address #{pf_ip_address_id} not exists.")
59
- env[:ui].info(I18n.t("IP address #{pf_ip_address_id} not exists."))
60
- pf_ip_address = nil
61
- else
62
- pf_ip_address = response["listpublicipaddressesresponse"]["publicipaddress"][0]["ipaddress"]
63
- end
64
- end
30
+ pf_ip_address, pf_public_port = retrieve_public_ip_port(cloudstack, domain_config, machine)
65
31
 
66
- rdp_info = {
32
+ transport_info = {
67
33
  :host => pf_ip_address || server.nics[0]['ipaddress'],
68
- :port => pf_public_rdp_port
34
+ :port => pf_public_port
69
35
  }
70
36
 
71
- rdp_info
37
+ transport_info
72
38
  end
73
39
  end
74
40
  end
@@ -1,3 +1,4 @@
1
+ require_relative 'read_transport_info.rb'
1
2
  require 'log4r'
2
3
 
3
4
  module VagrantPlugins
@@ -5,10 +6,12 @@ module VagrantPlugins
5
6
  module Action
6
7
  # This action reads the SSH info for the machine and puts it into the
7
8
  # `:machine_ssh_info` key in the environment.
8
- class ReadSSHInfo
9
+ class ReadSSHInfo < ReadTransportInfo
9
10
  def initialize(app, env)
10
11
  @app = app
11
12
  @logger = Log4r::Logger.new("vagrant_cloudstack::action::read_ssh_info")
13
+
14
+ @public_port_fieldname = 'pf_public_port'
12
15
  end
13
16
 
14
17
  def call(env)
@@ -18,50 +21,13 @@ module VagrantPlugins
18
21
  end
19
22
 
20
23
  def read_ssh_info(cloudstack, machine)
21
- return nil if machine.id.nil?
22
-
23
- # Find the machine
24
- server = cloudstack.servers.get(machine.id)
25
- if server.nil?
26
- # The machine can't be found
27
- @logger.info("Machine couldn't be found, assuming it got destroyed.")
28
- machine.id = nil
29
- return nil
30
- end
24
+ return nil if (server = find_server(cloudstack, machine)).nil?
31
25
 
32
26
  # Get the Port forwarding config
33
27
  domain = machine.provider_config.domain_id
34
28
  domain_config = machine.provider_config.get_domain_config(domain)
35
29
 
36
- pf_ip_address_id = domain_config.pf_ip_address_id
37
- pf_ip_address = domain_config.pf_ip_address
38
- pf_public_port = domain_config.pf_public_port
39
-
40
- if pf_public_port.nil?
41
- pf_public_port_file = machine.data_dir.join('pf_public_port')
42
- if pf_public_port_file.file?
43
- File.read(pf_public_port_file).each_line do |line|
44
- pf_public_port = line.strip
45
- end
46
- domain_config.pf_public_port = pf_public_port
47
- end
48
- end
49
-
50
- if not pf_ip_address and pf_ip_address_id and pf_public_port
51
- begin
52
- response = cloudstack.list_public_ip_addresses({:id => pf_ip_address_id})
53
- rescue Fog::Compute::Cloudstack::Error => e
54
- raise Errors::FogError, :message => e.message
55
- end
56
-
57
- if response["listpublicipaddressesresponse"]["count"] == 0
58
- @logger.info("IP address #{pf_ip_address_id} not exists.")
59
- env[:ui].info(I18n.t("IP address #{pf_ip_address_id} not exists."))
60
- pf_ip_address = nil
61
- else
62
- pf_ip_address = response["listpublicipaddressesresponse"]["publicipaddress"][0]["ipaddress"]
63
- end
64
- end
30
+ pf_ip_address, pf_public_port = retrieve_public_ip_port(cloudstack, domain_config, machine)
65
31
 
66
32
  if domain_config.keypair.nil? && domain_config.ssh_key.nil?
67
33
  sshkeyfile_file = machine.data_dir.join('sshkeyfile')
@@ -72,16 +38,16 @@ module VagrantPlugins
72
38
 
73
39
  nic_ip_address = fetch_nic_ip_address(server.nics, domain_config)
74
40
 
75
- ssh_info = {
41
+ transport_info = {
76
42
  :host => pf_ip_address || nic_ip_address,
77
43
  :port => pf_public_port
78
44
  }
79
- ssh_info = ssh_info.merge({
45
+ transport_info = transport_info.merge({
80
46
  :private_key_path => domain_config.ssh_key,
81
47
  :password => nil
82
48
  }) unless domain_config.ssh_key.nil?
83
- ssh_info = ssh_info.merge({ :username => domain_config.ssh_user }) unless domain_config.ssh_user.nil?
84
- ssh_info
49
+ transport_info = transport_info.merge({ :username => domain_config.ssh_user }) unless domain_config.ssh_user.nil?
50
+ transport_info
85
51
  end
86
52
 
87
53
  def fetch_nic_ip_address(nics, domain_config)
@@ -0,0 +1,59 @@
1
+ module VagrantPlugins
2
+ module Cloudstack
3
+ module Action
4
+ class ReadTransportInfo
5
+ def initialize
6
+ @public_port_fieldname = 'pf_public_port'
7
+ end
8
+
9
+ def retrieve_public_ip_port(cloudstack, domain_config, machine)
10
+ pf_ip_address_id = domain_config.pf_ip_address_id
11
+ pf_ip_address = domain_config.pf_ip_address
12
+ pf_public_port = domain_config.send(@public_port_fieldname)
13
+
14
+ if pf_public_port.nil?
15
+ pf_public_port_file = machine.data_dir.join(@public_port_fieldname)
16
+ if pf_public_port_file.file?
17
+ File.read(pf_public_port_file).each_line do |line|
18
+ pf_public_port = line.strip
19
+ end
20
+ domain_config.send("#{@public_port_fieldname}=", pf_public_port)
21
+ end
22
+ end
23
+
24
+ if not pf_ip_address and pf_ip_address_id and pf_public_port
25
+ begin
26
+ response = cloudstack.list_public_ip_addresses({:id => pf_ip_address_id})
27
+ rescue Fog::Compute::Cloudstack::Error => e
28
+ raise Errors::FogError, :message => e.message
29
+ end
30
+
31
+ if (response['listpublicipaddressesresponse']['count']).zero?
32
+ @logger.info("IP address #{pf_ip_address_id} not exists.")
33
+ env[:ui].info(I18n.t("IP address #{pf_ip_address_id} not exists."))
34
+ pf_ip_address = nil
35
+ else
36
+ pf_ip_address = response['listpublicipaddressesresponse']['publicipaddress'][0]['ipaddress']
37
+ end
38
+ end
39
+ return pf_ip_address, pf_public_port
40
+ end
41
+
42
+ def find_server(cloudstack, machine)
43
+ return nil if machine.id.nil?
44
+
45
+ # Find the machine
46
+ server = cloudstack.servers.get(machine.id)
47
+ if server.nil? || [:"shutting-down", :terminated].include?(server.state.downcase.to_sym)
48
+ # The machine can't be found
49
+ @logger.info("Machine couldn't be found, assuming it got destroyed.")
50
+ machine.id = nil
51
+ return nil
52
+ end
53
+
54
+ server
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,3 +1,4 @@
1
+ require_relative 'read_transport_info.rb'
1
2
  require 'log4r'
2
3
 
3
4
  module VagrantPlugins
@@ -5,10 +6,12 @@ module VagrantPlugins
5
6
  module Action
6
7
  # This action reads the WinRM info for the machine and puts it into the
7
8
  # `:machine_winrm_info` key in the environment.
8
- class ReadWinrmInfo
9
+ class ReadWinrmInfo < ReadTransportInfo
9
10
  def initialize(app, env)
10
11
  @app = app
11
12
  @logger = Log4r::Logger.new("vagrant_cloudstack::action::read_winrm_info")
13
+
14
+ @public_port_fieldname = 'pf_public_port'
12
15
  end
13
16
 
14
17
  def call(env)
@@ -18,58 +21,21 @@ module VagrantPlugins
18
21
  end
19
22
 
20
23
  def read_winrm_info(cloudstack, machine)
21
- return nil if machine.id.nil?
22
-
23
- # Find the machine
24
- server = cloudstack.servers.get(machine.id)
25
- if server.nil?
26
- # The machine can't be found
27
- @logger.info("Machine couldn't be found, assuming it got destroyed.")
28
- machine.id = nil
29
- return nil
30
- end
24
+ return nil if (server = find_server(cloudstack, machine)).nil?
31
25
 
32
26
  # Get the Port forwarding config
33
27
  domain = machine.provider_config.domain_id
34
28
  domain_config = machine.provider_config.get_domain_config(domain)
35
29
 
36
- pf_ip_address_id = domain_config.pf_ip_address_id
37
- pf_ip_address = domain_config.pf_ip_address
38
- pf_public_port = domain_config.pf_public_port
39
-
40
- if pf_public_port.nil?
41
- pf_public_port_file = machine.data_dir.join('pf_public_port')
42
- if pf_public_port_file.file?
43
- File.read(pf_public_port_file).each_line do |line|
44
- pf_public_port = line.strip
45
- end
46
- domain_config.pf_public_port = pf_public_port
47
- end
48
- end
49
-
50
- if not pf_ip_address and pf_ip_address_id and pf_public_port
51
- begin
52
- response = cloudstack.list_public_ip_addresses({:id => pf_ip_address_id})
53
- rescue Fog::Compute::Cloudstack::Error => e
54
- raise Errors::FogError, :message => e.message
55
- end
56
-
57
- if response["listpublicipaddressesresponse"]["count"] == 0
58
- @logger.info("IP address #{pf_ip_address_id} not exists.")
59
- env[:ui].info(I18n.t("IP address #{pf_ip_address_id} not exists."))
60
- pf_ip_address = nil
61
- else
62
- pf_ip_address = response["listpublicipaddressesresponse"]["publicipaddress"][0]["ipaddress"]
63
- end
64
- end
30
+ pf_ip_address, pf_public_port = retrieve_public_ip_port(cloudstack, domain_config, machine)
65
31
 
66
32
 
67
- winrm_info = {
33
+ transport_info = {
68
34
  :host => pf_ip_address || server.nics[0]['ipaddress'],
69
35
  :port => pf_public_port
70
36
  }
71
37
 
72
- winrm_info = winrm_info.merge({
38
+ transport_info = transport_info.merge({
73
39
  :username => domain_config.vm_user
74
40
  }) unless domain_config.vm_user.nil?
75
41
  machine.config.winrm.username = domain_config.vm_user unless domain_config.vm_user.nil?
@@ -88,14 +54,14 @@ module VagrantPlugins
88
54
  end
89
55
  end
90
56
 
91
- winrm_info = winrm_info.merge({
57
+ transport_info = transport_info.merge({
92
58
  :password => domain_config.vm_password
93
59
  }) unless domain_config.vm_password.nil?
94
60
  # The WinRM communicator doesnt support passing
95
61
  # the password via winrm_info ... yet ;-)
96
62
  machine.config.winrm.password = domain_config.vm_password unless domain_config.vm_password.nil?
97
63
 
98
- winrm_info
64
+ transport_info
99
65
  end
100
66
  end
101
67
  end
@@ -24,94 +24,20 @@ module VagrantPlugins
24
24
 
25
25
  def call(env)
26
26
  # Initialize metrics if they haven't been
27
- env[:metrics] ||= {}
27
+ env[:metrics] ||= {}
28
28
  @env = env
29
29
 
30
- # Get the domain we're going to booting up in
31
- @domain = @env[:machine].provider_config.domain_id
32
- # Get the configs
33
- @domain_config = @env[:machine].provider_config.get_domain_config(@domain)
34
-
35
- sanitize_domain_config
36
-
37
- @zone = CloudstackResource.new(@domain_config.zone_id, @domain_config.zone_name, 'zone')
38
- @networks = CloudstackResource.create_list(@domain_config.network_id, @domain_config.network_name, 'network')
39
- @service_offering = CloudstackResource.new(@domain_config.service_offering_id, @domain_config.service_offering_name, 'service_offering')
40
- @disk_offering = CloudstackResource.new(@domain_config.disk_offering_id, @domain_config.disk_offering_name, 'disk_offering')
41
- @template = CloudstackResource.new(@domain_config.template_id, @domain_config.template_name || @env[:machine].config.vm.box, 'template')
42
- @pf_ip_address = CloudstackResource.new(@domain_config.pf_ip_address_id, @domain_config.pf_ip_address, 'public_ip_address')
43
-
44
- @resource_service.sync_resource(@zone, { available: true })
45
- cs_zone = @env[:cloudstack_compute].zones.find{ |f| f.id == @zone.id }
46
- @resource_service.sync_resource(@service_offering, {listall: true})
47
- @resource_service.sync_resource(@disk_offering, {listall: true})
48
- @resource_service.sync_resource(@template, {zoneid: @zone.id, templatefilter: 'executable', listall: true})
49
- @resource_service.sync_resource(@pf_ip_address)
50
-
51
- if cs_zone.network_type.downcase == 'basic'
52
- # No network specification in basic zone
53
- @env[:ui].warn(I18n.t('vagrant_cloudstack.basic_network', :zone_name => @zone.name)) if !@networks.empty? && (@networks[0].id || @networks[0].name)
54
- @networks = [CloudstackResource.new(nil, nil, 'network')]
55
-
56
- # No portforwarding in basic zone, so none of the below
57
- @domain_config.pf_ip_address = nil
58
- @domain_config.pf_ip_address_id = nil
59
- @domain_config.pf_public_port = nil
60
- @domain_config.pf_public_rdp_port = nil
61
- @domain_config.pf_public_port_randomrange = nil
62
- else
63
- @networks.each do |network|
64
- @resource_service.sync_resource(network)
65
- end
66
- end
67
-
68
- if cs_zone.security_groups_enabled
69
- prepare_security_groups
70
- else
71
- if !@domain_config.security_group_ids.empty? || !@domain_config.security_group_names.empty? || !@domain_config.security_groups.empty?
72
- @env[:ui].warn(I18n.t('vagrant_cloudstack.security_groups_disabled', :zone_name => @zone.name))
73
- end
74
- @domain_config.security_group_ids = []
75
- @domain_config.security_group_names = []
76
- @domain_config.security_groups = []
77
- end
78
-
79
- @domain_config.display_name = generate_display_name if @domain_config.display_name.nil?
80
-
81
- # If there is no keypair or keyfile then warn the user
82
- if @domain_config.keypair.nil? && @domain_config.ssh_key.nil?
83
- @env[:ui].warn(I18n.t('vagrant_cloudstack.launch_no_keypair_no_sshkey'))
84
- store_ssh_keypair("vagacs_#{@domain_config.display_name}_#{sprintf('%04d', rand(9999))}",
85
- nil, @domain, @domain_config.project_id)
86
- end
30
+ sanatize_creation_parameters
87
31
 
32
+ show_creation_summary
88
33
 
89
- # Launch!
90
- @env[:ui].info(I18n.t('vagrant_cloudstack.launching_instance'))
91
- @env[:ui].info(" -- Display Name: #{@domain_config.display_name}")
92
- @env[:ui].info(" -- Group: #{@domain_config.group}") if @domain_config.group
93
- @env[:ui].info(" -- Service offering: #{@service_offering.name} (#{@service_offering.id})")
94
- @env[:ui].info(" -- Disk offering: #{@disk_offering.name} (#{@disk_offering.id})") unless @disk_offering.id.nil?
95
- @env[:ui].info(" -- Template: #{@template.name} (#{@template.id})")
96
- @env[:ui].info(" -- Project UUID: #{@domain_config.project_id}") unless @domain_config.project_id.nil?
97
- @env[:ui].info(" -- Zone: #{@zone.name} (#{@zone.id})")
98
- @networks.each do |network|
99
- @env[:ui].info(" -- Network: #{network.name} (#{network.id})")
100
- end
101
- @env[:ui].info(" -- Keypair: #{@domain_config.keypair}") if @domain_config.keypair
102
- @env[:ui].info(' -- User Data: Yes') if @domain_config.user_data
103
- @security_groups.each do |security_group|
104
- @env[:ui].info(" -- Security Group: #{security_group.name} (#{security_group.id})")
105
- end
106
-
107
- server = create_vm
34
+ create_vm
108
35
 
109
- # Wait for the instance to be ready first
110
- wait_for_instance_ready(server)
36
+ wait_for_instance_ready
111
37
 
112
- store_volumes(server)
38
+ store_volumes
113
39
 
114
- store_password(server)
40
+ store_password
115
41
 
116
42
  configure_networking
117
43
 
@@ -126,16 +52,34 @@ module VagrantPlugins
126
52
  @app.call(@env)
127
53
  end
128
54
 
129
- def store_volumes(server)
130
- volumes = @env[:cloudstack_compute].volumes.find_all { |f| f.server_id == server.id }
131
- # volumes refuses to be iterated directly, do it by index
132
- (0...volumes.length).each do |idx|
133
- unless volumes[idx].type == 'ROOT'
134
- volumes_file = @env[:machine].data_dir.join('volumes')
135
- volumes_file.open('a+') do |f|
136
- f.write("#{volumes[idx].id}\n")
137
- end
138
- end
55
+ def sanatize_creation_parameters
56
+ # Get the domain we're going to booting up in
57
+ @domain = @env[:machine].provider_config.domain_id
58
+ # Get the configs
59
+ @domain_config = @env[:machine].provider_config.get_domain_config(@domain)
60
+
61
+ sanitize_domain_config
62
+
63
+ initialize_parameters
64
+
65
+ if @zone.is_undefined?
66
+ @env[:ui].error("No Zone specified!")
67
+ exit(false)
68
+ end
69
+
70
+ resolve_parameters
71
+
72
+ cs_zone = @env[:cloudstack_compute].zones.find {|f| f.id == @zone.id}
73
+ resolve_network(cs_zone)
74
+
75
+ resolve_security_groups(cs_zone)
76
+
77
+ @domain_config.display_name = generate_display_name if @domain_config.display_name.nil?
78
+
79
+ if @domain_config.keypair.nil? && @domain_config.ssh_key.nil?
80
+ @env[:ui].warn(I18n.t('vagrant_cloudstack.launch_no_keypair_no_sshkey'))
81
+ generate_ssh_keypair("vagacs_#{@domain_config.display_name}_#{sprintf('%04d', rand(9999))}",
82
+ nil, @domain, @domain_config.project_id)
139
83
  end
140
84
  end
141
85
 
@@ -159,129 +103,64 @@ module VagrantPlugins
159
103
  end
160
104
  end
161
105
 
162
- def configure_networking
163
- enable_static_nat_rules
164
-
165
- evaluate_pf_private_port
166
- evaluate_pf_private_rdp_port
167
-
168
-
169
- create_port_forwardings
170
- # First create_port_forwardings,
171
- # as it may generate 'pf_public_port' or 'pf_public_rdp_port',
172
- # which after this may need a firewall rule
173
- configure_firewall
174
- end
175
-
176
- def enable_static_nat_rules
177
- unless @domain_config.static_nat.empty?
178
- @domain_config.static_nat.each do |rule|
179
- enable_static_nat( rule)
180
- end
181
- end
182
- end
183
-
184
- def generate_display_name
185
- local_user = ENV['USER'].dup
186
- local_user.gsub!(/[^-a-z0-9_]/i, '')
187
- prefix = @env[:root_path].basename.to_s
188
- prefix.gsub!(/[^-a-z0-9_]/i, '')
189
-
190
- local_user + '_' + prefix + "_#{Time.now.to_i}"
106
+ def initialize_parameters
107
+ @zone = CloudstackResource.new(@domain_config.zone_id, @domain_config.zone_name, 'zone')
108
+ @networks = CloudstackResource.create_list(@domain_config.network_id, @domain_config.network_name, 'network')
109
+ @service_offering = CloudstackResource.new(@domain_config.service_offering_id, @domain_config.service_offering_name, 'service_offering')
110
+ @disk_offering = CloudstackResource.new(@domain_config.disk_offering_id, @domain_config.disk_offering_name, 'disk_offering')
111
+ @template = CloudstackResource.new(@domain_config.template_id, @domain_config.template_name || @env[:machine].config.vm.box, 'template')
112
+ @pf_ip_address = CloudstackResource.new(@domain_config.pf_ip_address_id, @domain_config.pf_ip_address, 'public_ip_address')
191
113
  end
192
114
 
193
- def wait_for_communicator_ready
194
- @env[:metrics]['instance_ssh_time'] = Util::Timer.time do
195
- # Wait for communicator to be ready.
196
- communicator = @env[:machine].communicate.instance_variable_get('@logger').instance_variable_get('@name')
197
- @env[:ui].info(I18n.t('vagrant_cloudstack.waiting_for_communicator', :communicator => communicator.to_s.upcase))
198
- while true
199
- # If we're interrupted then just back out
200
- break if @env[:interrupted]
201
- break if @env[:machine].communicate.ready?
202
- sleep 2
203
- end
115
+ def resolve_parameters
116
+ begin
117
+ @resource_service.sync_resource(@zone, {available: true})
118
+ @resource_service.sync_resource(@service_offering, {listall: true})
119
+ @resource_service.sync_resource(@disk_offering, {listall: true})
120
+ @resource_service.sync_resource(@template, {zoneid: @zone.id, templatefilter: 'executable', listall: true})
121
+ @resource_service.sync_resource(@pf_ip_address)
122
+ rescue CloudstackResourceNotFound => e
123
+ @env[:ui].error(e.message)
124
+ exit(false)
204
125
  end
205
- @logger.info("Time for SSH ready: #{@env[:metrics]['instance_ssh_time']}")
206
126
  end
207
127
 
208
- def wait_for_instance_ready( server)
209
- @env[:metrics]['instance_ready_time'] = Util::Timer.time do
210
- tries = @domain_config.instance_ready_timeout / 2
211
-
212
- @env[:ui].info(I18n.t('vagrant_cloudstack.waiting_for_ready'))
213
- begin
214
- retryable(:on => Fog::Errors::TimeoutError, :tries => tries) do
215
- # If we're interrupted don't worry about waiting
216
- next if @env[:interrupted]
217
-
218
- # Wait for the server to be ready
219
- server.wait_for(2) { ready? }
220
- end
221
- rescue Fog::Errors::TimeoutError
222
- # Delete the instance
223
- terminate
128
+ def resolve_network(cs_zone)
129
+ if cs_zone.network_type.downcase == 'basic'
130
+ # No network specification in basic zone
131
+ @env[:ui].warn(I18n.t('vagrant_cloudstack.basic_network', :zone_name => @zone.name)) if !@networks.empty? && (@networks[0].id || @networks[0].name)
132
+ @networks = [CloudstackResource.new(nil, nil, 'network')]
224
133
 
225
- # Notify the user
226
- raise Errors::InstanceReadyTimeout,
227
- :timeout => @domain_config.instance_ready_timeout
134
+ # No portforwarding in basic zone, so none of the below
135
+ @domain_config.pf_ip_address = nil
136
+ @domain_config.pf_ip_address_id = nil
137
+ @domain_config.pf_public_port = nil
138
+ @domain_config.pf_public_rdp_port = nil
139
+ @domain_config.pf_public_port_randomrange = nil
140
+ else
141
+ @networks.each do |network|
142
+ @resource_service.sync_resource(network)
228
143
  end
229
144
  end
230
- @logger.info("Time to instance ready: #{@env[:metrics]['instance_ready_time']}")
231
145
  end
232
146
 
233
- def create_vm
234
- server = nil
235
- begin
236
- options = {
237
- :display_name => @domain_config.display_name,
238
- :group => @domain_config.group,
239
- :zone_id => @zone.id,
240
- :flavor_id => @service_offering.id,
241
- :image_id => @template.id
242
- }
243
-
244
- options['network_ids'] = @networks.map(&:id).compact.join(",") unless @networks.empty?
245
- options['security_group_ids'] = @security_groups.map{|security_group| security_group.id}.join(',') unless @security_groups.empty?
246
- options['project_id'] = @domain_config.project_id unless @domain_config.project_id.nil?
247
- options['key_name'] = @domain_config.keypair unless @domain_config.keypair.nil?
248
- options['name'] = @domain_config.name unless @domain_config.name.nil?
249
- options['ip_address'] = @domain_config.private_ip_address unless @domain_config.private_ip_address.nil?
250
- options['disk_offering_id'] = @disk_offering.id unless @disk_offering.id.nil?
251
-
252
- if @domain_config.user_data != nil
253
- options['user_data'] = Base64.urlsafe_encode64(@domain_config.user_data)
254
- if options['user_data'].length > 2048
255
- raise Errors::UserdataError,
256
- :userdataLength => options['user_data'].length
257
- end
258
- end
259
-
260
- server = @env[:cloudstack_compute].servers.create(options)
261
- rescue Fog::Compute::Cloudstack::NotFound => e
262
- # Invalid subnet doesn't have its own error so we catch and
263
- # check the error message here.
264
- # XXX FIXME vpc?
265
- if e.message =~ /subnet ID/
266
- raise Errors::FogError,
267
- :message => "Subnet ID not found: #{@networks.map(&:id).compact.join(",")}"
147
+ def resolve_security_groups(cs_zone)
148
+ if cs_zone.security_groups_enabled
149
+ prepare_security_groups
150
+ else
151
+ if !@domain_config.security_group_ids.empty? || !@domain_config.security_group_names.empty? || !@domain_config.security_groups.empty?
152
+ @env[:ui].warn(I18n.t('vagrant_cloudstack.security_groups_disabled', :zone_name => @zone.name))
268
153
  end
269
-
270
- raise
271
- rescue Fog::Compute::Cloudstack::Error => e
272
- raise Errors::FogError, :message => e.message
154
+ @domain_config.security_group_ids = []
155
+ @domain_config.security_group_names = []
156
+ @domain_config.security_groups = []
273
157
  end
274
-
275
- # Immediately save the ID since it is created at this point.
276
- @env[:machine].id = server.id
277
- server
278
158
  end
279
159
 
280
160
  def prepare_security_groups
281
161
  # Can't use Security Group IDs and Names at the same time
282
162
  # Let's use IDs by default...
283
163
  if @domain_config.security_group_ids.empty? and !@domain_config.security_group_names.empty?
284
- #@domain_config.security_group_ids = @domain_config.security_group_names.map { |name| name_to_id(@env, name, 'security_group') }
285
164
  @security_groups = @domain_config.security_group_names.map do |name|
286
165
  group = CloudstackResource.new(nil, name, 'security_group')
287
166
  @resource_service.sync_resource(group)
@@ -305,267 +184,146 @@ module VagrantPlugins
305
184
  end
306
185
  end
307
186
 
308
- def evaluate_pf_private_port
309
- if @domain_config.pf_private_port.nil?
187
+ def create_security_group(security_group)
188
+ begin
189
+ sgid = @env[:cloudstack_compute].create_security_group(:name => security_group[:name],
190
+ :description => security_group[:description])['createsecuritygroupresponse']['securitygroup']['id']
191
+ security_group_object = CloudstackResource.new(sgid, security_group[:name], 'security_group')
192
+ @env[:ui].info(" -- Security Group #{security_group[:name]} created with ID: #{sgid}")
193
+ rescue Exception => e
194
+ if e.message =~ /already exis/
195
+ security_group_object = CloudstackResource.new(nil, security_group[:name], 'security_group')
196
+ @resource_service.sync_resource(security_group_object)
197
+ @env[:ui].info(" -- Security Group #{security_group_object.name} found with ID: #{security_group_object.id}")
198
+ end
199
+ end
310
200
 
311
- communicator = @env[:machine].communicate.instance_variable_get('@logger').instance_variable_get('@name')
312
- comm_obj = @env[:machine].config.send(communicator)
201
+ # security group is created and we have it's ID
202
+ # so we add the rules... Does it really matter if they already exist ? CLoudstack seems to take care of that!
203
+ security_group[:rules].each do |rule|
204
+ rule_options = {
205
+ :securityGroupId => security_group_object.id,
206
+ :protocol => rule[:protocol],
207
+ :startport => rule[:startport],
208
+ :endport => rule[:endport],
209
+ :cidrlist => rule[:cidrlist]
210
+ }
313
211
 
314
- @domain_config.pf_private_port = comm_obj.port if comm_obj.respond_to?('port')
315
- @domain_config.pf_private_port = comm_obj.guest_port if comm_obj.respond_to?('guest_port')
316
- @domain_config.pf_private_port = comm_obj.default.port if (comm_obj.respond_to?('default') && comm_obj.default.respond_to?('port'))
212
+ # The rule[:type] is either ingress or egress, but the method call looks the same.
213
+ # We build a dynamic method name and then send it off.
214
+ @env[:cloudstack_compute].send("authorize_security_group_#{rule[:type]}".to_sym, rule_options)
215
+ @env[:ui].info(" --- #{rule[:type].capitalize} Rule added: #{rule[:protocol]} from #{rule[:startport]} to #{rule[:endport]} (#{rule[:cidrlist]})")
317
216
  end
318
- end
319
217
 
320
- def evaluate_pf_private_rdp_port
321
- @domain_config.pf_private_rdp_port = @env[:machine].config.vm.rdp.port if (@env[:machine].config.vm.respond_to?(:rdp) && @env[:machine].config.vm.rdp.respond_to?(:port))
218
+ store_security_groups(security_group_object)
322
219
  end
323
220
 
324
- def configure_firewall
221
+ def generate_display_name
222
+ local_user = ENV['USER'] ? ENV['USER'].dup : 'VACS'
223
+ local_user.gsub!(/[^-a-z0-9_]/i, '')
224
+ prefix = @env[:root_path].basename.to_s
225
+ prefix.gsub!(/[^-a-z0-9_]/i, '')
325
226
 
326
- unless @pf_ip_address.is_undefined?
327
- ports = [ Hash[publicport: 'pf_public_port', privateport: 'pf_private_port'] ]
328
- ports << Hash[publicport: 'pf_public_rdp_port', privateport: 'pf_private_rdp_port']
227
+ local_user + '_' + prefix + "_#{Time.now.to_i}"
228
+ end
329
229
 
330
- ports.each do |port_set|
331
- if @pf_ip_address.details.has_key?('vpcid')
332
- forward_portname = port_set[:privateport]
333
- else
334
- forward_portname = port_set[:publicport]
335
- end
336
- check_portname = port_set[:publicport]
337
- # As we take care of implicit/auto port_forward of 'pf_public_port' we do Firewall as well, possibly
338
- if (@domain_config.pf_ip_address_id || @domain_config.pf_ip_address) &&
339
- @domain_config.send(check_portname) &&
340
- @domain_config.pf_trusted_networks &&
341
- !@domain_config.pf_open_firewall
342
- # Allow access to public port from trusted networks only
343
- fw_rule_trusted_networks = {
344
- :ipaddressid => @domain_config.pf_ip_address_id,
345
- :ipaddress => @domain_config.pf_ip_address,
346
- :protocol => 'tcp',
347
- :startport => @domain_config.send(forward_portname),
348
- :endport => @domain_config.send(forward_portname),
349
- :cidrlist => @domain_config.pf_trusted_networks.join(',')
350
- }
351
- @domain_config.firewall_rules = [] unless @domain_config.firewall_rules
352
- @domain_config.firewall_rules << fw_rule_trusted_networks
353
- end
354
- end
230
+ def show_creation_summary
231
+ # Launch!
232
+ @env[:ui].info(I18n.t('vagrant_cloudstack.launching_instance'))
233
+ @env[:ui].info(" -- Display Name: #{@domain_config.display_name}")
234
+ @env[:ui].info(" -- Group: #{@domain_config.group}") if @domain_config.group
235
+ @env[:ui].info(" -- Service offering: #{@service_offering.name} (#{@service_offering.id})")
236
+ @env[:ui].info(" -- Disk offering: #{@disk_offering.name} (#{@disk_offering.id})") unless @disk_offering.id.nil?
237
+ @env[:ui].info(" -- Template: #{@template.name} (#{@template.id})")
238
+ @env[:ui].info(" -- Project UUID: #{@domain_config.project_id}") unless @domain_config.project_id.nil?
239
+ @env[:ui].info(" -- Zone: #{@zone.name} (#{@zone.id})")
240
+ @networks.each do |network|
241
+ @env[:ui].info(" -- Network: #{network.name} (#{network.id})")
355
242
  end
243
+ @env[:ui].info(" -- Keypair: #{@domain_config.keypair}") if @domain_config.keypair
244
+ @env[:ui].info(' -- User Data: Yes') if @domain_config.user_data
245
+ @security_groups.each do |security_group|
246
+ @env[:ui].info(" -- Security Group: #{security_group.name} (#{security_group.id})")
247
+ end
248
+ end
356
249
 
357
- unless @domain_config.firewall_rules.empty?
358
-
359
- # Inspect port_forwarding rules to make firewall rules
360
- if @pf_ip_address.details.has_key?('vpcid')
361
- port_name = :privateport
362
- else
363
- port_name = :publicport
364
- end
365
- unless @domain_config.port_forwarding_rules.empty?
366
- @domain_config.port_forwarding_rules.each do |port_forwarding_rule|
367
- if port_forwarding_rule[:generate_firewall] && @domain_config.pf_trusted_networks && !port_forwarding_rule[:openfirewall]
368
- # Allow access to public port from trusted networks only
369
- fw_rule_trusted_networks = {
370
- :ipaddressid => port_forwarding_rule[:ipaddressid],
371
- :ipaddress => port_forwarding_rule[:ipaddress],
372
- :protocol => port_forwarding_rule[:protocol],
373
- :startport => port_forwarding_rule[port_name],
374
- :endport => port_forwarding_rule[port_name],
375
- :cidrlist => @domain_config.pf_trusted_networks.join(',')
376
- }
377
- @domain_config.firewall_rules = [] unless @domain_config.firewall_rules
378
- @domain_config.firewall_rules << fw_rule_trusted_networks
379
- end
380
- end
381
- end
250
+ def create_vm
251
+ @server = nil
252
+ begin
253
+ options = compose_server_creation_options
382
254
 
383
- # Fill in the blanks for all rules
384
- @domain_config.firewall_rules.each do |firewall_rule|
385
- firewall_rule[:ipaddressid] = @domain_config.pf_ip_address_id if firewall_rule[:ipaddressid].nil?
386
- firewall_rule[:ipaddress] = @domain_config.pf_ip_address if firewall_rule[:ipaddress].nil?
387
- firewall_rule[:cidrlist] = @domain_config.pf_trusted_networks.join(',') if firewall_rule[:cidrlist].nil?
388
- firewall_rule[:protocol] = 'tcp' if firewall_rule[:protocol].nil?
389
- firewall_rule[:startport] = firewall_rule[:endport] if firewall_rule[:startport].nil?
255
+ @server = @env[:cloudstack_compute].servers.create(options)
256
+ rescue Fog::Compute::Cloudstack::NotFound => e
257
+ # Invalid subnet doesn't have its own error so we catch and
258
+ # check the error message here.
259
+ # XXX FIXME vpc?
260
+ if e.message =~ /subnet ID/
261
+ raise Errors::FogError,
262
+ :message => "Subnet ID not found: #{@networks.map(&:id).compact.join(",")}"
390
263
  end
391
264
 
392
- # Apply all rules
393
- @domain_config.firewall_rules.each do |firewall_rule|
394
- create_firewall_rule( firewall_rule )
395
- end
265
+ raise
266
+ rescue Fog::Compute::Cloudstack::Error => e
267
+ raise Errors::FogError, :message => e.message
396
268
  end
269
+
270
+ @env[:machine].id = @server.id
397
271
  end
398
272
 
399
- def create_port_forwardings
400
- unless @pf_ip_address.is_undefined?
401
- guest_windows = false || @env[:machine].config.vm.guest == :windows || @env[:machine].communicate.instance_variable_get('@logger').instance_variable_get('@name') == 'winrm'
402
-
403
- ports = [ Hash[:public_port => 'pf_public_port', :private_port => 'pf_private_port'] ]
404
- ports << Hash[:public_port => 'pf_public_rdp_port', :private_port => 'pf_private_rdp_port'] if guest_windows
405
-
406
- ports.each do |port_set|
407
- # Implicit/automatic Port forward for 'private' port (SSH/WinRM or RDP)
408
- # Also sets 'public_port' port to random port if missing
409
- public_port_name = port_set[:public_port]
410
- private_port_name = port_set[:private_port]
411
- if (@domain_config.pf_ip_address_id || @domain_config.pf_ip_address) && (@domain_config.send(public_port_name) || @domain_config.pf_public_port_randomrange)
412
- port_forwarding_rule = {
413
- :ipaddressid => @domain_config.pf_ip_address_id,
414
- :ipaddress => @domain_config.pf_ip_address,
415
- :protocol => 'tcp',
416
- :publicport => @domain_config.send(public_port_name),
417
- :privateport => @domain_config.send(private_port_name),
418
- :openfirewall => @domain_config.pf_open_firewall
419
- }
273
+ def compose_server_creation_options
274
+ options = {
275
+ :display_name => @domain_config.display_name,
276
+ :group => @domain_config.group,
277
+ :zone_id => @zone.id,
278
+ :flavor_id => @service_offering.id,
279
+ :image_id => @template.id
280
+ }
420
281
 
421
- public_port = create_randomport_forwarding_rule(
422
- port_forwarding_rule,
423
- @domain_config.pf_public_port_randomrange[:start]...@domain_config.pf_public_port_randomrange[:end],
424
- public_port_name
425
- )
426
- @domain_config.send("#{public_port_name}=", public_port)
427
- end
428
- end
282
+ unless @networks.empty?
283
+ nets = @networks.map(&:id).compact.join(",")
284
+ options['network_ids'] = nets unless nets.empty?
429
285
  end
286
+ options['security_group_ids'] = @security_groups.map {|security_group| security_group.id}.join(',') unless @security_groups.empty?
287
+ options['project_id'] = @domain_config.project_id unless @domain_config.project_id.nil?
288
+ options['key_name'] = @domain_config.keypair unless @domain_config.keypair.nil?
289
+ options['name'] = @domain_config.name unless @domain_config.name.nil?
290
+ options['ip_address'] = @domain_config.private_ip_address unless @domain_config.private_ip_address.nil?
291
+ options['disk_offering_id'] = @disk_offering.id unless @disk_offering.id.nil?
430
292
 
431
- unless @domain_config.port_forwarding_rules.empty?
432
- @domain_config.port_forwarding_rules.each do |port_forwarding_rule|
433
- port_forwarding_rule[:ipaddressid] = @domain_config.pf_ip_address_id if port_forwarding_rule[:ipaddressid].nil?
434
- port_forwarding_rule[:ipaddress] = @domain_config.pf_ip_address if port_forwarding_rule[:ipaddress].nil?
435
- port_forwarding_rule[:protocol] = 'tcp' if port_forwarding_rule[:protocol].nil?
436
- port_forwarding_rule[:openfirewall] = @domain_config.pf_open_firewall if port_forwarding_rule[:openfirewall].nil?
437
- port_forwarding_rule[:publicport] = port_forwarding_rule[:privateport] if port_forwarding_rule[:publicport].nil?
438
- port_forwarding_rule[:privateport] = port_forwarding_rule[:publicport] if port_forwarding_rule[:privateport].nil?
439
-
440
- create_port_forwarding_rule(port_forwarding_rule)
293
+ if @domain_config.user_data != nil
294
+ options['user_data'] = Base64.urlsafe_encode64(@domain_config.user_data)
295
+ if options['user_data'].length > 2048
296
+ raise Errors::UserdataError,
297
+ :userdataLength => options['user_data'].length
441
298
  end
442
299
  end
300
+ options
443
301
  end
444
302
 
445
- def create_randomport_forwarding_rule(rule, randomrange, filename)
446
- # Only if pf_public_port is nil, will generate and try
447
- # Otherwise, functionaly the same as just create_port_forwarding_rule
448
- pf_public_port = rule[:publicport]
449
- retryable(:on => DuplicatePFRule, :tries => 10) do
450
- begin
451
- rule[:publicport] = rand(randomrange) if pf_public_port.nil?
303
+ def configure_networking
304
+ begin
305
+ enable_static_nat_rules
452
306
 
453
- create_port_forwarding_rule(rule)
454
-
455
- if pf_public_port.nil?
456
- pf_port_file = @env[:machine].data_dir.join(filename)
457
- pf_port_file.open('a+') do |f|
458
- f.write("#{rule[:publicport]}")
459
- end
460
- end
461
- rescue Errors::FogError => e
462
- if pf_public_port.nil? && !(e.message =~ /The range specified,.*conflicts with rule.*which has/).nil?
463
- raise DuplicatePFRule, :message => e.message
464
- else
465
- raise Errors::FogError, :message => e.message
466
- end
467
- end
468
- end
469
- pf_public_port.nil? ? (rule[:publicport]) : (pf_public_port)
470
- end
471
-
472
- def store_ssh_keypair(keyname, account = nil, domainid = nil, projectid = nil)
473
- response = @env[:cloudstack_compute].create_ssh_key_pair(keyname, account, domainid, projectid)
474
- sshkeypair = response['createsshkeypairresponse']['keypair']
307
+ configure_port_forwarding
475
308
 
476
- # Save private key to file
477
- sshkeyfile_file = @env[:machine].data_dir.join('sshkeyfile')
478
- sshkeyfile_file.open('w') do |f|
479
- f.write("#{sshkeypair['privatekey']}")
480
- end
481
- @domain_config.ssh_key = sshkeyfile_file.to_s
309
+ # First create_port_forwardings,
310
+ # as it may generate 'pf_public_port' or 'pf_public_rdp_port',
311
+ # which after this may need a firewall rule
312
+ configure_firewall
482
313
 
483
- # Save keyname to file for terminate_instance
484
- sshkeyname_file = @env[:machine].data_dir.join('sshkeyname')
485
- sshkeyname_file.open('w') do |f|
486
- f.write("#{sshkeypair['name']}")
487
- end
488
-
489
- @domain_config.keypair = sshkeypair['name']
490
- end
491
-
492
- def store_password(server)
493
- password = nil
494
- if server.password_enabled and server.respond_to?('job_id')
495
- server_job_result = @env[:cloudstack_compute].query_async_job_result({:jobid => server.job_id})
496
- if server_job_result.nil?
497
- @env[:ui].warn(' -- Failed to retrieve job_result for retrieving the password')
498
- return
499
- end
500
-
501
- while true
502
- server_job_result = @env[:cloudstack_compute].query_async_job_result({:jobid => server.job_id})
503
- if server_job_result['queryasyncjobresultresponse']['jobstatus'] != 0
504
- password = server_job_result['queryasyncjobresultresponse']['jobresult']['virtualmachine']['password']
505
- break
506
- else
507
- sleep 2
508
- end
509
- end
510
-
511
- @env[:ui].info("Password of virtualmachine: #{password}")
512
- # Set the password on the current communicator
513
- @domain_config.vm_password = password
514
-
515
- # Save password to file
516
- vmcredentials_file = @env[:machine].data_dir.join('vmcredentials')
517
- vmcredentials_file.open('w') do |f|
518
- f.write("#{password}")
519
- end
314
+ rescue CloudstackResourceNotFound => e
315
+ @env[:ui].error(e.message)
316
+ terminate
317
+ exit(false)
520
318
  end
521
319
  end
522
320
 
523
- def create_security_group(security_group)
524
- begin
525
- sgid = @env[:cloudstack_compute].create_security_group(:name => security_group[:name],
526
- :description => security_group[:description])['createsecuritygroupresponse']['securitygroup']['id']
527
- security_group_object = CloudstackResource.new(sgid, security_group[:name], 'security_group')
528
- @env[:ui].info(" -- Security Group #{security_group[:name]} created with ID: #{sgid}")
529
- rescue Exception => e
530
- if e.message =~ /already exis/
531
- security_group_object = CloudstackResource.new(nil, security_group[:name], 'security_group')
532
- @resource_service.sync_resource(security_group_object)
533
- @env[:ui].info(" -- Security Group #{security_group_object.name} found with ID: #{security_group_object.id}")
321
+ def enable_static_nat_rules
322
+ unless @domain_config.static_nat.empty?
323
+ @domain_config.static_nat.each do |rule|
324
+ enable_static_nat(rule)
534
325
  end
535
326
  end
536
-
537
- # security group is created and we have it's ID
538
- # so we add the rules... Does it really matter if they already exist ? CLoudstack seems to take care of that!
539
- security_group[:rules].each do |rule|
540
- rule_options = {
541
- :securityGroupId => security_group_object.id,
542
- :protocol => rule[:protocol],
543
- :startport => rule[:startport],
544
- :endport => rule[:endport],
545
- :cidrlist => rule[:cidrlist]
546
- }
547
-
548
- # The rule[:type] is either ingress or egress, but the method call looks the same.
549
- # We build a dynamic method name and then send it off.
550
- @env[:cloudstack_compute].send("authorize_security_group_#{rule[:type]}".to_sym, rule_options)
551
- @env[:ui].info(" --- #{rule[:type].capitalize} Rule added: #{rule[:protocol]} from #{rule[:startport]} to #{rule[:endport]} (#{rule[:cidrlist]})")
552
- end
553
-
554
- # and record the security group ids for future deletion (of rules and groups if possible)
555
- security_groups_file = @env[:machine].data_dir.join('security_groups')
556
- security_groups_file.open('a+') do |f|
557
- f.write("#{security_group_object.id}\n")
558
- end
559
- security_group_object
560
- end
561
-
562
- def recover(env)
563
- return if env['vagrant.error'].is_a?(Vagrant::Errors::VagrantError)
564
-
565
- if env[:machine].provider.state.id != :not_created
566
- # Undo the import
567
- terminate
568
- end
569
327
  end
570
328
 
571
329
  def enable_static_nat(rule)
@@ -604,40 +362,118 @@ module VagrantPlugins
604
362
  end
605
363
  end
606
364
 
607
- def create_port_forwarding_rule(rule)
608
- port_forwarding_rule = nil
609
- @env[:ui].info(I18n.t('vagrant_cloudstack.creating_port_forwarding_rule'))
365
+ def configure_port_forwarding
610
366
 
611
- begin
612
- ip_address = sync_ip_address(rule[:ipaddressid], rule[:ipaddress])
613
- rescue IpNotFoundException
614
- return
367
+ unless @pf_ip_address.is_undefined?
368
+ evaluate_pf_private_port
369
+ evaluate_pf_private_rdp_port
370
+ generate_and_apply_port_forwarding_rules_for_communicators
615
371
  end
616
372
 
617
- @env[:ui].info(" -- IP address : #{ip_address.name} (#{ip_address.id})")
618
- @env[:ui].info(" -- Protocol : #{rule[:protocol]}")
619
- @env[:ui].info(" -- Public port : #{rule[:publicport]}")
620
- @env[:ui].info(" -- Private port : #{rule[:privateport]}")
621
- @env[:ui].info(" -- Open Firewall : #{rule[:openfirewall]}")
373
+ apply_port_forwarding_rules
374
+ end
622
375
 
623
- if ip_address.details.has_key?('associatednetworkid')
624
- network = @networks.find{ |f| f.id == ip_address.details['associatednetworkid']}
625
- elsif ip_address.details.has_key?('vpcid')
626
- # In case of VPC and ip has not yet been used, a network MUST be specified
627
- network = @networks.find{ |f| f.details['vpcid'] == ip_address.details['vpcid']}
376
+ def get_communicator_short_name(communicator)
377
+ communicator_short_names = {
378
+ 'VagrantPlugins::CommunicatorSSH::Communicator' => 'ssh',
379
+ 'VagrantPlugins::CommunicatorWinRM::Communicator' => 'winrm'
380
+ }
381
+ communicator_short_names[communicator.class.name]
382
+ end
383
+
384
+ def evaluate_pf_private_port
385
+ return unless @domain_config.pf_private_port.nil?
386
+
387
+ communicator_short_name = get_communicator_short_name(@env[:machine].communicate)
388
+ communicator_config = @env[:machine].config.send(communicator_short_name)
389
+
390
+ @domain_config.pf_private_port = communicator_config.port if communicator_config.respond_to?('port')
391
+ @domain_config.pf_private_port = communicator_config.guest_port if communicator_config.respond_to?('guest_port')
392
+ @domain_config.pf_private_port = communicator_config.default.port if (communicator_config.respond_to?('default') && communicator_config.default.respond_to?('port'))
393
+ end
394
+
395
+ def evaluate_pf_private_rdp_port
396
+ @domain_config.pf_private_rdp_port = @env[:machine].config.vm.rdp.port if
397
+ @env[:machine].config.vm.respond_to?(:rdp) &&
398
+ @env[:machine].config.vm.rdp.respond_to?(:port)
399
+ end
400
+
401
+ def generate_and_apply_port_forwarding_rules_for_communicators
402
+ communicators_port_names = [Hash[:public_port => 'pf_public_port', :private_port => 'pf_private_port']]
403
+ communicators_port_names << Hash[:public_port => 'pf_public_rdp_port', :private_port => 'pf_private_rdp_port'] if is_windows_guest
404
+
405
+ communicators_port_names.each do |communicator_port_names|
406
+ public_port_name = communicator_port_names[:public_port]
407
+ private_port_name = communicator_port_names[:private_port]
408
+
409
+ next unless @domain_config.send(public_port_name) || @domain_config.pf_public_port_randomrange
410
+
411
+ port_forwarding_rule = {
412
+ :ipaddressid => @domain_config.pf_ip_address_id,
413
+ :ipaddress => @domain_config.pf_ip_address,
414
+ :protocol => 'tcp',
415
+ :publicport => @domain_config.send(public_port_name),
416
+ :privateport => @domain_config.send(private_port_name),
417
+ :openfirewall => @domain_config.pf_open_firewall
418
+ }
419
+
420
+ public_port = create_randomport_forwarding_rule(
421
+ port_forwarding_rule,
422
+ @domain_config.pf_public_port_randomrange[:start]...@domain_config.pf_public_port_randomrange[:end],
423
+ public_port_name
424
+ )
425
+ @domain_config.send("#{public_port_name}=", public_port)
628
426
  end
427
+ end
629
428
 
630
- options = {
631
- :networkid => network.id,
632
- :ipaddressid => ip_address.id,
633
- :publicport => rule[:publicport],
634
- :privateport => rule[:privateport],
635
- :protocol => rule[:protocol],
636
- :openfirewall => rule[:openfirewall],
637
- :virtualmachineid => @env[:machine].id
638
- }
429
+ def create_randomport_forwarding_rule(rule, randomrange, filename)
430
+ pf_public_port = rule[:publicport]
431
+ retryable(:on => DuplicatePFRule, :tries => 10) do
432
+ begin
433
+ # Only if public port is missing, will generate a random one
434
+ rule[:publicport] = Kernel.rand(randomrange) if pf_public_port.nil?
435
+
436
+ apply_port_forwarding_rule(rule)
437
+
438
+ if pf_public_port.nil?
439
+ pf_port_file = @env[:machine].data_dir.join(filename)
440
+ pf_port_file.open('a+') do |f|
441
+ f.write("#{rule[:publicport]}")
442
+ end
443
+ end
444
+ rescue Errors::FogError => e
445
+ if pf_public_port.nil? && !(e.message =~ /The range specified,.*conflicts with rule.*which has/).nil?
446
+ raise DuplicatePFRule, :message => e.message
447
+ else
448
+ raise Errors::FogError, :message => e.message
449
+ end
450
+ end
451
+ end
452
+ pf_public_port.nil? ? (rule[:publicport]) : (pf_public_port)
453
+ end
454
+
455
+ def apply_port_forwarding_rules
456
+ return if @domain_config.port_forwarding_rules.empty?
457
+
458
+ @domain_config.port_forwarding_rules.each do |port_forwarding_rule|
459
+ # Sanatize/defaults pf rule before applying
460
+ port_forwarding_rule[:ipaddressid] = @domain_config.pf_ip_address_id if port_forwarding_rule[:ipaddressid].nil?
461
+ port_forwarding_rule[:ipaddress] = @domain_config.pf_ip_address if port_forwarding_rule[:ipaddress].nil?
462
+ port_forwarding_rule[:protocol] = 'tcp' if port_forwarding_rule[:protocol].nil?
463
+ port_forwarding_rule[:openfirewall] = @domain_config.pf_open_firewall if port_forwarding_rule[:openfirewall].nil?
464
+ port_forwarding_rule[:publicport] = port_forwarding_rule[:privateport] if port_forwarding_rule[:publicport].nil?
465
+ port_forwarding_rule[:privateport] = port_forwarding_rule[:publicport] if port_forwarding_rule[:privateport].nil?
466
+
467
+ apply_port_forwarding_rule(port_forwarding_rule)
468
+ end
469
+ end
470
+
471
+ def apply_port_forwarding_rule(rule)
472
+ port_forwarding_rule = nil
473
+ @env[:ui].info(I18n.t('vagrant_cloudstack.creating_port_forwarding_rule'))
474
+
475
+ return unless (options = compose_port_forwarding_rule_creation_options(rule))
639
476
 
640
- options.delete(:openfirewall) if network.details.has_key?('vpcid')
641
477
  begin
642
478
  resp = @env[:cloudstack_compute].create_port_forwarding_rule(options)
643
479
  job_id = resp['createportforwardingruleresponse']['jobid']
@@ -660,10 +496,124 @@ module VagrantPlugins
660
496
  raise Errors::FogError, :message => e.message
661
497
  end
662
498
 
663
- # Save port forwarding rule id to the data dir so it can be released when the instance is destroyed
664
- port_forwarding_file = @env[:machine].data_dir.join('port_forwarding')
665
- port_forwarding_file.open('a+') do |f|
666
- f.write("#{port_forwarding_rule['id']}\n")
499
+ store_port_forwarding_rules(port_forwarding_rule)
500
+ end
501
+
502
+ def compose_port_forwarding_rule_creation_options(rule)
503
+ begin
504
+ ip_address = sync_ip_address(rule[:ipaddressid], rule[:ipaddress])
505
+ rescue IpNotFoundException
506
+ return
507
+ end
508
+
509
+ @env[:ui].info(" -- IP address : #{ip_address.name} (#{ip_address.id})")
510
+ @env[:ui].info(" -- Protocol : #{rule[:protocol]}")
511
+ @env[:ui].info(" -- Public port : #{rule[:publicport]}")
512
+ @env[:ui].info(" -- Private port : #{rule[:privateport]}")
513
+ @env[:ui].info(" -- Open Firewall : #{rule[:openfirewall]}")
514
+
515
+ network = get_network_from_public_ip(ip_address)
516
+
517
+ options = {
518
+ :networkid => network.id,
519
+ :ipaddressid => ip_address.id,
520
+ :publicport => rule[:publicport],
521
+ :privateport => rule[:privateport],
522
+ :protocol => rule[:protocol],
523
+ :openfirewall => rule[:openfirewall],
524
+ :virtualmachineid => @env[:machine].id
525
+ }
526
+
527
+ options.delete(:openfirewall) if network.details.has_key?('vpcid')
528
+ options
529
+ end
530
+
531
+ def get_network_from_public_ip(ip_address)
532
+ if ip_address.details.has_key?('associatednetworkid')
533
+ network = @networks.find {|f| f.id == ip_address.details['associatednetworkid']}
534
+ elsif ip_address.details.has_key?('vpcid')
535
+ # In case of VPC and ip has not yet been used, a network MUST be specified
536
+ network = @networks.find {|f| f.details['vpcid'] == ip_address.details['vpcid']}
537
+ end
538
+ network
539
+ end
540
+
541
+ def configure_firewall
542
+ if @domain_config.pf_trusted_networks
543
+ generate_firewall_rules_for_communicators unless @pf_ip_address.is_undefined?
544
+ generate_firewall_rules_for_portforwarding_rules
545
+ end
546
+
547
+ return if @domain_config.firewall_rules.empty?
548
+ auto_complete_firewall_rules
549
+ apply_firewall_rules
550
+ end
551
+
552
+ def generate_firewall_rules_for_communicators
553
+
554
+ return if @pf_ip_address.is_undefined? ||
555
+ !@domain_config.pf_trusted_networks ||
556
+ @domain_config.pf_open_firewall
557
+
558
+ ports = [Hash[publicport: 'pf_public_port', privateport: 'pf_private_port']]
559
+ ports << Hash[publicport: 'pf_public_rdp_port', privateport: 'pf_private_rdp_port'] if is_windows_guest
560
+
561
+ ports.each do |port_set|
562
+ forward_portname = @pf_ip_address.details.key?('vpcid') ? port_set[:privateport] : port_set[:publicport]
563
+ check_portname = port_set[:publicport]
564
+
565
+ next unless @domain_config.send(check_portname)
566
+
567
+ # Allow access to public port from trusted networks only
568
+ fw_rule_trusted_networks = {
569
+ ipaddressid: @pf_ip_address.id,
570
+ ipaddress: @pf_ip_address.name,
571
+ protocol: 'tcp',
572
+ startport: @domain_config.send(forward_portname),
573
+ endport: @domain_config.send(forward_portname),
574
+ cidrlist: @domain_config.pf_trusted_networks.join(',')
575
+ }
576
+ @domain_config.firewall_rules = [] unless @domain_config.firewall_rules
577
+ @domain_config.firewall_rules << fw_rule_trusted_networks
578
+
579
+ end
580
+ end
581
+
582
+ def generate_firewall_rules_for_portforwarding_rules
583
+ @pf_ip_address.details.has_key?('vpcid') ? port_name = :privateport : port_name = :publicport
584
+
585
+ unless @domain_config.port_forwarding_rules.empty?
586
+ @domain_config.port_forwarding_rules.each do |port_forwarding_rule|
587
+ if port_forwarding_rule[:generate_firewall] && @domain_config.pf_trusted_networks && !port_forwarding_rule[:openfirewall]
588
+ # Allow access to public port from trusted networks only
589
+ fw_rule_trusted_networks = {
590
+ :ipaddressid => port_forwarding_rule[:ipaddressid],
591
+ :ipaddress => port_forwarding_rule[:ipaddress],
592
+ :protocol => port_forwarding_rule[:protocol],
593
+ :startport => port_forwarding_rule[port_name],
594
+ :endport => port_forwarding_rule[port_name],
595
+ :cidrlist => @domain_config.pf_trusted_networks.join(',')
596
+ }
597
+ @domain_config.firewall_rules = [] unless @domain_config.firewall_rules
598
+ @domain_config.firewall_rules << fw_rule_trusted_networks
599
+ end
600
+ end
601
+ end
602
+ end
603
+
604
+ def auto_complete_firewall_rules
605
+ @domain_config.firewall_rules.each do |firewall_rule|
606
+ firewall_rule[:ipaddressid] = @domain_config.pf_ip_address_id if firewall_rule[:ipaddressid].nil?
607
+ firewall_rule[:ipaddress] = @domain_config.pf_ip_address if firewall_rule[:ipaddress].nil?
608
+ firewall_rule[:cidrlist] = @domain_config.pf_trusted_networks.join(',') if firewall_rule[:cidrlist].nil?
609
+ firewall_rule[:protocol] = 'tcp' if firewall_rule[:protocol].nil?
610
+ firewall_rule[:startport] = firewall_rule[:endport] if firewall_rule[:startport].nil?
611
+ end
612
+ end
613
+
614
+ def apply_firewall_rules
615
+ @domain_config.firewall_rules.each do |firewall_rule|
616
+ create_firewall_rule(firewall_rule)
667
617
  end
668
618
  end
669
619
 
@@ -685,52 +635,74 @@ module VagrantPlugins
685
635
 
686
636
  if ip_address.details.has_key?('vpcid')
687
637
  network = @networks.find{ |f| f.id == ip_address.details['associatednetworkid']}
688
- resp = @env[:cloudstack_compute].list_network_acl_lists({
689
- id: network.details['aclid']
690
- })
638
+ acl_id = network.details['aclid']
639
+
640
+ raise CloudstackResourceNotFound.new("No ACL found associated with VPC tier #{network.details['name']} (id: #{network.details['id']})") unless acl_id
641
+
642
+ resp = @env[:cloudstack_compute].list_network_acl_lists(
643
+ id: network.details[acl_id]
644
+ )
691
645
  acl_name = resp['listnetworkacllistsresponse']['networkacllist'][0]['name']
692
646
 
693
- resp = @env[:cloudstack_compute].list_network_acls({
694
- aclid: network.details['aclid']
695
- })
696
- number = 0
697
- if resp["listnetworkaclsresponse"].key?("networkacl")
698
- resp["listnetworkaclsresponse"]["networkacl"].each{ |ace| number = [number, ace["number"]].max }
699
- end
700
- number = number+1
701
-
702
- command_string = 'createNetworkACL'
703
- response_string = 'createnetworkaclresponse'
704
- type_string = 'networkacl'
705
- options = {
706
- :command => command_string,
707
- :aclid => network.details['aclid'],
708
- :action => 'Allow',
709
- :protocol => rule[:protocol],
710
- :cidrlist => rule[:cidrlist],
711
- :startport => rule[:startport],
712
- :endport => rule[:endport],
713
- :icmpcode => rule[:icmpcode],
714
- :icmptype => rule[:icmptype],
715
- :number => number,
716
- :traffictype => 'Ingress'
717
- }
647
+ options, response_string, type_string = compose_firewall_rule_creation_options_vpc(network, rule)
718
648
  else
719
- command_string = 'createFirewallRule'
720
- response_string = 'createfirewallruleresponse'
721
- type_string = 'firewallrule'
722
- options = {
723
- :command => command_string,
724
- :ipaddressid => ip_address.id,
725
- :protocol => rule[:protocol],
726
- :cidrlist => rule[:cidrlist],
727
- :startport => rule[:startport],
728
- :endeport => rule[:endport],
729
- :icmpcode => rule[:icmpcode],
730
- :icmptype => rule[:icmptype]
731
- }
649
+ options, response_string, type_string = compose_firewall_rule_creation_options_non_vpc(ip_address, rule)
732
650
  end
733
651
 
652
+ firewall_rule = apply_firewall_rule(acl_name, options, response_string, type_string)
653
+
654
+ store_firewall_rule(firewall_rule, type_string) if firewall_rule
655
+ end
656
+
657
+ def compose_firewall_rule_creation_options_vpc(network, rule)
658
+ command_string = 'createNetworkACL'
659
+ response_string = 'createnetworkaclresponse'
660
+ type_string = 'networkacl'
661
+ options = {
662
+ :command => command_string,
663
+ :aclid => network.details['aclid'],
664
+ :action => 'Allow',
665
+ :protocol => rule[:protocol],
666
+ :cidrlist => rule[:cidrlist],
667
+ :startport => rule[:startport],
668
+ :endport => rule[:endport],
669
+ :icmpcode => rule[:icmpcode],
670
+ :icmptype => rule[:icmptype],
671
+ :traffictype => 'Ingress'
672
+ }
673
+ return options, response_string, type_string
674
+ end
675
+
676
+ def compose_firewall_rule_creation_options_non_vpc(ip_address, rule)
677
+ command_string = 'createFirewallRule'
678
+ response_string = 'createfirewallruleresponse'
679
+ type_string = 'firewallrule'
680
+ options = {
681
+ :command => command_string,
682
+ :ipaddressid => ip_address.id,
683
+ :protocol => rule[:protocol],
684
+ :cidrlist => rule[:cidrlist],
685
+ :startport => rule[:startport],
686
+ :endeport => rule[:endport],
687
+ :icmpcode => rule[:icmpcode],
688
+ :icmptype => rule[:icmptype]
689
+ }
690
+ return options, response_string, type_string
691
+ end
692
+
693
+ def get_next_free_acl_entry(network)
694
+ resp = @env[:cloudstack_compute].list_network_acls(
695
+ aclid: network.details['aclid']
696
+ )
697
+ number = 0
698
+ if resp["listnetworkaclsresponse"].key?("networkacl")
699
+ resp["listnetworkaclsresponse"]["networkacl"].each {|ace| number = [number, ace["number"]].max}
700
+ end
701
+ number = number+1
702
+ end
703
+
704
+ def apply_firewall_rule(acl_name, options, response_string, type_string)
705
+ firewall_rule = nil
734
706
  begin
735
707
  resp = @env[:cloudstack_compute].request(options)
736
708
  job_id = resp[response_string]['jobid']
@@ -758,7 +730,77 @@ module VagrantPlugins
758
730
  raise Errors::FogError, :message => e.message
759
731
  end
760
732
  end
733
+ firewall_rule
734
+ end
735
+
736
+ def is_windows_guest
737
+ false || @env[:machine].config.vm.guest == :windows || get_communicator_short_name(@env[:machine].communicate) == 'winrm'
738
+ end
739
+
740
+ def generate_ssh_keypair(keyname, account = nil, domainid = nil, projectid = nil)
741
+ response = @env[:cloudstack_compute].create_ssh_key_pair(keyname, account, domainid, projectid)
742
+ sshkeypair = response['createsshkeypairresponse']['keypair']
743
+
744
+ # Save private key to file
745
+ sshkeyfile_file = @env[:machine].data_dir.join('sshkeyfile')
746
+ sshkeyfile_file.open('w') do |f|
747
+ f.write("#{sshkeypair['privatekey']}")
748
+ end
749
+ @domain_config.ssh_key = sshkeyfile_file.to_s
750
+
751
+ sshkeyname_file = @env[:machine].data_dir.join('sshkeyname')
752
+ sshkeyname_file.open('w') do |f|
753
+ f.write("#{sshkeypair['name']}")
754
+ end
755
+
756
+ @domain_config.keypair = sshkeypair['name']
757
+ end
758
+
759
+ def store_password
760
+ password = nil
761
+ if @server.password_enabled and @server.respond_to?('job_id')
762
+ server_job_result = @env[:cloudstack_compute].query_async_job_result({:jobid => @server.job_id})
763
+ if server_job_result.nil?
764
+ @env[:ui].warn(' -- Failed to retrieve job_result for retrieving the password')
765
+ return
766
+ end
767
+
768
+ while true
769
+ server_job_result = @env[:cloudstack_compute].query_async_job_result({:jobid => @server.job_id})
770
+ if server_job_result['queryasyncjobresultresponse']['jobstatus'] != 0
771
+ password = server_job_result['queryasyncjobresultresponse']['jobresult']['virtualmachine']['password']
772
+ break
773
+ else
774
+ sleep 2
775
+ end
776
+ end
777
+
778
+ @env[:ui].info("Password of virtualmachine: #{password}")
779
+ # Set the password on the current communicator
780
+ @domain_config.vm_password = password
781
+
782
+ # Save password to file
783
+ vmcredentials_file = @env[:machine].data_dir.join('vmcredentials')
784
+ vmcredentials_file.open('w') do |f|
785
+ f.write("#{password}")
786
+ end
787
+ end
788
+ end
789
+
790
+ def store_volumes
791
+ volumes = @env[:cloudstack_compute].volumes.find_all { |f| f.server_id == @server.id }
792
+ # volumes refuses to be iterated directly, do it by index
793
+ (0...volumes.length).each do |idx|
794
+ unless volumes[idx].type == 'ROOT'
795
+ volumes_file = @env[:machine].data_dir.join('volumes')
796
+ volumes_file.open('a+') do |f|
797
+ f.write("#{volumes[idx].id}\n")
798
+ end
799
+ end
800
+ end
801
+ end
761
802
 
803
+ def store_firewall_rule(firewall_rule, type_string)
762
804
  unless firewall_rule.nil?
763
805
  # Save firewall rule id to the data dir so it can be released when the instance is destroyed
764
806
  firewall_file = @env[:machine].data_dir.join('firewall')
@@ -768,6 +810,73 @@ module VagrantPlugins
768
810
  end
769
811
  end
770
812
 
813
+ def store_port_forwarding_rules(port_forwarding_rule)
814
+ port_forwarding_file = @env[:machine].data_dir.join('port_forwarding')
815
+ port_forwarding_file.open('a+') do |f|
816
+ f.write("#{port_forwarding_rule['id']}\n")
817
+ end
818
+ end
819
+
820
+ def store_security_groups(security_group_object)
821
+ security_groups_file = @env[:machine].data_dir.join('security_groups')
822
+ security_groups_file.open('a+') do |f|
823
+ f.write("#{security_group_object.id}\n")
824
+ end
825
+ security_group_object
826
+ end
827
+
828
+ def wait_for_communicator_ready
829
+ @env[:metrics]['instance_ssh_time'] = Util::Timer.time do
830
+ # Wait for communicator to be ready.
831
+ communicator_short_name = get_communicator_short_name(@env[:machine].communicate)
832
+ @env[:ui].info(
833
+ I18n.t('vagrant_cloudstack.waiting_for_communicator',
834
+ communicator: communicator_short_name.to_s.upcase)
835
+ )
836
+ while true
837
+ # If we're interrupted then just back out
838
+ break if @env[:interrupted]
839
+ break if @env[:machine].communicate.ready?
840
+ sleep 2
841
+ end
842
+ end
843
+ @logger.info("Time for SSH ready: #{@env[:metrics]['instance_ssh_time']}")
844
+ end
845
+
846
+ def wait_for_instance_ready
847
+ @env[:metrics]['instance_ready_time'] = Util::Timer.time do
848
+ tries = @domain_config.instance_ready_timeout / 2
849
+
850
+ @env[:ui].info(I18n.t('vagrant_cloudstack.waiting_for_ready'))
851
+ begin
852
+ retryable(:on => Fog::Errors::TimeoutError, :tries => tries) do
853
+ # If we're interrupted don't worry about waiting
854
+ next if @env[:interrupted]
855
+
856
+ # Wait for the server to be ready
857
+ @server.wait_for(2) { ready? }
858
+ end
859
+ rescue Fog::Errors::TimeoutError
860
+ # Delete the instance
861
+ terminate
862
+
863
+ # Notify the user
864
+ raise Errors::InstanceReadyTimeout,
865
+ :timeout => @domain_config.instance_ready_timeout
866
+ end
867
+ end
868
+ @logger.info("Time to instance ready: #{@env[:metrics]['instance_ready_time']}")
869
+ end
870
+
871
+ def recover(env)
872
+ return if env['vagrant.error'].is_a?(Vagrant::Errors::VagrantError)
873
+
874
+ if env[:machine].provider.state.id != :not_created
875
+ # Undo the import
876
+ terminate
877
+ end
878
+ end
879
+
771
880
  def terminate
772
881
  destroy_env = @env.dup
773
882
  destroy_env.delete(:interrupted)