vagrant-cloudstack 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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)