vagrant-cloudstack 1.3.0 → 1.4.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 +18 -19
- data/.ruby-version +1 -1
- data/.travis.yml +19 -19
- data/CHANGELOG.md +179 -171
- data/Docker/.dockerignore +2 -0
- data/Docker/Dockerfile +51 -0
- data/Docker/Dockerfile.chefdk_0_17 +49 -0
- data/Docker/Dockerfile.latest_dependencies +49 -0
- data/Docker/README.md +67 -0
- data/Docker/vac.ps1 +29 -0
- data/Docker/vac.sh +30 -0
- data/Gemfile +20 -20
- data/LICENSE +8 -8
- data/README.md +416 -416
- data/Rakefile +106 -99
- data/bootstrap.key +27 -0
- data/build_rpm.sh +7 -7
- data/functional-tests/basic/Vagrantfile.basic_networking +45 -45
- data/functional-tests/basic/basic_spec.rb +21 -21
- data/functional-tests/networking/Vagrantfile.advanced_networking +119 -102
- data/functional-tests/networking/networking_spec.rb +14 -0
- data/functional-tests/rsync/Vagrantfile.advanced_networking +39 -56
- data/functional-tests/rsync/rsync_spec.rb +9 -9
- data/functional-tests/vmlifecycle/Vagrantfile.advanced_networking +66 -82
- data/functional-tests/vmlifecycle/vmlifecycle_spec.rb +13 -13
- data/lib/vagrant-cloudstack/action/connect_cloudstack.rb +47 -47
- data/lib/vagrant-cloudstack/action/is_created.rb +18 -18
- data/lib/vagrant-cloudstack/action/is_stopped.rb +18 -18
- data/lib/vagrant-cloudstack/action/message_already_created.rb +16 -16
- data/lib/vagrant-cloudstack/action/message_not_created.rb +16 -16
- data/lib/vagrant-cloudstack/action/message_will_not_destroy.rb +16 -16
- data/lib/vagrant-cloudstack/action/read_rdp_info.rb +76 -76
- data/lib/vagrant-cloudstack/action/read_ssh_info.rb +104 -87
- data/lib/vagrant-cloudstack/action/read_state.rb +38 -38
- data/lib/vagrant-cloudstack/action/read_winrm_info.rb +103 -103
- data/lib/vagrant-cloudstack/action/run_instance.rb +798 -703
- data/lib/vagrant-cloudstack/action/start_instance.rb +81 -81
- data/lib/vagrant-cloudstack/action/stop_instance.rb +28 -28
- data/lib/vagrant-cloudstack/action/terminate_instance.rb +269 -224
- data/lib/vagrant-cloudstack/action/timed_provision.rb +21 -21
- data/lib/vagrant-cloudstack/action/wait_for_state.rb +41 -41
- data/lib/vagrant-cloudstack/action/warn_networks.rb +19 -19
- data/lib/vagrant-cloudstack/action.rb +210 -210
- data/lib/vagrant-cloudstack/capabilities/rdp.rb +12 -12
- data/lib/vagrant-cloudstack/capabilities/winrm.rb +12 -12
- data/lib/vagrant-cloudstack/config.rb +566 -548
- data/lib/vagrant-cloudstack/errors.rb +27 -27
- data/lib/vagrant-cloudstack/exceptions/exceptions.rb +10 -10
- data/lib/vagrant-cloudstack/model/cloudstack_resource.rb +51 -33
- data/lib/vagrant-cloudstack/plugin.rb +82 -82
- data/lib/vagrant-cloudstack/provider.rb +58 -58
- data/lib/vagrant-cloudstack/service/cloudstack_resource_service.rb +64 -58
- data/lib/vagrant-cloudstack/util/timer.rb +17 -17
- data/lib/vagrant-cloudstack/version.rb +5 -5
- data/lib/vagrant-cloudstack.rb +17 -17
- data/locales/en.yml +131 -123
- data/spec/spec_helper.rb +8 -6
- data/spec/vagrant-cloudstack/action/read_ssh_info_spec.rb +80 -0
- data/spec/vagrant-cloudstack/config_spec.rb +355 -355
- data/spec/vagrant-cloudstack/model/cloudstack_resource_spec.rb +95 -73
- data/spec/vagrant-cloudstack/service/cloudstack_resource_service_spec.rb +43 -43
- data/spec/vagrant-cloudstack/support/be_a_resource.rb +6 -0
- data/vagrant-cloudstack.gemspec +59 -59
- data/vagrant-cloudstack.spec +42 -42
- metadata +14 -7
- data/dummy.box +0 -0
- data/example_box/README.md +0 -13
- data/example_box/metadata.json +0 -3
- data/functional-tests/networking/rsync_spec.rb +0 -12
@@ -1,103 +1,103 @@
|
|
1
|
-
require 'log4r'
|
2
|
-
|
3
|
-
module VagrantPlugins
|
4
|
-
module Cloudstack
|
5
|
-
module Action
|
6
|
-
# This action reads the WinRM info for the machine and puts it into the
|
7
|
-
# `:machine_winrm_info` key in the environment.
|
8
|
-
class ReadWinrmInfo
|
9
|
-
def initialize(app, env)
|
10
|
-
@app = app
|
11
|
-
@logger = Log4r::Logger.new("vagrant_cloudstack::action::read_winrm_info")
|
12
|
-
end
|
13
|
-
|
14
|
-
def call(env)
|
15
|
-
env[:machine_winrm_info] = read_winrm_info(env[:cloudstack_compute], env[:machine])
|
16
|
-
|
17
|
-
@app.call(env)
|
18
|
-
end
|
19
|
-
|
20
|
-
def read_winrm_info(cloudstack, machine)
|
21
|
-
return nil if machine.id.nil?
|
22
|
-
|
23
|
-
# Find the machine
|
24
|
-
server = cloudstack.servers.get(machine.id)
|
25
|
-
if server.nil?
|
26
|
-
# The machine can't be found
|
27
|
-
@logger.info("Machine couldn't be found, assuming it got destroyed.")
|
28
|
-
machine.id = nil
|
29
|
-
return nil
|
30
|
-
end
|
31
|
-
|
32
|
-
# Get the Port forwarding config
|
33
|
-
domain = machine.provider_config.domain_id
|
34
|
-
domain_config = machine.provider_config.get_domain_config(domain)
|
35
|
-
|
36
|
-
pf_ip_address_id = domain_config.pf_ip_address_id
|
37
|
-
pf_ip_address = domain_config.pf_ip_address
|
38
|
-
pf_public_port = domain_config.pf_public_port
|
39
|
-
|
40
|
-
if pf_public_port.nil?
|
41
|
-
pf_public_port_file = machine.data_dir.join('pf_public_port')
|
42
|
-
if pf_public_port_file.file?
|
43
|
-
File.read(pf_public_port_file).each_line do |line|
|
44
|
-
pf_public_port = line.strip
|
45
|
-
end
|
46
|
-
domain_config.pf_public_port = pf_public_port
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
if not pf_ip_address and pf_ip_address_id and pf_public_port
|
51
|
-
begin
|
52
|
-
response = cloudstack.list_public_ip_addresses({:id => pf_ip_address_id})
|
53
|
-
rescue Fog::Compute::Cloudstack::Error => e
|
54
|
-
raise Errors::FogError, :message => e.message
|
55
|
-
end
|
56
|
-
|
57
|
-
if response["listpublicipaddressesresponse"]["count"] == 0
|
58
|
-
@logger.info("IP address #{pf_ip_address_id} not exists.")
|
59
|
-
env[:ui].info(I18n.t("IP address #{pf_ip_address_id} not exists."))
|
60
|
-
pf_ip_address = nil
|
61
|
-
else
|
62
|
-
pf_ip_address = response["listpublicipaddressesresponse"]["publicipaddress"][0]["ipaddress"]
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
|
67
|
-
winrm_info = {
|
68
|
-
:host => pf_ip_address || server.nics[0]['ipaddress'],
|
69
|
-
:port => pf_public_port
|
70
|
-
}
|
71
|
-
|
72
|
-
winrm_info = winrm_info.merge({
|
73
|
-
:username => domain_config.vm_user
|
74
|
-
}) unless domain_config.vm_user.nil?
|
75
|
-
machine.config.winrm.username = domain_config.vm_user unless domain_config.vm_user.nil?
|
76
|
-
# The WinRM communicator doesnt support passing
|
77
|
-
# the username via winrm_info ... yet ;-)
|
78
|
-
|
79
|
-
# Read password from file into domain_config
|
80
|
-
if domain_config.vm_password.nil?
|
81
|
-
vmcredentials_file = machine.data_dir.join("vmcredentials")
|
82
|
-
if vmcredentials_file.file?
|
83
|
-
vmcredentials_password = nil
|
84
|
-
File.read(vmcredentials_file).each_line do |line|
|
85
|
-
vmcredentials_password = line.strip
|
86
|
-
end
|
87
|
-
domain_config.vm_password = vmcredentials_password
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
winrm_info = winrm_info.merge({
|
92
|
-
:password => domain_config.vm_password
|
93
|
-
}) unless domain_config.vm_password.nil?
|
94
|
-
# The WinRM communicator doesnt support passing
|
95
|
-
# the password via winrm_info ... yet ;-)
|
96
|
-
machine.config.winrm.password = domain_config.vm_password unless domain_config.vm_password.nil?
|
97
|
-
|
98
|
-
winrm_info
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
1
|
+
require 'log4r'
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module Cloudstack
|
5
|
+
module Action
|
6
|
+
# This action reads the WinRM info for the machine and puts it into the
|
7
|
+
# `:machine_winrm_info` key in the environment.
|
8
|
+
class ReadWinrmInfo
|
9
|
+
def initialize(app, env)
|
10
|
+
@app = app
|
11
|
+
@logger = Log4r::Logger.new("vagrant_cloudstack::action::read_winrm_info")
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(env)
|
15
|
+
env[:machine_winrm_info] = read_winrm_info(env[:cloudstack_compute], env[:machine])
|
16
|
+
|
17
|
+
@app.call(env)
|
18
|
+
end
|
19
|
+
|
20
|
+
def read_winrm_info(cloudstack, machine)
|
21
|
+
return nil if machine.id.nil?
|
22
|
+
|
23
|
+
# Find the machine
|
24
|
+
server = cloudstack.servers.get(machine.id)
|
25
|
+
if server.nil?
|
26
|
+
# The machine can't be found
|
27
|
+
@logger.info("Machine couldn't be found, assuming it got destroyed.")
|
28
|
+
machine.id = nil
|
29
|
+
return nil
|
30
|
+
end
|
31
|
+
|
32
|
+
# Get the Port forwarding config
|
33
|
+
domain = machine.provider_config.domain_id
|
34
|
+
domain_config = machine.provider_config.get_domain_config(domain)
|
35
|
+
|
36
|
+
pf_ip_address_id = domain_config.pf_ip_address_id
|
37
|
+
pf_ip_address = domain_config.pf_ip_address
|
38
|
+
pf_public_port = domain_config.pf_public_port
|
39
|
+
|
40
|
+
if pf_public_port.nil?
|
41
|
+
pf_public_port_file = machine.data_dir.join('pf_public_port')
|
42
|
+
if pf_public_port_file.file?
|
43
|
+
File.read(pf_public_port_file).each_line do |line|
|
44
|
+
pf_public_port = line.strip
|
45
|
+
end
|
46
|
+
domain_config.pf_public_port = pf_public_port
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
if not pf_ip_address and pf_ip_address_id and pf_public_port
|
51
|
+
begin
|
52
|
+
response = cloudstack.list_public_ip_addresses({:id => pf_ip_address_id})
|
53
|
+
rescue Fog::Compute::Cloudstack::Error => e
|
54
|
+
raise Errors::FogError, :message => e.message
|
55
|
+
end
|
56
|
+
|
57
|
+
if response["listpublicipaddressesresponse"]["count"] == 0
|
58
|
+
@logger.info("IP address #{pf_ip_address_id} not exists.")
|
59
|
+
env[:ui].info(I18n.t("IP address #{pf_ip_address_id} not exists."))
|
60
|
+
pf_ip_address = nil
|
61
|
+
else
|
62
|
+
pf_ip_address = response["listpublicipaddressesresponse"]["publicipaddress"][0]["ipaddress"]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
winrm_info = {
|
68
|
+
:host => pf_ip_address || server.nics[0]['ipaddress'],
|
69
|
+
:port => pf_public_port
|
70
|
+
}
|
71
|
+
|
72
|
+
winrm_info = winrm_info.merge({
|
73
|
+
:username => domain_config.vm_user
|
74
|
+
}) unless domain_config.vm_user.nil?
|
75
|
+
machine.config.winrm.username = domain_config.vm_user unless domain_config.vm_user.nil?
|
76
|
+
# The WinRM communicator doesnt support passing
|
77
|
+
# the username via winrm_info ... yet ;-)
|
78
|
+
|
79
|
+
# Read password from file into domain_config
|
80
|
+
if domain_config.vm_password.nil?
|
81
|
+
vmcredentials_file = machine.data_dir.join("vmcredentials")
|
82
|
+
if vmcredentials_file.file?
|
83
|
+
vmcredentials_password = nil
|
84
|
+
File.read(vmcredentials_file).each_line do |line|
|
85
|
+
vmcredentials_password = line.strip
|
86
|
+
end
|
87
|
+
domain_config.vm_password = vmcredentials_password
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
winrm_info = winrm_info.merge({
|
92
|
+
:password => domain_config.vm_password
|
93
|
+
}) unless domain_config.vm_password.nil?
|
94
|
+
# The WinRM communicator doesnt support passing
|
95
|
+
# the password via winrm_info ... yet ;-)
|
96
|
+
machine.config.winrm.password = domain_config.vm_password unless domain_config.vm_password.nil?
|
97
|
+
|
98
|
+
winrm_info
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -1,703 +1,798 @@
|
|
1
|
-
require 'log4r'
|
2
|
-
require 'vagrant/util/retryable'
|
3
|
-
require 'vagrant-cloudstack/exceptions/exceptions'
|
4
|
-
require 'vagrant-cloudstack/util/timer'
|
5
|
-
require 'vagrant-cloudstack/model/cloudstack_resource'
|
6
|
-
require 'vagrant-cloudstack/service/cloudstack_resource_service'
|
7
|
-
|
8
|
-
module VagrantPlugins
|
9
|
-
module Cloudstack
|
10
|
-
module Action
|
11
|
-
# This runs the configured instance.
|
12
|
-
class RunInstance
|
13
|
-
include Vagrant::Util::Retryable
|
14
|
-
include VagrantPlugins::Cloudstack::Model
|
15
|
-
include VagrantPlugins::Cloudstack::Service
|
16
|
-
include VagrantPlugins::Cloudstack::Exceptions
|
17
|
-
|
18
|
-
def initialize(app, env)
|
19
|
-
@app = app
|
20
|
-
@logger = Log4r::Logger.new('vagrant_cloudstack::action::run_instance')
|
21
|
-
@resource_service = CloudstackResourceService.new(env[:cloudstack_compute], env[:ui])
|
22
|
-
@security_groups = []
|
23
|
-
end
|
24
|
-
|
25
|
-
def call(env)
|
26
|
-
# Initialize metrics if they haven't been
|
27
|
-
env[:metrics] ||= {}
|
28
|
-
@env = env
|
29
|
-
|
30
|
-
# Get the domain we're going to booting up in
|
31
|
-
@domain = @env[:machine].provider_config.domain_id
|
32
|
-
# Get the configs
|
33
|
-
@domain_config = @env[:machine].provider_config.get_domain_config(@domain)
|
34
|
-
|
35
|
-
sanitize_domain_config
|
36
|
-
|
37
|
-
@zone = CloudstackResource.new(@domain_config.zone_id, @domain_config.zone_name, 'zone')
|
38
|
-
@
|
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
|
-
|
43
|
-
|
44
|
-
|
45
|
-
@
|
46
|
-
@resource_service.sync_resource(@
|
47
|
-
@resource_service.sync_resource(@
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
@domain_config.
|
58
|
-
@domain_config.
|
59
|
-
@domain_config.
|
60
|
-
|
61
|
-
@
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
@env[:ui].info(
|
91
|
-
@env[:ui].info(" --
|
92
|
-
@env[:ui].info(" --
|
93
|
-
@env[:ui].info(" --
|
94
|
-
@env[:ui].info(" --
|
95
|
-
@env[:ui].info(" --
|
96
|
-
@env[:ui].info(
|
97
|
-
@
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
@
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
@
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
end
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
#
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
@domain_config.
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
begin
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
:
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
}
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
@env[:ui].info(" --
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
:
|
632
|
-
:
|
633
|
-
:
|
634
|
-
:
|
635
|
-
:
|
636
|
-
:
|
637
|
-
:
|
638
|
-
}
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
while true
|
651
|
-
response = @env[:cloudstack_compute].query_async_job_result({:jobid => job_id})
|
652
|
-
if response['queryasyncjobresultresponse']['jobstatus'] != 0
|
653
|
-
|
654
|
-
break
|
655
|
-
else
|
656
|
-
sleep 2
|
657
|
-
end
|
658
|
-
end
|
659
|
-
rescue Fog::Compute::Cloudstack::Error => e
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
@env[:
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
1
|
+
require 'log4r'
|
2
|
+
require 'vagrant/util/retryable'
|
3
|
+
require 'vagrant-cloudstack/exceptions/exceptions'
|
4
|
+
require 'vagrant-cloudstack/util/timer'
|
5
|
+
require 'vagrant-cloudstack/model/cloudstack_resource'
|
6
|
+
require 'vagrant-cloudstack/service/cloudstack_resource_service'
|
7
|
+
|
8
|
+
module VagrantPlugins
|
9
|
+
module Cloudstack
|
10
|
+
module Action
|
11
|
+
# This runs the configured instance.
|
12
|
+
class RunInstance
|
13
|
+
include Vagrant::Util::Retryable
|
14
|
+
include VagrantPlugins::Cloudstack::Model
|
15
|
+
include VagrantPlugins::Cloudstack::Service
|
16
|
+
include VagrantPlugins::Cloudstack::Exceptions
|
17
|
+
|
18
|
+
def initialize(app, env)
|
19
|
+
@app = app
|
20
|
+
@logger = Log4r::Logger.new('vagrant_cloudstack::action::run_instance')
|
21
|
+
@resource_service = CloudstackResourceService.new(env[:cloudstack_compute], env[:ui])
|
22
|
+
@security_groups = []
|
23
|
+
end
|
24
|
+
|
25
|
+
def call(env)
|
26
|
+
# Initialize metrics if they haven't been
|
27
|
+
env[:metrics] ||= {}
|
28
|
+
@env = env
|
29
|
+
|
30
|
+
# Get the domain we're going to booting up in
|
31
|
+
@domain = @env[:machine].provider_config.domain_id
|
32
|
+
# Get the configs
|
33
|
+
@domain_config = @env[:machine].provider_config.get_domain_config(@domain)
|
34
|
+
|
35
|
+
sanitize_domain_config
|
36
|
+
|
37
|
+
@zone = CloudstackResource.new(@domain_config.zone_id, @domain_config.zone_name, 'zone')
|
38
|
+
@networks = CloudstackResource.create_list(@domain_config.network_id, @domain_config.network_name, 'network')
|
39
|
+
@service_offering = CloudstackResource.new(@domain_config.service_offering_id, @domain_config.service_offering_name, 'service_offering')
|
40
|
+
@disk_offering = CloudstackResource.new(@domain_config.disk_offering_id, @domain_config.disk_offering_name, 'disk_offering')
|
41
|
+
@template = CloudstackResource.new(@domain_config.template_id, @domain_config.template_name || @env[:machine].config.vm.box, 'template')
|
42
|
+
@pf_ip_address = CloudstackResource.new(@domain_config.pf_ip_address_id, @domain_config.pf_ip_address, 'public_ip_address')
|
43
|
+
|
44
|
+
@resource_service.sync_resource(@zone, { available: true })
|
45
|
+
cs_zone = @env[:cloudstack_compute].zones.find{ |f| f.id == @zone.id }
|
46
|
+
@resource_service.sync_resource(@service_offering, {listall: true})
|
47
|
+
@resource_service.sync_resource(@disk_offering, {listall: true})
|
48
|
+
@resource_service.sync_resource(@template, {zoneid: @zone.id, templatefilter: 'executable', listall: true})
|
49
|
+
@resource_service.sync_resource(@pf_ip_address)
|
50
|
+
|
51
|
+
if cs_zone.network_type.downcase == 'basic'
|
52
|
+
# No network specification in basic zone
|
53
|
+
@env[:ui].warn(I18n.t('vagrant_cloudstack.basic_network', :zone_name => @zone.name)) if !@networks.empty? && (@networks[0].id || @networks[0].name)
|
54
|
+
@networks = [CloudstackResource.new(nil, nil, 'network')]
|
55
|
+
|
56
|
+
# No portforwarding in basic zone, so none of the below
|
57
|
+
@domain_config.pf_ip_address = nil
|
58
|
+
@domain_config.pf_ip_address_id = nil
|
59
|
+
@domain_config.pf_public_port = nil
|
60
|
+
@domain_config.pf_public_rdp_port = nil
|
61
|
+
@domain_config.pf_public_port_randomrange = nil
|
62
|
+
else
|
63
|
+
@networks.each do |network|
|
64
|
+
@resource_service.sync_resource(network)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
if cs_zone.security_groups_enabled
|
69
|
+
prepare_security_groups
|
70
|
+
else
|
71
|
+
if !@domain_config.security_group_ids.empty? || !@domain_config.security_group_names.empty? || !@domain_config.security_groups.empty?
|
72
|
+
@env[:ui].warn(I18n.t('vagrant_cloudstack.security_groups_disabled', :zone_name => @zone.name))
|
73
|
+
end
|
74
|
+
@domain_config.security_group_ids = []
|
75
|
+
@domain_config.security_group_names = []
|
76
|
+
@domain_config.security_groups = []
|
77
|
+
end
|
78
|
+
|
79
|
+
@domain_config.display_name = generate_display_name if @domain_config.display_name.nil?
|
80
|
+
|
81
|
+
# If there is no keypair or keyfile then warn the user
|
82
|
+
if @domain_config.keypair.nil? && @domain_config.ssh_key.nil?
|
83
|
+
@env[:ui].warn(I18n.t('vagrant_cloudstack.launch_no_keypair_no_sshkey'))
|
84
|
+
store_ssh_keypair("vagacs_#{@domain_config.display_name}_#{sprintf('%04d', rand(9999))}",
|
85
|
+
nil, @domain, @domain_config.project_id)
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
# Launch!
|
90
|
+
@env[:ui].info(I18n.t('vagrant_cloudstack.launching_instance'))
|
91
|
+
@env[:ui].info(" -- Display Name: #{@domain_config.display_name}")
|
92
|
+
@env[:ui].info(" -- Group: #{@domain_config.group}") if @domain_config.group
|
93
|
+
@env[:ui].info(" -- Service offering: #{@service_offering.name} (#{@service_offering.id})")
|
94
|
+
@env[:ui].info(" -- Disk offering: #{@disk_offering.name} (#{@disk_offering.id})") unless @disk_offering.id.nil?
|
95
|
+
@env[:ui].info(" -- Template: #{@template.name} (#{@template.id})")
|
96
|
+
@env[:ui].info(" -- Project UUID: #{@domain_config.project_id}") unless @domain_config.project_id.nil?
|
97
|
+
@env[:ui].info(" -- Zone: #{@zone.name} (#{@zone.id})")
|
98
|
+
@networks.each do |network|
|
99
|
+
@env[:ui].info(" -- Network: #{network.name} (#{network.id})")
|
100
|
+
end
|
101
|
+
@env[:ui].info(" -- Keypair: #{@domain_config.keypair}") if @domain_config.keypair
|
102
|
+
@env[:ui].info(' -- User Data: Yes') if @domain_config.user_data
|
103
|
+
@security_groups.each do |security_group|
|
104
|
+
@env[:ui].info(" -- Security Group: #{security_group.name} (#{security_group.id})")
|
105
|
+
end
|
106
|
+
|
107
|
+
server = create_vm
|
108
|
+
|
109
|
+
# Wait for the instance to be ready first
|
110
|
+
wait_for_instance_ready(server)
|
111
|
+
|
112
|
+
store_volumes(server)
|
113
|
+
|
114
|
+
store_password(server)
|
115
|
+
|
116
|
+
configure_networking
|
117
|
+
|
118
|
+
unless @env[:interrupted]
|
119
|
+
wait_for_communicator_ready
|
120
|
+
@env[:ui].info(I18n.t('vagrant_cloudstack.ready'))
|
121
|
+
end
|
122
|
+
|
123
|
+
# Terminate the instance if we were interrupted
|
124
|
+
terminate if @env[:interrupted]
|
125
|
+
|
126
|
+
@app.call(@env)
|
127
|
+
end
|
128
|
+
|
129
|
+
def store_volumes(server)
|
130
|
+
volumes = @env[:cloudstack_compute].volumes.find_all { |f| f.server_id == server.id }
|
131
|
+
# volumes refuses to be iterated directly, do it by index
|
132
|
+
(0...volumes.length).each do |idx|
|
133
|
+
unless volumes[idx].type == 'ROOT'
|
134
|
+
volumes_file = @env[:machine].data_dir.join('volumes')
|
135
|
+
volumes_file.open('a+') do |f|
|
136
|
+
f.write("#{volumes[idx].id}\n")
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def sanitize_domain_config
|
143
|
+
# Accept a single entry as input, convert it to array
|
144
|
+
@domain_config.pf_trusted_networks = [@domain_config.pf_trusted_networks] if @domain_config.pf_trusted_networks
|
145
|
+
|
146
|
+
if @domain_config.network_id.nil?
|
147
|
+
# Use names if ids are not present
|
148
|
+
@domain_config.network_id = []
|
149
|
+
|
150
|
+
if @domain_config.network_name.nil?
|
151
|
+
@domain_config.network_name = []
|
152
|
+
else
|
153
|
+
@domain_config.network_name = Array(@domain_config.network_name)
|
154
|
+
end
|
155
|
+
else
|
156
|
+
# Use ids if present
|
157
|
+
@domain_config.network_id = Array(@domain_config.network_id)
|
158
|
+
@domain_config.network_name = []
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def configure_networking
|
163
|
+
enable_static_nat_rules
|
164
|
+
|
165
|
+
evaluate_pf_private_port
|
166
|
+
evaluate_pf_private_rdp_port
|
167
|
+
|
168
|
+
|
169
|
+
create_port_forwardings
|
170
|
+
# First create_port_forwardings,
|
171
|
+
# as it may generate 'pf_public_port' or 'pf_public_rdp_port',
|
172
|
+
# which after this may need a firewall rule
|
173
|
+
configure_firewall
|
174
|
+
end
|
175
|
+
|
176
|
+
def enable_static_nat_rules
|
177
|
+
unless @domain_config.static_nat.empty?
|
178
|
+
@domain_config.static_nat.each do |rule|
|
179
|
+
enable_static_nat( rule)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def generate_display_name
|
185
|
+
local_user = ENV['USER'].dup
|
186
|
+
local_user.gsub!(/[^-a-z0-9_]/i, '')
|
187
|
+
prefix = @env[:root_path].basename.to_s
|
188
|
+
prefix.gsub!(/[^-a-z0-9_]/i, '')
|
189
|
+
|
190
|
+
local_user + '_' + prefix + "_#{Time.now.to_i}"
|
191
|
+
end
|
192
|
+
|
193
|
+
def wait_for_communicator_ready
|
194
|
+
@env[:metrics]['instance_ssh_time'] = Util::Timer.time do
|
195
|
+
# Wait for communicator to be ready.
|
196
|
+
communicator = @env[:machine].communicate.instance_variable_get('@logger').instance_variable_get('@name')
|
197
|
+
@env[:ui].info(I18n.t('vagrant_cloudstack.waiting_for_communicator', :communicator => communicator.to_s.upcase))
|
198
|
+
while true
|
199
|
+
# If we're interrupted then just back out
|
200
|
+
break if @env[:interrupted]
|
201
|
+
break if @env[:machine].communicate.ready?
|
202
|
+
sleep 2
|
203
|
+
end
|
204
|
+
end
|
205
|
+
@logger.info("Time for SSH ready: #{@env[:metrics]['instance_ssh_time']}")
|
206
|
+
end
|
207
|
+
|
208
|
+
def wait_for_instance_ready( server)
|
209
|
+
@env[:metrics]['instance_ready_time'] = Util::Timer.time do
|
210
|
+
tries = @domain_config.instance_ready_timeout / 2
|
211
|
+
|
212
|
+
@env[:ui].info(I18n.t('vagrant_cloudstack.waiting_for_ready'))
|
213
|
+
begin
|
214
|
+
retryable(:on => Fog::Errors::TimeoutError, :tries => tries) do
|
215
|
+
# If we're interrupted don't worry about waiting
|
216
|
+
next if @env[:interrupted]
|
217
|
+
|
218
|
+
# Wait for the server to be ready
|
219
|
+
server.wait_for(2) { ready? }
|
220
|
+
end
|
221
|
+
rescue Fog::Errors::TimeoutError
|
222
|
+
# Delete the instance
|
223
|
+
terminate
|
224
|
+
|
225
|
+
# Notify the user
|
226
|
+
raise Errors::InstanceReadyTimeout,
|
227
|
+
:timeout => @domain_config.instance_ready_timeout
|
228
|
+
end
|
229
|
+
end
|
230
|
+
@logger.info("Time to instance ready: #{@env[:metrics]['instance_ready_time']}")
|
231
|
+
end
|
232
|
+
|
233
|
+
def create_vm
|
234
|
+
server = nil
|
235
|
+
begin
|
236
|
+
options = {
|
237
|
+
:display_name => @domain_config.display_name,
|
238
|
+
:group => @domain_config.group,
|
239
|
+
:zone_id => @zone.id,
|
240
|
+
:flavor_id => @service_offering.id,
|
241
|
+
:image_id => @template.id
|
242
|
+
}
|
243
|
+
|
244
|
+
options['network_ids'] = @networks.map(&:id).compact.join(",") unless @networks.empty?
|
245
|
+
options['security_group_ids'] = @security_groups.map{|security_group| security_group.id}.join(',') unless @security_groups.empty?
|
246
|
+
options['project_id'] = @domain_config.project_id unless @domain_config.project_id.nil?
|
247
|
+
options['key_name'] = @domain_config.keypair unless @domain_config.keypair.nil?
|
248
|
+
options['name'] = @domain_config.name unless @domain_config.name.nil?
|
249
|
+
options['ip_address'] = @domain_config.private_ip_address unless @domain_config.private_ip_address.nil?
|
250
|
+
options['disk_offering_id'] = @disk_offering.id unless @disk_offering.id.nil?
|
251
|
+
|
252
|
+
if @domain_config.user_data != nil
|
253
|
+
options['user_data'] = Base64.urlsafe_encode64(@domain_config.user_data)
|
254
|
+
if options['user_data'].length > 2048
|
255
|
+
raise Errors::UserdataError,
|
256
|
+
:userdataLength => options['user_data'].length
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
server = @env[:cloudstack_compute].servers.create(options)
|
261
|
+
rescue Fog::Compute::Cloudstack::NotFound => e
|
262
|
+
# Invalid subnet doesn't have its own error so we catch and
|
263
|
+
# check the error message here.
|
264
|
+
# XXX FIXME vpc?
|
265
|
+
if e.message =~ /subnet ID/
|
266
|
+
raise Errors::FogError,
|
267
|
+
:message => "Subnet ID not found: #{@networks.map(&:id).compact.join(",")}"
|
268
|
+
end
|
269
|
+
|
270
|
+
raise
|
271
|
+
rescue Fog::Compute::Cloudstack::Error => e
|
272
|
+
raise Errors::FogError, :message => e.message
|
273
|
+
end
|
274
|
+
|
275
|
+
# Immediately save the ID since it is created at this point.
|
276
|
+
@env[:machine].id = server.id
|
277
|
+
server
|
278
|
+
end
|
279
|
+
|
280
|
+
def prepare_security_groups
|
281
|
+
# Can't use Security Group IDs and Names at the same time
|
282
|
+
# Let's use IDs by default...
|
283
|
+
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
|
+
@security_groups = @domain_config.security_group_names.map do |name|
|
286
|
+
group = CloudstackResource.new(nil, name, 'security_group')
|
287
|
+
@resource_service.sync_resource(group)
|
288
|
+
group
|
289
|
+
end
|
290
|
+
elsif !@domain_config.security_group_ids.empty?
|
291
|
+
@security_groups = @domain_config.security_group_ids.map do |id|
|
292
|
+
group = CloudstackResource.new(id, nil, 'security_group')
|
293
|
+
@resource_service.sync_resource(group)
|
294
|
+
group
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
# Still no security group ids huh?
|
299
|
+
# Let's try to create some security groups from specifcation, if provided.
|
300
|
+
if !@domain_config.security_groups.empty? and @security_groups.empty?
|
301
|
+
@domain_config.security_groups.each do |security_group|
|
302
|
+
security_group = create_security_group( security_group)
|
303
|
+
@security_groups.push(security_group)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
def evaluate_pf_private_port
|
309
|
+
if @domain_config.pf_private_port.nil?
|
310
|
+
|
311
|
+
communicator = @env[:machine].communicate.instance_variable_get('@logger').instance_variable_get('@name')
|
312
|
+
comm_obj = @env[:machine].config.send(communicator)
|
313
|
+
|
314
|
+
@domain_config.pf_private_port = comm_obj.port if comm_obj.respond_to?('port')
|
315
|
+
@domain_config.pf_private_port = comm_obj.guest_port if comm_obj.respond_to?('guest_port')
|
316
|
+
@domain_config.pf_private_port = comm_obj.default.port if (comm_obj.respond_to?('default') && comm_obj.default.respond_to?('port'))
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
def evaluate_pf_private_rdp_port
|
321
|
+
@domain_config.pf_private_rdp_port = @env[:machine].config.vm.rdp.port if (@env[:machine].config.vm.respond_to?(:rdp) && @env[:machine].config.vm.rdp.respond_to?(:port))
|
322
|
+
end
|
323
|
+
|
324
|
+
def configure_firewall
|
325
|
+
|
326
|
+
unless @pf_ip_address.is_undefined?
|
327
|
+
ports = [ Hash[publicport: 'pf_public_port', privateport: 'pf_private_port'] ]
|
328
|
+
ports << Hash[publicport: 'pf_public_rdp_port', privateport: 'pf_private_rdp_port']
|
329
|
+
|
330
|
+
ports.each do |port_set|
|
331
|
+
if @pf_ip_address.details.has_key?('vpcid')
|
332
|
+
forward_portname = port_set[:privateport]
|
333
|
+
else
|
334
|
+
forward_portname = port_set[:publicport]
|
335
|
+
end
|
336
|
+
check_portname = port_set[:publicport]
|
337
|
+
# As we take care of implicit/auto port_forward of 'pf_public_port' we do Firewall as well, possibly
|
338
|
+
if (@domain_config.pf_ip_address_id || @domain_config.pf_ip_address) &&
|
339
|
+
@domain_config.send(check_portname) &&
|
340
|
+
@domain_config.pf_trusted_networks &&
|
341
|
+
!@domain_config.pf_open_firewall
|
342
|
+
# Allow access to public port from trusted networks only
|
343
|
+
fw_rule_trusted_networks = {
|
344
|
+
:ipaddressid => @domain_config.pf_ip_address_id,
|
345
|
+
:ipaddress => @domain_config.pf_ip_address,
|
346
|
+
:protocol => 'tcp',
|
347
|
+
:startport => @domain_config.send(forward_portname),
|
348
|
+
:endport => @domain_config.send(forward_portname),
|
349
|
+
:cidrlist => @domain_config.pf_trusted_networks.join(',')
|
350
|
+
}
|
351
|
+
@domain_config.firewall_rules = [] unless @domain_config.firewall_rules
|
352
|
+
@domain_config.firewall_rules << fw_rule_trusted_networks
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
unless @domain_config.firewall_rules.empty?
|
358
|
+
|
359
|
+
# Inspect port_forwarding rules to make firewall rules
|
360
|
+
if @pf_ip_address.details.has_key?('vpcid')
|
361
|
+
port_name = :privateport
|
362
|
+
else
|
363
|
+
port_name = :publicport
|
364
|
+
end
|
365
|
+
unless @domain_config.port_forwarding_rules.empty?
|
366
|
+
@domain_config.port_forwarding_rules.each do |port_forwarding_rule|
|
367
|
+
if port_forwarding_rule[:generate_firewall] && @domain_config.pf_trusted_networks && !port_forwarding_rule[:openfirewall]
|
368
|
+
# Allow access to public port from trusted networks only
|
369
|
+
fw_rule_trusted_networks = {
|
370
|
+
:ipaddressid => port_forwarding_rule[:ipaddressid],
|
371
|
+
:ipaddress => port_forwarding_rule[:ipaddress],
|
372
|
+
:protocol => port_forwarding_rule[:protocol],
|
373
|
+
:startport => port_forwarding_rule[port_name],
|
374
|
+
:endport => port_forwarding_rule[port_name],
|
375
|
+
:cidrlist => @domain_config.pf_trusted_networks.join(',')
|
376
|
+
}
|
377
|
+
@domain_config.firewall_rules = [] unless @domain_config.firewall_rules
|
378
|
+
@domain_config.firewall_rules << fw_rule_trusted_networks
|
379
|
+
end
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
# Fill in the blanks for all rules
|
384
|
+
@domain_config.firewall_rules.each do |firewall_rule|
|
385
|
+
firewall_rule[:ipaddressid] = @domain_config.pf_ip_address_id if firewall_rule[:ipaddressid].nil?
|
386
|
+
firewall_rule[:ipaddress] = @domain_config.pf_ip_address if firewall_rule[:ipaddress].nil?
|
387
|
+
firewall_rule[:cidrlist] = @domain_config.pf_trusted_networks.join(',') if firewall_rule[:cidrlist].nil?
|
388
|
+
firewall_rule[:protocol] = 'tcp' if firewall_rule[:protocol].nil?
|
389
|
+
firewall_rule[:startport] = firewall_rule[:endport] if firewall_rule[:startport].nil?
|
390
|
+
end
|
391
|
+
|
392
|
+
# Apply all rules
|
393
|
+
@domain_config.firewall_rules.each do |firewall_rule|
|
394
|
+
create_firewall_rule( firewall_rule )
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
def create_port_forwardings
|
400
|
+
unless @pf_ip_address.is_undefined?
|
401
|
+
guest_windows = false || @env[:machine].config.vm.guest == :windows || @env[:machine].communicate.instance_variable_get('@logger').instance_variable_get('@name') == 'winrm'
|
402
|
+
|
403
|
+
ports = [ Hash[:public_port => 'pf_public_port', :private_port => 'pf_private_port'] ]
|
404
|
+
ports << Hash[:public_port => 'pf_public_rdp_port', :private_port => 'pf_private_rdp_port'] if guest_windows
|
405
|
+
|
406
|
+
ports.each do |port_set|
|
407
|
+
# Implicit/automatic Port forward for 'private' port (SSH/WinRM or RDP)
|
408
|
+
# Also sets 'public_port' port to random port if missing
|
409
|
+
public_port_name = port_set[:public_port]
|
410
|
+
private_port_name = port_set[:private_port]
|
411
|
+
if (@domain_config.pf_ip_address_id || @domain_config.pf_ip_address) && (@domain_config.send(public_port_name) || @domain_config.pf_public_port_randomrange)
|
412
|
+
port_forwarding_rule = {
|
413
|
+
:ipaddressid => @domain_config.pf_ip_address_id,
|
414
|
+
:ipaddress => @domain_config.pf_ip_address,
|
415
|
+
:protocol => 'tcp',
|
416
|
+
:publicport => @domain_config.send(public_port_name),
|
417
|
+
:privateport => @domain_config.send(private_port_name),
|
418
|
+
:openfirewall => @domain_config.pf_open_firewall
|
419
|
+
}
|
420
|
+
|
421
|
+
public_port = create_randomport_forwarding_rule(
|
422
|
+
port_forwarding_rule,
|
423
|
+
@domain_config.pf_public_port_randomrange[:start]...@domain_config.pf_public_port_randomrange[:end],
|
424
|
+
public_port_name
|
425
|
+
)
|
426
|
+
@domain_config.send("#{public_port_name}=", public_port)
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
unless @domain_config.port_forwarding_rules.empty?
|
432
|
+
@domain_config.port_forwarding_rules.each do |port_forwarding_rule|
|
433
|
+
port_forwarding_rule[:ipaddressid] = @domain_config.pf_ip_address_id if port_forwarding_rule[:ipaddressid].nil?
|
434
|
+
port_forwarding_rule[:ipaddress] = @domain_config.pf_ip_address if port_forwarding_rule[:ipaddress].nil?
|
435
|
+
port_forwarding_rule[:protocol] = 'tcp' if port_forwarding_rule[:protocol].nil?
|
436
|
+
port_forwarding_rule[:openfirewall] = @domain_config.pf_open_firewall if port_forwarding_rule[:openfirewall].nil?
|
437
|
+
port_forwarding_rule[:publicport] = port_forwarding_rule[:privateport] if port_forwarding_rule[:publicport].nil?
|
438
|
+
port_forwarding_rule[:privateport] = port_forwarding_rule[:publicport] if port_forwarding_rule[:privateport].nil?
|
439
|
+
|
440
|
+
create_port_forwarding_rule(port_forwarding_rule)
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
def create_randomport_forwarding_rule(rule, randomrange, filename)
|
446
|
+
# Only if pf_public_port is nil, will generate and try
|
447
|
+
# Otherwise, functionaly the same as just create_port_forwarding_rule
|
448
|
+
pf_public_port = rule[:publicport]
|
449
|
+
retryable(:on => DuplicatePFRule, :tries => 10) do
|
450
|
+
begin
|
451
|
+
rule[:publicport] = rand(randomrange) if pf_public_port.nil?
|
452
|
+
|
453
|
+
create_port_forwarding_rule(rule)
|
454
|
+
|
455
|
+
if pf_public_port.nil?
|
456
|
+
pf_port_file = @env[:machine].data_dir.join(filename)
|
457
|
+
pf_port_file.open('a+') do |f|
|
458
|
+
f.write("#{rule[:publicport]}")
|
459
|
+
end
|
460
|
+
end
|
461
|
+
rescue Errors::FogError => e
|
462
|
+
if pf_public_port.nil? && !(e.message =~ /The range specified,.*conflicts with rule.*which has/).nil?
|
463
|
+
raise DuplicatePFRule, :message => e.message
|
464
|
+
else
|
465
|
+
raise Errors::FogError, :message => e.message
|
466
|
+
end
|
467
|
+
end
|
468
|
+
end
|
469
|
+
pf_public_port.nil? ? (rule[:publicport]) : (pf_public_port)
|
470
|
+
end
|
471
|
+
|
472
|
+
def store_ssh_keypair(keyname, account = nil, domainid = nil, projectid = nil)
|
473
|
+
response = @env[:cloudstack_compute].create_ssh_key_pair(keyname, account, domainid, projectid)
|
474
|
+
sshkeypair = response['createsshkeypairresponse']['keypair']
|
475
|
+
|
476
|
+
# Save private key to file
|
477
|
+
sshkeyfile_file = @env[:machine].data_dir.join('sshkeyfile')
|
478
|
+
sshkeyfile_file.open('w') do |f|
|
479
|
+
f.write("#{sshkeypair['privatekey']}")
|
480
|
+
end
|
481
|
+
@domain_config.ssh_key = sshkeyfile_file.to_s
|
482
|
+
|
483
|
+
# Save keyname to file for terminate_instance
|
484
|
+
sshkeyname_file = @env[:machine].data_dir.join('sshkeyname')
|
485
|
+
sshkeyname_file.open('w') do |f|
|
486
|
+
f.write("#{sshkeypair['name']}")
|
487
|
+
end
|
488
|
+
|
489
|
+
@domain_config.keypair = sshkeypair['name']
|
490
|
+
end
|
491
|
+
|
492
|
+
def store_password(server)
|
493
|
+
password = nil
|
494
|
+
if server.password_enabled and server.respond_to?('job_id')
|
495
|
+
server_job_result = @env[:cloudstack_compute].query_async_job_result({:jobid => server.job_id})
|
496
|
+
if server_job_result.nil?
|
497
|
+
@env[:ui].warn(' -- Failed to retrieve job_result for retrieving the password')
|
498
|
+
return
|
499
|
+
end
|
500
|
+
|
501
|
+
while true
|
502
|
+
server_job_result = @env[:cloudstack_compute].query_async_job_result({:jobid => server.job_id})
|
503
|
+
if server_job_result['queryasyncjobresultresponse']['jobstatus'] != 0
|
504
|
+
password = server_job_result['queryasyncjobresultresponse']['jobresult']['virtualmachine']['password']
|
505
|
+
break
|
506
|
+
else
|
507
|
+
sleep 2
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
511
|
+
@env[:ui].info("Password of virtualmachine: #{password}")
|
512
|
+
# Set the password on the current communicator
|
513
|
+
@domain_config.vm_password = password
|
514
|
+
|
515
|
+
# Save password to file
|
516
|
+
vmcredentials_file = @env[:machine].data_dir.join('vmcredentials')
|
517
|
+
vmcredentials_file.open('w') do |f|
|
518
|
+
f.write("#{password}")
|
519
|
+
end
|
520
|
+
end
|
521
|
+
end
|
522
|
+
|
523
|
+
def create_security_group(security_group)
|
524
|
+
begin
|
525
|
+
sgid = @env[:cloudstack_compute].create_security_group(:name => security_group[:name],
|
526
|
+
:description => security_group[:description])['createsecuritygroupresponse']['securitygroup']['id']
|
527
|
+
security_group_object = CloudstackResource.new(sgid, security_group[:name], 'security_group')
|
528
|
+
@env[:ui].info(" -- Security Group #{security_group[:name]} created with ID: #{sgid}")
|
529
|
+
rescue Exception => e
|
530
|
+
if e.message =~ /already exis/
|
531
|
+
security_group_object = CloudstackResource.new(nil, security_group[:name], 'security_group')
|
532
|
+
@resource_service.sync_resource(security_group_object)
|
533
|
+
@env[:ui].info(" -- Security Group #{security_group_object.name} found with ID: #{security_group_object.id}")
|
534
|
+
end
|
535
|
+
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
|
+
end
|
570
|
+
|
571
|
+
def enable_static_nat(rule)
|
572
|
+
@env[:ui].info(I18n.t('vagrant_cloudstack.enabling_static_nat'))
|
573
|
+
|
574
|
+
begin
|
575
|
+
ip_address = sync_ip_address(rule[:ipaddressid], rule[:ipaddress])
|
576
|
+
rescue IpNotFoundException
|
577
|
+
return
|
578
|
+
end
|
579
|
+
|
580
|
+
@env[:ui].info(" -- IP address : #{ip_address.name} (#{ip_address.id})")
|
581
|
+
|
582
|
+
options = {
|
583
|
+
:command => 'enableStaticNat',
|
584
|
+
:ipaddressid => ip_address_id,
|
585
|
+
:virtualmachineid => @env[:machine].id
|
586
|
+
}
|
587
|
+
|
588
|
+
begin
|
589
|
+
resp = @env[:cloudstack_compute].request(options)
|
590
|
+
is_success = resp['enablestaticnatresponse']['success']
|
591
|
+
|
592
|
+
if is_success != 'true'
|
593
|
+
@env[:ui].warn(" -- Failed to enable static nat: #{resp['enablestaticnatresponse']['errortext']}")
|
594
|
+
return
|
595
|
+
end
|
596
|
+
rescue Fog::Compute::Cloudstack::Error => e
|
597
|
+
raise Errors::FogError, :message => e.message
|
598
|
+
end
|
599
|
+
|
600
|
+
# Save ipaddress id to the data dir so it can be disabled when the instance is destroyed
|
601
|
+
static_nat_file = @env[:machine].data_dir.join('static_nat')
|
602
|
+
static_nat_file.open('a+') do |f|
|
603
|
+
f.write("#{ip_address.id}\n")
|
604
|
+
end
|
605
|
+
end
|
606
|
+
|
607
|
+
def create_port_forwarding_rule(rule)
|
608
|
+
port_forwarding_rule = nil
|
609
|
+
@env[:ui].info(I18n.t('vagrant_cloudstack.creating_port_forwarding_rule'))
|
610
|
+
|
611
|
+
begin
|
612
|
+
ip_address = sync_ip_address(rule[:ipaddressid], rule[:ipaddress])
|
613
|
+
rescue IpNotFoundException
|
614
|
+
return
|
615
|
+
end
|
616
|
+
|
617
|
+
@env[:ui].info(" -- IP address : #{ip_address.name} (#{ip_address.id})")
|
618
|
+
@env[:ui].info(" -- Protocol : #{rule[:protocol]}")
|
619
|
+
@env[:ui].info(" -- Public port : #{rule[:publicport]}")
|
620
|
+
@env[:ui].info(" -- Private port : #{rule[:privateport]}")
|
621
|
+
@env[:ui].info(" -- Open Firewall : #{rule[:openfirewall]}")
|
622
|
+
|
623
|
+
if ip_address.details.has_key?('associatednetworkid')
|
624
|
+
network = @networks.find{ |f| f.id == ip_address.details['associatednetworkid']}
|
625
|
+
elsif ip_address.details.has_key?('vpcid')
|
626
|
+
# In case of VPC and ip has not yet been used, a network MUST be specified
|
627
|
+
network = @networks.find{ |f| f.details['vpcid'] == ip_address.details['vpcid']}
|
628
|
+
end
|
629
|
+
|
630
|
+
options = {
|
631
|
+
:networkid => network.id,
|
632
|
+
:ipaddressid => ip_address.id,
|
633
|
+
:publicport => rule[:publicport],
|
634
|
+
:privateport => rule[:privateport],
|
635
|
+
:protocol => rule[:protocol],
|
636
|
+
:openfirewall => rule[:openfirewall],
|
637
|
+
:virtualmachineid => @env[:machine].id
|
638
|
+
}
|
639
|
+
|
640
|
+
options.delete(:openfirewall) if network.details.has_key?('vpcid')
|
641
|
+
begin
|
642
|
+
resp = @env[:cloudstack_compute].create_port_forwarding_rule(options)
|
643
|
+
job_id = resp['createportforwardingruleresponse']['jobid']
|
644
|
+
|
645
|
+
if job_id.nil?
|
646
|
+
@env[:ui].warn(" -- Failed to create port forwarding rule: #{resp['createportforwardingruleresponse']['errortext']}")
|
647
|
+
return
|
648
|
+
end
|
649
|
+
|
650
|
+
while true
|
651
|
+
response = @env[:cloudstack_compute].query_async_job_result({:jobid => job_id})
|
652
|
+
if response['queryasyncjobresultresponse']['jobstatus'] != 0
|
653
|
+
port_forwarding_rule = response['queryasyncjobresultresponse']['jobresult']['portforwardingrule']
|
654
|
+
break
|
655
|
+
else
|
656
|
+
sleep 2
|
657
|
+
end
|
658
|
+
end
|
659
|
+
rescue Fog::Compute::Cloudstack::Error => e
|
660
|
+
raise Errors::FogError, :message => e.message
|
661
|
+
end
|
662
|
+
|
663
|
+
# Save port forwarding rule id to the data dir so it can be released when the instance is destroyed
|
664
|
+
port_forwarding_file = @env[:machine].data_dir.join('port_forwarding')
|
665
|
+
port_forwarding_file.open('a+') do |f|
|
666
|
+
f.write("#{port_forwarding_rule['id']}\n")
|
667
|
+
end
|
668
|
+
end
|
669
|
+
|
670
|
+
def create_firewall_rule(rule)
|
671
|
+
acl_name = ''
|
672
|
+
firewall_rule = nil
|
673
|
+
@env[:ui].info(I18n.t('vagrant_cloudstack.creating_firewall_rule'))
|
674
|
+
|
675
|
+
ip_address = CloudstackResource.new(rule[:ipaddressid], rule[:ipaddress], 'public_ip_address')
|
676
|
+
@resource_service.sync_resource(ip_address)
|
677
|
+
|
678
|
+
@env[:ui].info(" -- IP address : #{ip_address.name} (#{ip_address.id})")
|
679
|
+
@env[:ui].info(" -- Protocol : #{rule[:protocol]}")
|
680
|
+
@env[:ui].info(" -- CIDR list : #{rule[:cidrlist]}")
|
681
|
+
@env[:ui].info(" -- Start port : #{rule[:startport]}")
|
682
|
+
@env[:ui].info(" -- End port : #{rule[:endport]}")
|
683
|
+
@env[:ui].info(" -- ICMP code : #{rule[:icmpcode]}")
|
684
|
+
@env[:ui].info(" -- ICMP type : #{rule[:icmptype]}")
|
685
|
+
|
686
|
+
if ip_address.details.has_key?('vpcid')
|
687
|
+
network = @networks.find{ |f| f.id == ip_address.details['associatednetworkid']}
|
688
|
+
resp = @env[:cloudstack_compute].list_network_acl_lists({
|
689
|
+
id: network.details['aclid']
|
690
|
+
})
|
691
|
+
acl_name = resp['listnetworkacllistsresponse']['networkacllist'][0]['name']
|
692
|
+
|
693
|
+
resp = @env[:cloudstack_compute].list_network_acls({
|
694
|
+
aclid: network.details['aclid']
|
695
|
+
})
|
696
|
+
number = 0
|
697
|
+
if resp["listnetworkaclsresponse"].key?("networkacl")
|
698
|
+
resp["listnetworkaclsresponse"]["networkacl"].each{ |ace| number = [number, ace["number"]].max }
|
699
|
+
end
|
700
|
+
number = number+1
|
701
|
+
|
702
|
+
command_string = 'createNetworkACL'
|
703
|
+
response_string = 'createnetworkaclresponse'
|
704
|
+
type_string = 'networkacl'
|
705
|
+
options = {
|
706
|
+
:command => command_string,
|
707
|
+
:aclid => network.details['aclid'],
|
708
|
+
:action => 'Allow',
|
709
|
+
:protocol => rule[:protocol],
|
710
|
+
:cidrlist => rule[:cidrlist],
|
711
|
+
:startport => rule[:startport],
|
712
|
+
:endport => rule[:endport],
|
713
|
+
:icmpcode => rule[:icmpcode],
|
714
|
+
:icmptype => rule[:icmptype],
|
715
|
+
:number => number,
|
716
|
+
:traffictype => 'Ingress'
|
717
|
+
}
|
718
|
+
else
|
719
|
+
command_string = 'createFirewallRule'
|
720
|
+
response_string = 'createfirewallruleresponse'
|
721
|
+
type_string = 'firewallrule'
|
722
|
+
options = {
|
723
|
+
:command => command_string,
|
724
|
+
:ipaddressid => ip_address.id,
|
725
|
+
:protocol => rule[:protocol],
|
726
|
+
:cidrlist => rule[:cidrlist],
|
727
|
+
:startport => rule[:startport],
|
728
|
+
:endeport => rule[:endport],
|
729
|
+
:icmpcode => rule[:icmpcode],
|
730
|
+
:icmptype => rule[:icmptype]
|
731
|
+
}
|
732
|
+
end
|
733
|
+
|
734
|
+
begin
|
735
|
+
resp = @env[:cloudstack_compute].request(options)
|
736
|
+
job_id = resp[response_string]['jobid']
|
737
|
+
|
738
|
+
if job_id.nil?
|
739
|
+
@env[:ui].warn(" -- Failed to create firewall rule: #{resp[response_string]['errortext']}")
|
740
|
+
return
|
741
|
+
end
|
742
|
+
|
743
|
+
while true
|
744
|
+
response = @env[:cloudstack_compute].query_async_job_result({:jobid => job_id})
|
745
|
+
if response['queryasyncjobresultresponse']['jobstatus'] != 0
|
746
|
+
firewall_rule = response['queryasyncjobresultresponse']['jobresult'][type_string]
|
747
|
+
break
|
748
|
+
else
|
749
|
+
sleep 2
|
750
|
+
end
|
751
|
+
end
|
752
|
+
rescue Fog::Compute::Cloudstack::Error => e
|
753
|
+
if e.message =~ /The range specified,.*conflicts with rule/
|
754
|
+
@env[:ui].warn(" -- Failed to create firewall rule: #{e.message}")
|
755
|
+
elsif e.message =~ /Default ACL cannot be modified/
|
756
|
+
@env[:ui].warn(" -- Failed to create network acl: #{e.message}: #{acl_name}")
|
757
|
+
else
|
758
|
+
raise Errors::FogError, :message => e.message
|
759
|
+
end
|
760
|
+
end
|
761
|
+
|
762
|
+
unless firewall_rule.nil?
|
763
|
+
# Save firewall rule id to the data dir so it can be released when the instance is destroyed
|
764
|
+
firewall_file = @env[:machine].data_dir.join('firewall')
|
765
|
+
firewall_file.open('a+') do |f|
|
766
|
+
f.write("#{firewall_rule['id']},#{type_string}\n")
|
767
|
+
end
|
768
|
+
end
|
769
|
+
end
|
770
|
+
|
771
|
+
def terminate
|
772
|
+
destroy_env = @env.dup
|
773
|
+
destroy_env.delete(:interrupted)
|
774
|
+
destroy_env[:config_validate] = false
|
775
|
+
destroy_env[:force_confirm_destroy] = true
|
776
|
+
@env[:action_runner].run(Action.action_destroy, destroy_env)
|
777
|
+
end
|
778
|
+
|
779
|
+
private
|
780
|
+
|
781
|
+
def sync_ip_address(ip_address_id, ip_address_value)
|
782
|
+
ip_address = CloudstackResource.new(ip_address_id, ip_address_value, 'public_ip_address')
|
783
|
+
|
784
|
+
if ip_address.is_undefined?
|
785
|
+
message = 'IP address is not specified. Skip creating port forwarding rule.'
|
786
|
+
@logger.info(message)
|
787
|
+
@env[:ui].info(I18n.t(message))
|
788
|
+
raise IpNotFoundException
|
789
|
+
end
|
790
|
+
|
791
|
+
@resource_service.sync_resource(ip_address)
|
792
|
+
|
793
|
+
ip_address
|
794
|
+
end
|
795
|
+
end
|
796
|
+
end
|
797
|
+
end
|
798
|
+
end
|