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.
- 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
|
-
[![Build Status](https://travis-ci.org/MissionCriticalCloud/vagrant-cloudstack.png?branch=master)](https://travis-ci.org/
|
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/
|
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
|
-
[![
|
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.
|
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)
|