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.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/.travis.yml +13 -9
- data/CHANGELOG.md +30 -0
- data/Docker/Dockerfile +5 -8
- data/Docker/Dockerfile.chefdk_0_17 +2 -2
- data/Docker/Dockerfile.latest_dependencies +2 -2
- data/Docker/README.md +63 -35
- data/Gemfile +2 -2
- data/Gemfile.lock +307 -0
- data/README.md +3 -3
- data/Rakefile +2 -2
- data/build_rpm.sh +1 -1
- data/functional-tests/rsync/Vagrantfile.advanced_networking +1 -0
- data/functional-tests/vmlifecycle/Vagrantfile.advanced_networking +5 -7
- data/functional-tests/vmlifecycle/vmlifecycle_spec.rb +14 -2
- data/lib/vagrant-cloudstack/action/read_rdp_info.rb +9 -43
- data/lib/vagrant-cloudstack/action/read_ssh_info.rb +10 -44
- data/lib/vagrant-cloudstack/action/read_transport_info.rb +59 -0
- data/lib/vagrant-cloudstack/action/read_winrm_info.rb +10 -44
- data/lib/vagrant-cloudstack/action/run_instance.rb +607 -498
- data/lib/vagrant-cloudstack/action/terminate_instance.rb +17 -41
- data/lib/vagrant-cloudstack/config.rb +41 -166
- data/lib/vagrant-cloudstack/exceptions/exceptions.rb +7 -2
- data/lib/vagrant-cloudstack/service/cloudstack_resource_service.rb +17 -5
- data/lib/vagrant-cloudstack/version.rb +1 -1
- data/spec/spec_helper.rb +45 -0
- data/spec/vagrant-cloudstack/action/retrieve_public_ip_port_spec.rb +94 -0
- data/spec/vagrant-cloudstack/action/run_instance_spec.rb +609 -0
- data/spec/vagrant-cloudstack/action/terminate_instance_spec.rb +248 -0
- data/spec/vagrant-cloudstack/config_spec.rb +7 -7
- data/vagrant-cloudstack.gemspec +2 -1
- metadata +22 -10
- data/bootstrap.key +0 -27
data/README.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# Vagrant Cloudstack Provider
|
2
2
|
|
3
|
-
[](https://travis-ci.org/
|
3
|
+
[](https://travis-ci.org/MissionCriticalCloud/vagrant-cloudstack)
|
4
4
|
[](http://badge.fury.io/rb/vagrant-cloudstack)
|
5
|
-
[](https://codeclimate.com/github/
|
5
|
+
[](https://codeclimate.com/github/MissionCriticalCloud/vagrant-cloudstack)
|
6
6
|
[](https://coveralls.io/github/MissionCriticalCloud/vagrant-cloudstack?branch=master)
|
7
|
-
[](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.
|
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['
|
61
|
-
|
62
|
-
|
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.
|
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
|
-
|
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
|
-
|
32
|
+
transport_info = {
|
67
33
|
:host => pf_ip_address || server.nics[0]['ipaddress'],
|
68
|
-
:port =>
|
34
|
+
:port => pf_public_port
|
69
35
|
}
|
70
36
|
|
71
|
-
|
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.
|
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
|
-
|
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
|
-
|
41
|
+
transport_info = {
|
76
42
|
:host => pf_ip_address || nic_ip_address,
|
77
43
|
:port => pf_public_port
|
78
44
|
}
|
79
|
-
|
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
|
-
|
84
|
-
|
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.
|
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
|
-
|
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
|
-
|
33
|
+
transport_info = {
|
68
34
|
:host => pf_ip_address || server.nics[0]['ipaddress'],
|
69
35
|
:port => pf_public_port
|
70
36
|
}
|
71
37
|
|
72
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
110
|
-
wait_for_instance_ready(server)
|
36
|
+
wait_for_instance_ready
|
111
37
|
|
112
|
-
store_volumes
|
38
|
+
store_volumes
|
113
39
|
|
114
|
-
store_password
|
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
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
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
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
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
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
@
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
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
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
@
|
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
|
-
|
226
|
-
|
227
|
-
|
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
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
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
|
-
|
271
|
-
|
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
|
309
|
-
|
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
|
-
|
312
|
-
|
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
|
-
|
315
|
-
|
316
|
-
@
|
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
|
-
|
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
|
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
|
-
|
327
|
-
|
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
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
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
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
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
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
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
|
-
|
393
|
-
|
394
|
-
|
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
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
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
|
-
|
422
|
-
|
423
|
-
|
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
|
-
|
432
|
-
@domain_config.
|
433
|
-
|
434
|
-
|
435
|
-
|
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
|
446
|
-
|
447
|
-
|
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
|
-
|
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
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
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
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
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
|
524
|
-
|
525
|
-
|
526
|
-
|
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
|
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
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
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
|
-
|
618
|
-
|
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
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
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
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
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
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
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
|
-
|
689
|
-
|
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
|
-
|
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
|
-
|
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)
|