vagrant-vcloud 0.1.2 → 0.2.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 +3 -0
- data/.rubocop.yml +34 -0
- data/README.md +19 -2
- data/Rakefile +1 -1
- data/lib/vagrant-vcloud.rb +11 -13
- data/lib/vagrant-vcloud/action.rb +74 -47
- data/lib/vagrant-vcloud/action/announce_ssh_exec.rb +4 -2
- data/lib/vagrant-vcloud/action/build_vapp.rb +134 -110
- data/lib/vagrant-vcloud/action/connect_vcloud.rb +31 -42
- data/lib/vagrant-vcloud/action/destroy.rb +34 -29
- data/lib/vagrant-vcloud/action/disconnect_vcloud.rb +7 -5
- data/lib/vagrant-vcloud/action/forward_ports.rb +42 -35
- data/lib/vagrant-vcloud/action/handle_nat_port_collisions.rb +26 -22
- data/lib/vagrant-vcloud/action/inventory_check.rb +79 -52
- data/lib/vagrant-vcloud/action/is_bridged.rb +30 -0
- data/lib/vagrant-vcloud/action/is_created.rb +13 -14
- data/lib/vagrant-vcloud/action/is_paused.rb +0 -2
- data/lib/vagrant-vcloud/action/is_running.rb +0 -2
- data/lib/vagrant-vcloud/action/message_already_running.rb +1 -1
- data/lib/vagrant-vcloud/action/message_cannot_suspend.rb +1 -1
- data/lib/vagrant-vcloud/action/message_not_created.rb +1 -1
- data/lib/vagrant-vcloud/action/message_will_not_destroy.rb +6 -1
- data/lib/vagrant-vcloud/action/power_off.rb +16 -21
- data/lib/vagrant-vcloud/action/power_on.rb +64 -28
- data/lib/vagrant-vcloud/action/read_ssh_info.rb +44 -28
- data/lib/vagrant-vcloud/action/read_state.rb +16 -23
- data/lib/vagrant-vcloud/action/resume.rb +5 -13
- data/lib/vagrant-vcloud/action/suspend.rb +5 -13
- data/lib/vagrant-vcloud/action/sync_folders.rb +82 -48
- data/lib/vagrant-vcloud/action/unmap_port_forwardings.rb +27 -29
- data/lib/vagrant-vcloud/command.rb +186 -0
- data/lib/vagrant-vcloud/config.rb +41 -20
- data/lib/vagrant-vcloud/driver/base.rb +170 -121
- data/lib/vagrant-vcloud/driver/meta.rb +64 -70
- data/lib/vagrant-vcloud/driver/version_5_1.rb +1038 -716
- data/lib/vagrant-vcloud/errors.rb +4 -4
- data/lib/vagrant-vcloud/model/forwarded_port.rb +4 -2
- data/lib/vagrant-vcloud/plugin.rb +30 -20
- data/lib/vagrant-vcloud/provider.rb +6 -6
- data/lib/vagrant-vcloud/util/compile_forwarded_ports.rb +1 -1
- data/lib/vagrant-vcloud/version.rb +1 -1
- data/locales/en.yml +6 -5
- data/vagrant-vcloud.gemspec +10 -7
- metadata +35 -4
@@ -15,30 +15,26 @@
|
|
15
15
|
# limitations under the License.
|
16
16
|
#
|
17
17
|
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
21
|
-
require
|
18
|
+
require 'forwardable'
|
19
|
+
require 'log4r'
|
20
|
+
require 'nokogiri'
|
21
|
+
require 'httpclient'
|
22
22
|
|
23
|
-
|
24
|
-
require File.expand_path("../base", __FILE__)
|
23
|
+
require File.expand_path('../base', __FILE__)
|
25
24
|
|
26
25
|
module VagrantPlugins
|
27
26
|
module VCloud
|
28
27
|
module Driver
|
29
|
-
|
30
28
|
class Meta < Base
|
31
|
-
|
32
29
|
# We use forwardable to do all our driver forwarding
|
33
30
|
extend Forwardable
|
34
31
|
attr_reader :driver
|
35
|
-
|
36
|
-
def initialize(hostname, username, password, org_name)
|
37
32
|
|
33
|
+
def initialize(hostname, username, password, org_name)
|
38
34
|
# Setup the base
|
39
35
|
super()
|
40
36
|
|
41
|
-
@logger = Log4r::Logger.new(
|
37
|
+
@logger = Log4r::Logger.new('vagrant::provider::vcloud::meta')
|
42
38
|
@hostname = hostname
|
43
39
|
@username = username
|
44
40
|
@password = password
|
@@ -46,17 +42,20 @@ module VagrantPlugins
|
|
46
42
|
|
47
43
|
# Read and assign the version of vCloud we know which
|
48
44
|
# specific driver to instantiate.
|
49
|
-
@version = get_api_version(@hostname) ||
|
45
|
+
@version = get_api_version(@hostname) || ''
|
50
46
|
|
51
47
|
# Instantiate the proper version driver for vCloud
|
52
48
|
@logger.debug("Finding driver for vCloud version: #{@version}")
|
53
49
|
driver_map = {
|
54
50
|
"5.1" => Version_5_1,
|
55
|
-
"5.5" => Version_5_1 # Binding vCloud 5.5 API on our current 5.1 implementation
|
51
|
+
"5.5" => Version_5_1, # Binding vCloud 5.5 API on our current 5.1 implementation
|
52
|
+
"5.7" => Version_5_1 # Binding vCHS API on our current 5.1 implementation
|
56
53
|
}
|
57
54
|
|
58
|
-
if @version.start_with?(
|
59
|
-
|
55
|
+
if @version.start_with?('0.9') ||
|
56
|
+
@version.start_with?('1.0') ||
|
57
|
+
@version.start_with?('1.5')
|
58
|
+
# We only support vCloud Director 5.1 or higher.
|
60
59
|
raise Errors::VCloudOldVersion, :version => @version
|
61
60
|
end
|
62
61
|
|
@@ -69,93 +68,88 @@ module VagrantPlugins
|
|
69
68
|
end
|
70
69
|
|
71
70
|
if !driver_klass
|
72
|
-
supported_versions = driver_map.keys.sort.join(
|
73
|
-
raise Errors::VCloudInvalidVersion,
|
71
|
+
supported_versions = driver_map.keys.sort.join(', ')
|
72
|
+
raise Errors::VCloudInvalidVersion,
|
73
|
+
:supported_versions => supported_versions
|
74
74
|
end
|
75
75
|
|
76
76
|
@logger.info("Using vCloud driver: #{driver_klass}")
|
77
77
|
@driver = driver_klass.new(@hostname, @username, @password, @org_name)
|
78
|
-
|
79
78
|
end
|
80
79
|
|
81
|
-
def_delegators
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
80
|
+
def_delegators :@driver,
|
81
|
+
:login,
|
82
|
+
:logout,
|
83
|
+
:get_organizations,
|
84
|
+
:get_organization_id_by_name,
|
85
|
+
:get_organization_by_name,
|
86
|
+
:get_organization,
|
87
|
+
:get_catalog,
|
88
|
+
:get_catalog_id_by_name,
|
89
|
+
:get_catalog_by_name,
|
90
|
+
:get_vdc,
|
91
|
+
:get_vdc_id_by_name,
|
92
|
+
:get_vdc_by_name,
|
93
|
+
:get_catalog_item,
|
94
|
+
:get_catalog_item_by_name,
|
95
|
+
:get_vapp,
|
96
|
+
:delete_vapp,
|
97
|
+
:poweroff_vapp,
|
98
|
+
:suspend_vapp,
|
99
|
+
:reboot_vapp,
|
100
|
+
:reset_vapp,
|
101
|
+
:poweron_vapp,
|
102
|
+
:create_vapp_from_template,
|
103
|
+
:compose_vapp_from_vm,
|
104
|
+
:get_vapp_template,
|
105
|
+
:set_vapp_port_forwarding_rules,
|
106
|
+
:get_vapp_port_forwarding_rules,
|
107
|
+
:get_vapp_edge_public_ip,
|
108
|
+
:upload_ovf,
|
109
|
+
:get_task,
|
110
|
+
:wait_task_completion,
|
111
|
+
:set_vapp_network_config,
|
112
|
+
:set_vm_network_config,
|
113
|
+
:set_vm_guest_customization,
|
114
|
+
:get_vm,
|
115
|
+
:send_request,
|
116
|
+
:upload_file,
|
117
|
+
:convert_vapp_status
|
120
118
|
|
121
119
|
protected
|
122
120
|
|
123
121
|
def get_api_version(host_url)
|
124
|
-
|
125
122
|
# Create a new HTTP client
|
126
123
|
clnt = HTTPClient.new
|
127
124
|
|
128
125
|
# Disable SSL cert verification
|
129
|
-
clnt.ssl_config.verify_mode=(OpenSSL::SSL::VERIFY_NONE)
|
126
|
+
clnt.ssl_config.verify_mode = (OpenSSL::SSL::VERIFY_NONE)
|
130
127
|
|
131
128
|
# Suppress SSL depth message
|
132
|
-
clnt.ssl_config.verify_callback=proc{ |ok, ctx|; true }
|
133
|
-
|
129
|
+
clnt.ssl_config.verify_callback = proc { |ok, ctx|; true }
|
130
|
+
|
134
131
|
url = "#{host_url}/api/versions"
|
135
132
|
|
136
133
|
begin
|
137
|
-
response = clnt.request(
|
134
|
+
response = clnt.request('GET', url, nil, nil, nil)
|
138
135
|
if !response.ok?
|
139
|
-
raise "Warning: unattended code #{response.status}
|
136
|
+
raise "Warning: unattended code #{response.status} " +
|
137
|
+
"#{response.reason}"
|
140
138
|
end
|
141
139
|
|
142
|
-
|
140
|
+
version_info = Nokogiri.parse(response.body)
|
143
141
|
# FIXME: Find a smarter way to check for vCloud API version
|
144
142
|
# Changed from .first to .last because that's the way it's defined
|
145
143
|
# in the request answer.
|
146
|
-
|
147
|
-
|
148
|
-
apiVersion.last.text
|
149
|
-
|
144
|
+
api_version = version_info.css('VersionInfo Version')
|
145
|
+
api_version.last.text
|
150
146
|
|
151
147
|
rescue SocketError
|
152
148
|
raise Errors::HostNotFound, :message => host_url
|
153
149
|
rescue Errno::EADDRNOTAVAIL
|
154
150
|
raise Errors::HostNotFound, :message => host_url
|
155
151
|
end
|
156
|
-
|
157
152
|
end
|
158
|
-
|
159
153
|
end
|
160
154
|
end
|
161
155
|
end
|
@@ -15,21 +15,21 @@
|
|
15
15
|
# limitations under the License.
|
16
16
|
#
|
17
17
|
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
18
|
+
require 'ruby-progressbar'
|
19
|
+
require 'set'
|
20
|
+
require 'netaddr'
|
21
21
|
|
22
22
|
module VagrantPlugins
|
23
23
|
module VCloud
|
24
24
|
module Driver
|
25
|
-
|
26
25
|
# Main class to access vCloud rest APIs
|
27
26
|
class Version_5_1 < Base
|
28
27
|
attr_reader :auth_key, :id
|
29
|
-
|
30
|
-
def initialize(host, username, password, org_name)
|
31
28
|
|
32
|
-
|
29
|
+
##
|
30
|
+
# Init the driver with the Vagrantfile information
|
31
|
+
def initialize(host, username, password, org_name)
|
32
|
+
@logger = Log4r::Logger.new('vagrant::provider::vcloud::driver_5_1')
|
33
33
|
|
34
34
|
@host = host
|
35
35
|
@api_url = "#{host}/api"
|
@@ -37,7 +37,7 @@ module VagrantPlugins
|
|
37
37
|
@username = username
|
38
38
|
@password = password
|
39
39
|
@org_name = org_name
|
40
|
-
@api_version =
|
40
|
+
@api_version = '5.1'
|
41
41
|
@id = nil
|
42
42
|
end
|
43
43
|
|
@@ -45,28 +45,29 @@ module VagrantPlugins
|
|
45
45
|
# Authenticate against the specified server
|
46
46
|
def login
|
47
47
|
params = {
|
48
|
-
'method'
|
48
|
+
'method' => :post,
|
49
49
|
'command' => '/sessions'
|
50
50
|
}
|
51
51
|
|
52
|
-
|
52
|
+
_response, headers = send_request(params)
|
53
53
|
|
54
|
-
if !headers.
|
55
|
-
raise
|
54
|
+
if !headers.key?('x-vcloud-authorization')
|
55
|
+
raise 'Failed to authenticate: ' \
|
56
|
+
'missing x-vcloud-authorization header'
|
56
57
|
end
|
57
58
|
|
58
|
-
@auth_key = headers[
|
59
|
+
@auth_key = headers['x-vcloud-authorization']
|
59
60
|
end
|
60
61
|
|
61
62
|
##
|
62
63
|
# Destroy the current session
|
63
64
|
def logout
|
64
65
|
params = {
|
65
|
-
'method'
|
66
|
+
'method' => :delete,
|
66
67
|
'command' => '/session'
|
67
68
|
}
|
68
69
|
|
69
|
-
|
70
|
+
_response, _headers = send_request(params)
|
70
71
|
# reset auth key to nil
|
71
72
|
@auth_key = nil
|
72
73
|
end
|
@@ -75,16 +76,16 @@ module VagrantPlugins
|
|
75
76
|
# Fetch existing organizations and their IDs
|
76
77
|
def get_organizations
|
77
78
|
params = {
|
78
|
-
'method'
|
79
|
+
'method' => :get,
|
79
80
|
'command' => '/org'
|
80
81
|
}
|
81
82
|
|
82
|
-
response,
|
83
|
+
response, _headers = send_request(params)
|
83
84
|
orgs = response.css('OrgList Org')
|
84
85
|
|
85
86
|
results = {}
|
86
87
|
orgs.each do |org|
|
87
|
-
results[org['name']] = org['href'].gsub("#{@api_url}/org/",
|
88
|
+
results[org['name']] = org['href'].gsub("#{@api_url}/org/", '')
|
88
89
|
end
|
89
90
|
results
|
90
91
|
end
|
@@ -96,7 +97,7 @@ module VagrantPlugins
|
|
96
97
|
result = nil
|
97
98
|
|
98
99
|
# Fetch all organizations
|
99
|
-
organizations = get_organizations
|
100
|
+
organizations = get_organizations
|
100
101
|
|
101
102
|
organizations.each do |organization|
|
102
103
|
if organization[0].downcase == name.downcase
|
@@ -106,7 +107,6 @@ module VagrantPlugins
|
|
106
107
|
result
|
107
108
|
end
|
108
109
|
|
109
|
-
|
110
110
|
##
|
111
111
|
# friendly helper method to fetch an Organization by name
|
112
112
|
# - name (this isn't case sensitive)
|
@@ -114,7 +114,7 @@ module VagrantPlugins
|
|
114
114
|
result = nil
|
115
115
|
|
116
116
|
# Fetch all organizations
|
117
|
-
organizations = get_organizations
|
117
|
+
organizations = get_organizations
|
118
118
|
|
119
119
|
organizations.each do |organization|
|
120
120
|
if organization[0].downcase == name.downcase
|
@@ -129,51 +129,77 @@ module VagrantPlugins
|
|
129
129
|
# - catalogs
|
130
130
|
# - vdcs
|
131
131
|
# - networks
|
132
|
-
def get_organization(
|
132
|
+
def get_organization(org_id)
|
133
133
|
params = {
|
134
|
-
'method'
|
135
|
-
'command' => "/org/#{
|
134
|
+
'method' => :get,
|
135
|
+
'command' => "/org/#{org_id}"
|
136
136
|
}
|
137
137
|
|
138
|
-
response,
|
138
|
+
response, _headers = send_request(params)
|
139
|
+
|
139
140
|
catalogs = {}
|
140
|
-
response.css(
|
141
|
-
|
141
|
+
response.css(
|
142
|
+
"Link[type='application/vnd.vmware.vcloud.catalog+xml']"
|
143
|
+
).each do |item|
|
144
|
+
catalogs[item['name']] = item['href'].gsub(
|
145
|
+
"#{@api_url}/catalog/", ''
|
146
|
+
)
|
142
147
|
end
|
143
148
|
|
144
149
|
vdcs = {}
|
145
|
-
response.css(
|
146
|
-
|
150
|
+
response.css(
|
151
|
+
"Link[type='application/vnd.vmware.vcloud.vdc+xml']"
|
152
|
+
).each do |item|
|
153
|
+
vdcs[item['name']] = item['href'].gsub(
|
154
|
+
"#{@api_url}/vdc/", ''
|
155
|
+
)
|
147
156
|
end
|
148
157
|
|
149
158
|
networks = {}
|
150
|
-
response.css(
|
151
|
-
|
159
|
+
response.css(
|
160
|
+
"Link[type='application/vnd.vmware.vcloud.orgNetwork+xml']"
|
161
|
+
).each do |item|
|
162
|
+
networks[item['name']] = item['href'].gsub(
|
163
|
+
"#{@api_url}/network/", ''
|
164
|
+
)
|
152
165
|
end
|
153
166
|
|
154
167
|
tasklists = {}
|
155
|
-
response.css(
|
156
|
-
|
168
|
+
response.css(
|
169
|
+
"Link[type='application/vnd.vmware.vcloud.tasksList+xml']"
|
170
|
+
).each do |item|
|
171
|
+
tasklists[item['name']] = item['href'].gsub(
|
172
|
+
"#{@api_url}/tasksList/", ''
|
173
|
+
)
|
157
174
|
end
|
158
175
|
|
159
|
-
{
|
176
|
+
{
|
177
|
+
:catalogs => catalogs,
|
178
|
+
:vdcs => vdcs,
|
179
|
+
:networks => networks,
|
180
|
+
:tasklists => tasklists
|
181
|
+
}
|
160
182
|
end
|
161
183
|
|
162
184
|
##
|
163
185
|
# Fetch details about a given catalog
|
164
|
-
def get_catalog(
|
186
|
+
def get_catalog(catalog_id)
|
165
187
|
params = {
|
166
|
-
'method'
|
167
|
-
'command' => "/catalog/#{
|
188
|
+
'method' => :get,
|
189
|
+
'command' => "/catalog/#{catalog_id}"
|
168
190
|
}
|
169
191
|
|
170
|
-
response,
|
171
|
-
description = response.css(
|
192
|
+
response, _headers = send_request(params)
|
193
|
+
description = response.css('Description').first
|
172
194
|
description = description.text unless description.nil?
|
173
195
|
|
174
196
|
items = {}
|
175
|
-
response.css(
|
176
|
-
|
197
|
+
response.css(
|
198
|
+
"CatalogItem[type='application/vnd.vmware.vcloud.catalogItem+xml']"
|
199
|
+
).each do |item|
|
200
|
+
items[item['name']] = item['href'].gsub(
|
201
|
+
"#{@api_url}/catalogItem/", ''
|
202
|
+
)
|
177
203
|
end
|
178
204
|
{ :description => description, :items => items }
|
179
205
|
end
|
@@ -182,11 +208,11 @@ module VagrantPlugins
|
|
182
208
|
# Friendly helper method to fetch an catalog id by name
|
183
209
|
# - organization hash (from get_organization/get_organization_by_name)
|
184
210
|
# - catalog name
|
185
|
-
def get_catalog_id_by_name(organization,
|
211
|
+
def get_catalog_id_by_name(organization, catalog_name)
|
186
212
|
result = nil
|
187
213
|
|
188
214
|
organization[:catalogs].each do |catalog|
|
189
|
-
if catalog[0].downcase ==
|
215
|
+
if catalog[0].downcase == catalog_name.downcase
|
190
216
|
result = catalog[1]
|
191
217
|
end
|
192
218
|
end
|
@@ -198,11 +224,11 @@ module VagrantPlugins
|
|
198
224
|
# Friendly helper method to fetch an catalog by name
|
199
225
|
# - organization hash (from get_organization/get_organization_by_name)
|
200
226
|
# - catalog name
|
201
|
-
def get_catalog_by_name(organization,
|
227
|
+
def get_catalog_by_name(organization, catalog_name)
|
202
228
|
result = nil
|
203
229
|
|
204
230
|
organization[:catalogs].each do |catalog|
|
205
|
-
if catalog[0].downcase ==
|
231
|
+
if catalog[0].downcase == catalog_name.downcase
|
206
232
|
result = get_catalog(catalog[1])
|
207
233
|
end
|
208
234
|
end
|
@@ -215,37 +241,47 @@ module VagrantPlugins
|
|
215
241
|
# - description
|
216
242
|
# - vapps
|
217
243
|
# - networks
|
218
|
-
def get_vdc(
|
244
|
+
def get_vdc(vdc_id)
|
219
245
|
params = {
|
220
|
-
'method'
|
221
|
-
'command' => "/vdc/#{
|
246
|
+
'method' => :get,
|
247
|
+
'command' => "/vdc/#{vdc_id}"
|
222
248
|
}
|
223
249
|
|
224
|
-
response,
|
225
|
-
description = response.css(
|
250
|
+
response, _headers = send_request(params)
|
251
|
+
description = response.css('Description').first
|
226
252
|
description = description.text unless description.nil?
|
227
253
|
|
228
254
|
vapps = {}
|
229
|
-
response.css(
|
230
|
-
|
255
|
+
response.css(
|
256
|
+
"ResourceEntity[type='application/vnd.vmware.vcloud.vApp+xml']"
|
257
|
+
).each do |item|
|
258
|
+
vapps[item['name']] = item['href'].gsub(
|
259
|
+
"#{@api_url}/vApp/vapp-", ''
|
260
|
+
)
|
231
261
|
end
|
232
262
|
|
233
263
|
networks = {}
|
234
|
-
response.css(
|
235
|
-
|
264
|
+
response.css(
|
265
|
+
"Network[type='application/vnd.vmware.vcloud.network+xml']"
|
266
|
+
).each do |item|
|
267
|
+
networks[item['name']] = item['href'].gsub(
|
268
|
+
"#{@api_url}/network/", ''
|
269
|
+
)
|
236
270
|
end
|
237
|
-
{
|
271
|
+
{
|
272
|
+
:description => description, :vapps => vapps, :networks => networks
|
273
|
+
}
|
238
274
|
end
|
239
275
|
|
240
276
|
##
|
241
277
|
# Friendly helper method to fetch a Organization VDC Id by name
|
242
278
|
# - Organization object
|
243
279
|
# - Organization VDC Name
|
244
|
-
def get_vdc_id_by_name(organization,
|
280
|
+
def get_vdc_id_by_name(organization, vdc_name)
|
245
281
|
result = nil
|
246
282
|
|
247
283
|
organization[:vdcs].each do |vdc|
|
248
|
-
if vdc[0].downcase ==
|
284
|
+
if vdc[0].downcase == vdc_name.downcase
|
249
285
|
result = vdc[1]
|
250
286
|
end
|
251
287
|
end
|
@@ -257,11 +293,11 @@ module VagrantPlugins
|
|
257
293
|
# Friendly helper method to fetch a Organization VDC by name
|
258
294
|
# - Organization object
|
259
295
|
# - Organization VDC Name
|
260
|
-
def get_vdc_by_name(organization,
|
296
|
+
def get_vdc_by_name(organization, vdc_name)
|
261
297
|
result = nil
|
262
298
|
|
263
299
|
organization[:vdcs].each do |vdc|
|
264
|
-
if vdc[0].downcase ==
|
300
|
+
if vdc[0].downcase == vdc_name.downcase
|
265
301
|
result = get_vdc(vdc[1])
|
266
302
|
end
|
267
303
|
end
|
@@ -273,19 +309,23 @@ module VagrantPlugins
|
|
273
309
|
# Fetch details about a given catalog item:
|
274
310
|
# - description
|
275
311
|
# - vApp templates
|
276
|
-
def get_catalog_item(
|
312
|
+
def get_catalog_item(catalog_item_id)
|
277
313
|
params = {
|
278
|
-
'method'
|
279
|
-
'command' => "/catalogItem/#{
|
314
|
+
'method' => :get,
|
315
|
+
'command' => "/catalogItem/#{catalog_item_id}"
|
280
316
|
}
|
281
317
|
|
282
|
-
response,
|
283
|
-
description = response.css(
|
318
|
+
response, _headers = send_request(params)
|
319
|
+
description = response.css('Description').first
|
284
320
|
description = description.text unless description.nil?
|
285
321
|
|
286
322
|
items = {}
|
287
|
-
response.css(
|
288
|
-
|
323
|
+
response.css(
|
324
|
+
"Entity[type='application/vnd.vmware.vcloud.vAppTemplate+xml']"
|
325
|
+
).each do |item|
|
326
|
+
items[item['name']] = item['href'].gsub(
|
327
|
+
"#{@api_url}/vAppTemplate/vappTemplate-", ''
|
328
|
+
)
|
289
329
|
end
|
290
330
|
{ :description => description, :items => items }
|
291
331
|
end
|
@@ -293,41 +333,43 @@ module VagrantPlugins
|
|
293
333
|
##
|
294
334
|
# friendly helper method to fetch an catalogItem by name
|
295
335
|
# - catalogId (use get_catalog_name(org, name))
|
296
|
-
# - catalagItemName
|
297
|
-
def get_catalog_item_by_name(
|
336
|
+
# - catalagItemName
|
337
|
+
def get_catalog_item_by_name(catalog_id, catalog_item_name)
|
298
338
|
result = nil
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
if
|
339
|
+
catalog_elems = get_catalog(catalog_id)
|
340
|
+
|
341
|
+
catalog_elems[:items].each do |catalog_elem|
|
342
|
+
|
343
|
+
catalog_item = get_catalog_item(catalog_elem[1])
|
344
|
+
if catalog_item[:items][catalog_item_name]
|
305
345
|
# This is a vApp Catalog Item
|
306
346
|
|
307
347
|
# fetch CatalogItemId
|
308
|
-
|
348
|
+
catalog_item_id = catalog_item[:items][catalog_item_name]
|
309
349
|
|
310
350
|
# Fetch the catalogItemId information
|
311
351
|
params = {
|
312
|
-
'method'
|
313
|
-
'command' => "/vAppTemplate/vappTemplate-#{
|
352
|
+
'method' => :get,
|
353
|
+
'command' => "/vAppTemplate/vappTemplate-#{catalog_item_id}"
|
314
354
|
}
|
315
|
-
response,
|
355
|
+
response, _headers = send_request(params)
|
316
356
|
|
317
|
-
# VMs Hash for all the vApp VM entities
|
357
|
+
# VMs Hash for all the vApp VM entities
|
318
358
|
vms_hash = {}
|
319
|
-
response.css(
|
320
|
-
|
321
|
-
|
322
|
-
|
359
|
+
response.css('/VAppTemplate/Children/Vm').each do |vm_elem|
|
360
|
+
vm_name = vm_elem['name']
|
361
|
+
vm_id = vm_elem['href'].gsub("#{@api_url}/vAppTemplate/vm-", '')
|
362
|
+
|
323
363
|
# Add the VM name/id to the VMs Hash
|
324
|
-
vms_hash[
|
364
|
+
vms_hash[vm_name] = { :id => vm_id }
|
325
365
|
end
|
326
|
-
|
366
|
+
result = {
|
367
|
+
catalog_item_name => catalog_item_id, :vms_hash => vms_hash
|
368
|
+
}
|
327
369
|
end
|
328
370
|
end
|
329
|
-
result
|
330
|
-
end
|
371
|
+
result
|
372
|
+
end
|
331
373
|
|
332
374
|
##
|
333
375
|
# Fetch details about a given vapp:
|
@@ -339,13 +381,13 @@ module VagrantPlugins
|
|
339
381
|
# -- IP addresses
|
340
382
|
# -- status
|
341
383
|
# -- ID
|
342
|
-
def get_vapp(
|
384
|
+
def get_vapp(vapp_id)
|
343
385
|
params = {
|
344
|
-
'method'
|
345
|
-
'command' => "/vApp/vapp-#{
|
386
|
+
'method' => :get,
|
387
|
+
'command' => "/vApp/vapp-#{vapp_id}"
|
346
388
|
}
|
347
389
|
|
348
|
-
response,
|
390
|
+
response, _headers = send_request(params)
|
349
391
|
|
350
392
|
vapp_node = response.css('VApp').first
|
351
393
|
if vapp_node
|
@@ -353,7 +395,7 @@ module VagrantPlugins
|
|
353
395
|
status = convert_vapp_status(vapp_node['status'])
|
354
396
|
end
|
355
397
|
|
356
|
-
description = response.css(
|
398
|
+
description = response.css('Description').first
|
357
399
|
description = description.text unless description.nil?
|
358
400
|
|
359
401
|
ip = response.css('IpAddress').first
|
@@ -362,67 +404,78 @@ module VagrantPlugins
|
|
362
404
|
vms = response.css('Children Vm')
|
363
405
|
vms_hash = {}
|
364
406
|
|
365
|
-
# ipAddress could be namespaced or not:
|
407
|
+
# ipAddress could be namespaced or not:
|
408
|
+
# see https://github.com/astratto/vcloud-rest/issues/3
|
366
409
|
vms.each do |vm|
|
367
410
|
vapp_local_id = vm.css('VAppScopedLocalId')
|
368
|
-
addresses = vm.css('rasd|Connection').collect{
|
411
|
+
addresses = vm.css('rasd|Connection').collect {
|
412
|
+
|n| n['vcloud:ipAddress'] || n['ipAddress']
|
413
|
+
}
|
369
414
|
vms_hash[vm['name'].to_sym] = {
|
370
|
-
:addresses
|
371
|
-
:status
|
372
|
-
:id
|
415
|
+
:addresses => addresses,
|
416
|
+
:status => convert_vapp_status(vm['status']),
|
417
|
+
:id => vm['href'].gsub("#{@api_url}/vApp/vm-", ''),
|
373
418
|
:vapp_scoped_local_id => vapp_local_id.text
|
374
419
|
}
|
375
420
|
end
|
376
421
|
|
377
422
|
# TODO: EXPAND INFO FROM RESPONSE
|
378
|
-
{
|
423
|
+
{
|
424
|
+
:name => name,
|
425
|
+
:description => description,
|
426
|
+
:status => status,
|
427
|
+
:ip => ip,
|
428
|
+
:vms_hash => vms_hash
|
429
|
+
}
|
379
430
|
end
|
380
431
|
|
381
432
|
##
|
382
433
|
# Delete a given vapp
|
383
434
|
# NOTE: It doesn't verify that the vapp is shutdown
|
384
|
-
def delete_vapp(
|
435
|
+
def delete_vapp(vapp_id)
|
385
436
|
params = {
|
386
|
-
'method'
|
387
|
-
'command' => "/vApp/vapp-#{
|
437
|
+
'method' => :delete,
|
438
|
+
'command' => "/vApp/vapp-#{vapp_id}"
|
388
439
|
}
|
389
440
|
|
390
|
-
|
391
|
-
task_id = headers[
|
441
|
+
_response, headers = send_request(params)
|
442
|
+
task_id = headers['Location'].gsub("#{@api_url}/task/", '')
|
392
443
|
task_id
|
393
444
|
end
|
394
445
|
|
395
446
|
##
|
396
447
|
# Shutdown a given vapp
|
397
|
-
def poweroff_vapp(
|
448
|
+
def poweroff_vapp(vapp_id)
|
398
449
|
builder = Nokogiri::XML::Builder.new do |xml|
|
399
|
-
|
400
|
-
|
401
|
-
xml.UndeployPowerAction 'powerOff'
|
402
|
-
}
|
450
|
+
xml.UndeployVAppParams(
|
451
|
+
'xmlns' => 'http://www.vmware.com/vcloud/v1.5'
|
452
|
+
) { xml.UndeployPowerAction 'powerOff' }
|
403
453
|
end
|
404
454
|
|
405
455
|
params = {
|
406
|
-
'method'
|
407
|
-
'command' => "/vApp/vapp-#{
|
456
|
+
'method' => :post,
|
457
|
+
'command' => "/vApp/vapp-#{vapp_id}/action/undeploy"
|
408
458
|
}
|
409
459
|
|
410
|
-
|
411
|
-
|
412
|
-
|
460
|
+
_response, headers = send_request(
|
461
|
+
params,
|
462
|
+
builder.to_xml,
|
463
|
+
'application/vnd.vmware.vcloud.undeployVAppParams+xml'
|
464
|
+
)
|
465
|
+
task_id = headers['Location'].gsub("#{@api_url}/task/", '')
|
413
466
|
task_id
|
414
467
|
end
|
415
468
|
|
416
469
|
##
|
417
470
|
# Suspend a given vapp
|
418
|
-
def suspend_vapp(
|
471
|
+
def suspend_vapp(vapp_id)
|
419
472
|
params = {
|
420
|
-
'method'
|
421
|
-
'command' => "/vApp/vapp-#{
|
473
|
+
'method' => :post,
|
474
|
+
'command' => "/vApp/vapp-#{vapp_id}/power/action/suspend"
|
422
475
|
}
|
423
476
|
|
424
|
-
|
425
|
-
task_id = headers[
|
477
|
+
_response, headers = send_request(params)
|
478
|
+
task_id = headers['Location'].gsub("#{@api_url}/task/", '')
|
426
479
|
task_id
|
427
480
|
end
|
428
481
|
|
@@ -431,14 +484,14 @@ module VagrantPlugins
|
|
431
484
|
# This will basically initial a guest OS reboot, and will only work if
|
432
485
|
# VMware-tools are installed on the underlying VMs.
|
433
486
|
# vShield Edge devices are not affected
|
434
|
-
def reboot_vapp(
|
487
|
+
def reboot_vapp(vapp_id)
|
435
488
|
params = {
|
436
|
-
'method'
|
437
|
-
'command' => "/vApp/vapp-#{
|
489
|
+
'method' => :post,
|
490
|
+
'command' => "/vApp/vapp-#{vapp_id}/power/action/reboot"
|
438
491
|
}
|
439
492
|
|
440
|
-
|
441
|
-
task_id = headers[
|
493
|
+
_response, headers = send_request(params)
|
494
|
+
task_id = headers['Location'].gsub("#{@api_url}/task/", '')
|
442
495
|
task_id
|
443
496
|
end
|
444
497
|
|
@@ -446,27 +499,27 @@ module VagrantPlugins
|
|
446
499
|
# reset a given vapp
|
447
500
|
# This will basically reset the VMs within the vApp
|
448
501
|
# vShield Edge devices are not affected.
|
449
|
-
def reset_vapp(
|
502
|
+
def reset_vapp(vapp_id)
|
450
503
|
params = {
|
451
|
-
'method'
|
452
|
-
'command' => "/vApp/vapp-#{
|
504
|
+
'method' => :post,
|
505
|
+
'command' => "/vApp/vapp-#{vapp_id}/power/action/reset"
|
453
506
|
}
|
454
507
|
|
455
|
-
|
456
|
-
task_id = headers[
|
508
|
+
_response, headers = send_request(params)
|
509
|
+
task_id = headers['Location'].gsub("#{@api_url}/task/", '')
|
457
510
|
task_id
|
458
511
|
end
|
459
512
|
|
460
513
|
##
|
461
514
|
# Boot a given vapp
|
462
|
-
def poweron_vapp(
|
515
|
+
def poweron_vapp(vapp_id)
|
463
516
|
params = {
|
464
|
-
'method'
|
465
|
-
'command' => "/vApp/vapp-#{
|
517
|
+
'method' => :post,
|
518
|
+
'command' => "/vApp/vapp-#{vapp_id}/power/action/powerOn"
|
466
519
|
}
|
467
520
|
|
468
|
-
|
469
|
-
task_id = headers[
|
521
|
+
_response, headers = send_request(params)
|
522
|
+
task_id = headers['Location'].gsub("#{@api_url}/task/", '')
|
470
523
|
task_id
|
471
524
|
end
|
472
525
|
|
@@ -474,58 +527,62 @@ module VagrantPlugins
|
|
474
527
|
##
|
475
528
|
# Delete a given vm
|
476
529
|
# NOTE: It doesn't verify that the vm is shutdown
|
477
|
-
def delete_vm(
|
530
|
+
def delete_vm(vm_id)
|
478
531
|
params = {
|
479
|
-
'method'
|
480
|
-
'command' => "/vApp/vm-#{
|
532
|
+
'method' => :delete,
|
533
|
+
'command' => "/vApp/vm-#{vm_id}"
|
481
534
|
}
|
482
535
|
|
483
|
-
|
484
|
-
task_id = headers[
|
536
|
+
_response, headers = send_request(params)
|
537
|
+
task_id = headers['Location'].gsub("#{@api_url}/task/", '')
|
485
538
|
task_id
|
486
539
|
end
|
487
540
|
|
488
541
|
##
|
489
542
|
# Shutdown a given VM
|
490
|
-
# Using undeploy as a REAL powerOff
|
543
|
+
# Using undeploy as a REAL powerOff
|
491
544
|
# Only poweroff will put the VM into a partially powered off state.
|
492
|
-
def poweroff_vm(
|
545
|
+
def poweroff_vm(vm_id)
|
493
546
|
builder = Nokogiri::XML::Builder.new do |xml|
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
}
|
547
|
+
xml.UndeployVAppParams(
|
548
|
+
'xmlns' => 'http://www.vmware.com/vcloud/v1.5'
|
549
|
+
) { xml.UndeployPowerAction 'powerOff' }
|
498
550
|
end
|
499
551
|
|
500
552
|
params = {
|
501
|
-
'method'
|
502
|
-
'command' => "/vApp/vm-#{
|
553
|
+
'method' => :post,
|
554
|
+
'command' => "/vApp/vm-#{vm_id}/action/undeploy"
|
503
555
|
}
|
504
556
|
|
505
|
-
|
506
|
-
|
507
|
-
|
557
|
+
_response, headers = send_request(
|
558
|
+
params,
|
559
|
+
builder.to_xml,
|
560
|
+
'application/vnd.vmware.vcloud.undeployVAppParams+xml'
|
561
|
+
)
|
562
|
+
task_id = headers['Location'].gsub("#{@api_url}/task/", '')
|
508
563
|
task_id
|
509
564
|
end
|
510
565
|
|
511
566
|
##
|
512
567
|
# Suspend a given VM
|
513
|
-
def suspend_vm(
|
568
|
+
def suspend_vm(vm_id)
|
514
569
|
builder = Nokogiri::XML::Builder.new do |xml|
|
515
|
-
|
516
|
-
|
517
|
-
xml.UndeployPowerAction 'suspend'
|
518
|
-
}
|
570
|
+
xml.UndeployVAppParams(
|
571
|
+
'xmlns' => 'http://www.vmware.com/vcloud/v1.5'
|
572
|
+
) { xml.UndeployPowerAction 'suspend' }
|
519
573
|
end
|
520
574
|
|
521
575
|
params = {
|
522
|
-
'method'
|
523
|
-
'command' => "/vApp/vm-#{
|
576
|
+
'method' => :post,
|
577
|
+
'command' => "/vApp/vm-#{vm_id}/action/undeploy"
|
524
578
|
}
|
525
579
|
|
526
|
-
|
527
|
-
|
528
|
-
|
580
|
+
_response, headers = send_request(
|
581
|
+
params,
|
582
|
+
builder.to_xml,
|
583
|
+
'application/vnd.vmware.vcloud.undeployVAppParams+xml'
|
584
|
+
)
|
585
|
+
task_id = headers['Location'].gsub("#{@api_url}/task/", '')
|
529
586
|
task_id
|
530
587
|
end
|
531
588
|
|
@@ -534,14 +591,14 @@ module VagrantPlugins
|
|
534
591
|
# This will basically initial a guest OS reboot, and will only work if
|
535
592
|
# VMware-tools are installed on the underlying VMs.
|
536
593
|
# vShield Edge devices are not affected
|
537
|
-
def reboot_vm(
|
594
|
+
def reboot_vm(vm_id)
|
538
595
|
params = {
|
539
|
-
'method'
|
540
|
-
'command' => "/vApp/vm-#{
|
596
|
+
'method' => :post,
|
597
|
+
'command' => "/vApp/vm-#{vm_id}/power/action/reboot"
|
541
598
|
}
|
542
599
|
|
543
|
-
|
544
|
-
task_id = headers[
|
600
|
+
_response, headers = send_request(params)
|
601
|
+
task_id = headers['Location'].gsub("#{@api_url}/task/", '')
|
545
602
|
task_id
|
546
603
|
end
|
547
604
|
|
@@ -549,77 +606,74 @@ module VagrantPlugins
|
|
549
606
|
# reset a given VM
|
550
607
|
# This will basically reset the VMs within the vApp
|
551
608
|
# vShield Edge devices are not affected.
|
552
|
-
def reset_vm(
|
609
|
+
def reset_vm(vm_id)
|
553
610
|
params = {
|
554
|
-
'method'
|
555
|
-
'command' => "/vApp/vm-#{
|
611
|
+
'method' => :post,
|
612
|
+
'command' => "/vApp/vm-#{vm_id}/power/action/reset"
|
556
613
|
}
|
557
614
|
|
558
|
-
|
559
|
-
task_id = headers[
|
615
|
+
_response, headers = send_request(params)
|
616
|
+
task_id = headers['Location'].gsub("#{@api_url}/task/", '')
|
560
617
|
task_id
|
561
618
|
end
|
562
619
|
|
563
620
|
##
|
564
621
|
# Boot a given VM
|
565
|
-
def poweron_vm(
|
622
|
+
def poweron_vm(vm_id)
|
566
623
|
params = {
|
567
|
-
'method'
|
568
|
-
'command' => "/vApp/vm-#{
|
624
|
+
'method' => :post,
|
625
|
+
'command' => "/vApp/vm-#{vm_id}/power/action/powerOn"
|
569
626
|
}
|
570
627
|
|
571
|
-
|
572
|
-
task_id = headers[
|
628
|
+
_response, headers = send_request(params)
|
629
|
+
task_id = headers['Location'].gsub("#{@api_url}/task/", '')
|
573
630
|
task_id
|
574
631
|
end
|
575
632
|
|
576
|
-
### End Of VM operations ###
|
577
|
-
|
578
|
-
|
579
|
-
|
580
633
|
##
|
581
634
|
# Boot a given vm
|
582
|
-
def poweron_vm(
|
635
|
+
def poweron_vm(vm_id)
|
583
636
|
params = {
|
584
|
-
'method'
|
585
|
-
'command' => "/vApp/vm-#{
|
637
|
+
'method' => :post,
|
638
|
+
'command' => "/vApp/vm-#{vm_id}/power/action/powerOn"
|
586
639
|
}
|
587
640
|
|
588
|
-
|
589
|
-
task_id = headers[
|
641
|
+
_response, headers = send_request(params)
|
642
|
+
task_id = headers['Location'].gsub("#{@api_url}/task/", '')
|
590
643
|
task_id
|
591
644
|
end
|
592
645
|
|
593
|
-
|
594
646
|
##
|
595
647
|
# Create a catalog in an organization
|
596
|
-
def create_catalog(
|
648
|
+
def create_catalog(org_id, catalog_name, catalog_description)
|
597
649
|
builder = Nokogiri::XML::Builder.new do |xml|
|
650
|
+
xml.AdminCatalog(
|
651
|
+
'xmlns' => 'http://www.vmware.com/vcloud/v1.5',
|
652
|
+
'name' => catalog_name
|
653
|
+
) { xml.Description catalog_description }
|
598
654
|
|
599
|
-
xml.AdminCatalog(
|
600
|
-
"xmlns" => "http://www.vmware.com/vcloud/v1.5",
|
601
|
-
"name" => catalogName
|
602
|
-
) {
|
603
|
-
xml.Description catalogDescription
|
604
|
-
}
|
605
|
-
|
606
655
|
end
|
607
656
|
|
608
657
|
params = {
|
609
|
-
'method'
|
610
|
-
'command' => "/admin/org/#{
|
611
|
-
|
658
|
+
'method' => :post,
|
659
|
+
'command' => "/admin/org/#{org_id}/catalogs"
|
612
660
|
}
|
613
661
|
|
614
|
-
response,
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
662
|
+
response, _headers = send_request(
|
663
|
+
params,
|
664
|
+
builder.to_xml,
|
665
|
+
'application/vnd.vmware.admin.catalog+xml'
|
666
|
+
)
|
667
|
+
task_id = response.css(
|
668
|
+
"AdminCatalog Tasks Task[operationName='catalogCreateCatalog']"
|
669
|
+
).first[:href].gsub("#{@api_url}/task/", '')
|
621
670
|
|
671
|
+
catalog_id = response.css(
|
672
|
+
"AdminCatalog Link [type='application/vnd.vmware.vcloud.catalog+xml']"
|
673
|
+
).first[:href].gsub("#{@api_url}/catalog/", '')
|
622
674
|
|
675
|
+
{ :task_id => task_id, :catalog_id => catalog_id }
|
676
|
+
end
|
623
677
|
|
624
678
|
##
|
625
679
|
# Create a vapp starting from a template
|
@@ -629,31 +683,39 @@ module VagrantPlugins
|
|
629
683
|
# - vapp_name: name of the target vapp
|
630
684
|
# - vapp_description: description of the target vapp
|
631
685
|
# - vapp_templateid: ID of the vapp template
|
632
|
-
def create_vapp_from_template(vdc, vapp_name, vapp_description,
|
686
|
+
def create_vapp_from_template(vdc, vapp_name, vapp_description, vapp_template_id, poweron = false)
|
633
687
|
builder = Nokogiri::XML::Builder.new do |xml|
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
xml.Description vapp_description
|
642
|
-
|
643
|
-
|
688
|
+
xml.InstantiateVAppTemplateParams(
|
689
|
+
'xmlns' => 'http://www.vmware.com/vcloud/v1.5',
|
690
|
+
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
|
691
|
+
'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1',
|
692
|
+
'name' => vapp_name,
|
693
|
+
'deploy' => 'true',
|
694
|
+
'powerOn' => poweron
|
695
|
+
) { xml.Description vapp_description xml.Source(
|
696
|
+
'href' => "#{@api_url}/vAppTemplate/#{vapp_template_id}"
|
697
|
+
)
|
698
|
+
}
|
644
699
|
end
|
645
700
|
|
646
701
|
params = {
|
647
|
-
|
648
|
-
|
702
|
+
'method' => :post,
|
703
|
+
'command' => "/vdc/#{vdc}/action/instantiateVAppTemplate"
|
649
704
|
}
|
650
705
|
|
651
|
-
response, headers = send_request(
|
706
|
+
response, headers = send_request(
|
707
|
+
params,
|
708
|
+
builder.to_xml,
|
709
|
+
'application/vnd.vmware.vcloud.instantiateVAppTemplateParams+xml'
|
710
|
+
)
|
711
|
+
|
712
|
+
vapp_id = headers['Location'].gsub("#{@api_url}/vApp/vapp-", '')
|
652
713
|
|
653
|
-
|
714
|
+
task = response.css(
|
715
|
+
"VApp Task[operationName='vdcInstantiateVapp']"
|
716
|
+
).first
|
654
717
|
|
655
|
-
|
656
|
-
task_id = task["href"].gsub("#{@api_url}/task/", "")
|
718
|
+
task_id = task['href'].gsub("#{@api_url}/task/", '')
|
657
719
|
|
658
720
|
{ :vapp_id => vapp_id, :task_id => task_id }
|
659
721
|
end
|
@@ -665,25 +727,26 @@ module VagrantPlugins
|
|
665
727
|
# - vdc: the associated VDC
|
666
728
|
# - vapp_name: name of the target vapp
|
667
729
|
# - vapp_description: description of the target vapp
|
668
|
-
# - vm_list: hash with IDs of the VMs
|
730
|
+
# - vm_list: hash with IDs of the VMs used in the composing process
|
669
731
|
# - network_config: hash of the network configuration for the vapp
|
670
|
-
def compose_vapp_from_vm(vdc, vapp_name, vapp_description, vm_list={}, network_config={})
|
732
|
+
def compose_vapp_from_vm(vdc, vapp_name, vapp_description, vm_list = {}, network_config = {})
|
671
733
|
builder = Nokogiri::XML::Builder.new do |xml|
|
672
734
|
xml.ComposeVAppParams(
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
735
|
+
'xmlns' => 'http://www.vmware.com/vcloud/v1.5',
|
736
|
+
'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1',
|
737
|
+
'name' => vapp_name,
|
738
|
+
'deploy' => 'false',
|
739
|
+
'powerOn' => 'false') {
|
678
740
|
xml.Description vapp_description
|
679
741
|
xml.InstantiationParams {
|
680
742
|
xml.NetworkConfigSection {
|
681
|
-
xml['ovf'].Info
|
682
|
-
xml.NetworkConfig(
|
743
|
+
xml['ovf'].Info 'Configuration parameters for logical networks'
|
744
|
+
xml.NetworkConfig('networkName' => network_config[:name]) {
|
683
745
|
xml.Configuration {
|
684
|
-
|
746
|
+
if network_config[:fence_mode] != 'bridged'
|
747
|
+
xml.IpScopes {
|
685
748
|
xml.IpScope {
|
686
|
-
xml.IsInherited(network_config[:is_inherited] ||
|
749
|
+
xml.IsInherited(network_config[:is_inherited] || 'false')
|
687
750
|
xml.Gateway network_config[:gateway]
|
688
751
|
xml.Netmask network_config[:netmask]
|
689
752
|
xml.Dns1 network_config[:dns1] if network_config[:dns1]
|
@@ -693,67 +756,72 @@ module VagrantPlugins
|
|
693
756
|
xml.IpRange {
|
694
757
|
xml.StartAddress network_config[:start_address]
|
695
758
|
xml.EndAddress network_config[:end_address]
|
759
|
+
}
|
696
760
|
}
|
697
761
|
}
|
698
762
|
}
|
699
|
-
|
763
|
+
end
|
700
764
|
xml.ParentNetwork("href" => "#{@api_url}/network/#{network_config[:parent_network]}")
|
701
765
|
xml.FenceMode network_config[:fence_mode]
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
766
|
+
if network_config[:fence_mode] != 'bridged'
|
767
|
+
xml.Features {
|
768
|
+
xml.FirewallService {
|
769
|
+
xml.IsEnabled(network_config[:enable_firewall] || "false")
|
770
|
+
}
|
771
|
+
xml.NatService {
|
772
|
+
xml.IsEnabled "true"
|
773
|
+
xml.NatType "portForwarding"
|
774
|
+
xml.Policy(network_config[:nat_policy_type] || "allowTraffic")
|
775
|
+
}
|
711
776
|
}
|
712
|
-
|
777
|
+
end
|
713
778
|
}
|
714
779
|
}
|
715
780
|
}
|
716
781
|
}
|
717
782
|
vm_list.each do |vm_name, vm_id|
|
718
783
|
xml.SourcedItem {
|
719
|
-
xml.Source(
|
784
|
+
xml.Source('href' => "#{@api_url}/vAppTemplate/vm-#{vm_id}", 'name' => vm_name)
|
720
785
|
xml.InstantiationParams {
|
721
786
|
xml.NetworkConnectionSection(
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
xml['ovf'].Info
|
726
|
-
xml.PrimaryNetworkConnectionIndex
|
727
|
-
xml.NetworkConnection(
|
728
|
-
xml.NetworkConnectionIndex
|
729
|
-
xml.IsConnected
|
730
|
-
xml.IpAddressAllocationMode(network_config[:ip_allocation_mode] ||
|
787
|
+
'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1',
|
788
|
+
'type' => 'application/vnd.vmware.vcloud.networkConnectionSection+xml',
|
789
|
+
'href' => "#{@api_url}/vAppTemplate/vm-#{vm_id}/networkConnectionSection/") {
|
790
|
+
xml['ovf'].Info 'Network config for sourced item'
|
791
|
+
xml.PrimaryNetworkConnectionIndex '0'
|
792
|
+
xml.NetworkConnection('network' => network_config[:name]) {
|
793
|
+
xml.NetworkConnectionIndex '0'
|
794
|
+
xml.IsConnected 'true'
|
795
|
+
xml.IpAddressAllocationMode(network_config[:ip_allocation_mode] || 'POOL')
|
731
796
|
}
|
732
797
|
}
|
733
798
|
}
|
734
|
-
xml.NetworkAssignment(
|
799
|
+
xml.NetworkAssignment('containerNetwork' => network_config[:name], 'innerNetwork' => network_config[:name])
|
735
800
|
}
|
736
801
|
end
|
737
|
-
xml.AllEULAsAccepted
|
802
|
+
xml.AllEULAsAccepted 'true'
|
738
803
|
}
|
739
804
|
end
|
740
805
|
|
741
806
|
params = {
|
742
|
-
|
743
|
-
|
807
|
+
'method' => :post,
|
808
|
+
'command' => "/vdc/#{vdc}/action/composeVApp"
|
744
809
|
}
|
745
810
|
|
746
|
-
response, headers = send_request(
|
811
|
+
response, headers = send_request(
|
812
|
+
params,
|
813
|
+
builder.to_xml,
|
814
|
+
'application/vnd.vmware.vcloud.composeVAppParams+xml'
|
815
|
+
)
|
747
816
|
|
748
|
-
vapp_id = headers[
|
817
|
+
vapp_id = headers['Location'].gsub("#{@api_url}/vApp/vapp-", '')
|
749
818
|
|
750
819
|
task = response.css("VApp Task[operationName='vdcComposeVapp']").first
|
751
|
-
task_id = task[
|
820
|
+
task_id = task['href'].gsub("#{@api_url}/task/", '')
|
752
821
|
|
753
822
|
{ :vapp_id => vapp_id, :task_id => task_id }
|
754
823
|
end
|
755
824
|
|
756
|
-
|
757
825
|
##
|
758
826
|
# Recompose an existing vapp using existing virtual machines
|
759
827
|
#
|
@@ -764,82 +832,84 @@ module VagrantPlugins
|
|
764
832
|
# - vm_list: hash with IDs of the VMs to be used in the composing process
|
765
833
|
# - network_config: hash of the network configuration for the vapp
|
766
834
|
|
767
|
-
def recompose_vapp_from_vm(
|
768
|
-
|
835
|
+
def recompose_vapp_from_vm(vapp_id, vm_list = {}, network_config = {})
|
836
|
+
original_vapp = get_vapp(vapp_id)
|
769
837
|
|
770
838
|
builder = Nokogiri::XML::Builder.new do |xml|
|
771
839
|
xml.RecomposeVAppParams(
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
xml.Description
|
840
|
+
'xmlns' => 'http://www.vmware.com/vcloud/v1.5',
|
841
|
+
'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1',
|
842
|
+
'name' => original_vapp[:name]) {
|
843
|
+
xml.Description original_vapp[:description]
|
776
844
|
xml.InstantiationParams {}
|
777
845
|
vm_list.each do |vm_name, vm_id|
|
778
846
|
xml.SourcedItem {
|
779
|
-
xml.Source(
|
847
|
+
xml.Source('href' => "#{@api_url}/vAppTemplate/vm-#{vm_id}", 'name' => vm_name)
|
780
848
|
xml.InstantiationParams {
|
781
849
|
xml.NetworkConnectionSection(
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
xml['ovf'].Info
|
786
|
-
xml.PrimaryNetworkConnectionIndex
|
787
|
-
xml.NetworkConnection(
|
788
|
-
xml.NetworkConnectionIndex
|
789
|
-
xml.IsConnected
|
790
|
-
xml.IpAddressAllocationMode(network_config[:ip_allocation_mode] ||
|
850
|
+
'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1',
|
851
|
+
'type' => 'application/vnd.vmware.vcloud.networkConnectionSection+xml',
|
852
|
+
'href' => "#{@api_url}/vAppTemplate/vm-#{vm_id}/networkConnectionSection/") {
|
853
|
+
xml['ovf'].Info 'Network config for sourced item'
|
854
|
+
xml.PrimaryNetworkConnectionIndex '0'
|
855
|
+
xml.NetworkConnection('network' => network_config[:name]) {
|
856
|
+
xml.NetworkConnectionIndex '0'
|
857
|
+
xml.IsConnected 'true'
|
858
|
+
xml.IpAddressAllocationMode(network_config[:ip_allocation_mode] || 'POOL')
|
791
859
|
}
|
792
860
|
}
|
793
861
|
}
|
794
|
-
xml.NetworkAssignment(
|
862
|
+
xml.NetworkAssignment('containerNetwork' => network_config[:name], 'innerNetwork' => network_config[:name])
|
795
863
|
}
|
796
864
|
end
|
797
|
-
xml.AllEULAsAccepted
|
865
|
+
xml.AllEULAsAccepted 'true'
|
798
866
|
}
|
799
867
|
end
|
800
868
|
|
801
869
|
params = {
|
802
|
-
|
803
|
-
|
870
|
+
'method' => :post,
|
871
|
+
'command' => "/vApp/vapp-#{vapp_id}/action/recomposeVApp"
|
804
872
|
}
|
805
873
|
|
806
|
-
response, headers = send_request(
|
874
|
+
response, headers = send_request(
|
875
|
+
params,
|
876
|
+
builder.to_xml,
|
877
|
+
'application/vnd.vmware.vcloud.recomposeVAppParams+xml'
|
878
|
+
)
|
807
879
|
|
808
|
-
vapp_id = headers[
|
880
|
+
vapp_id = headers['Location'].gsub("#{@api_url}/vApp/vapp-", '')
|
809
881
|
|
810
882
|
task = response.css("Task [operationName='vdcRecomposeVapp']").first
|
811
|
-
task_id = task[
|
883
|
+
task_id = task['href'].gsub("#{@api_url}/task/", '')
|
812
884
|
|
813
885
|
{ :vapp_id => vapp_id, :task_id => task_id }
|
814
886
|
end
|
815
887
|
|
816
|
-
|
817
|
-
|
818
|
-
|
819
888
|
# Fetch details about a given vapp template:
|
820
889
|
# - name
|
821
890
|
# - description
|
822
891
|
# - Children VMs:
|
823
892
|
# -- ID
|
824
|
-
def get_vapp_template(
|
893
|
+
def get_vapp_template(vapp_id)
|
825
894
|
params = {
|
826
|
-
'method'
|
827
|
-
'command' => "/vAppTemplate/vappTemplate-#{
|
895
|
+
'method' => :get,
|
896
|
+
'command' => "/vAppTemplate/vappTemplate-#{vapp_id}"
|
828
897
|
}
|
829
898
|
|
830
|
-
response,
|
899
|
+
response, _headers = send_request(params)
|
831
900
|
|
832
901
|
vapp_node = response.css('VAppTemplate').first
|
833
902
|
if vapp_node
|
834
903
|
name = vapp_node['name']
|
835
|
-
|
904
|
+
convert_vapp_status(vapp_node['status'])
|
836
905
|
end
|
837
906
|
|
838
|
-
description = response.css(
|
907
|
+
description = response.css('Description').first
|
839
908
|
description = description.text unless description.nil?
|
840
909
|
|
841
|
-
|
842
|
-
ip =
|
910
|
+
# FIXME: What are those 2 lines for ? disabling for now (tsugliani)
|
911
|
+
# ip = response.css('IpAddress').first
|
912
|
+
# ip = ip.text unless ip.nil?
|
843
913
|
|
844
914
|
vms = response.css('Children Vm')
|
845
915
|
vms_hash = {}
|
@@ -857,32 +927,32 @@ module VagrantPlugins
|
|
857
927
|
##
|
858
928
|
# Set vApp port forwarding rules
|
859
929
|
#
|
860
|
-
# -
|
930
|
+
# - vapp_id: id of the vapp to be modified
|
861
931
|
# - network_name: name of the vapp network to be modified
|
862
932
|
# - config: hash with network configuration specifications, must contain an array inside :nat_rules with the nat rules to be applied.
|
863
|
-
def set_vapp_port_forwarding_rules(
|
933
|
+
def set_vapp_port_forwarding_rules(vapp_id, network_name, config = {})
|
864
934
|
builder = Nokogiri::XML::Builder.new do |xml|
|
865
935
|
xml.NetworkConfigSection(
|
866
|
-
|
867
|
-
|
868
|
-
xml['ovf'].Info
|
869
|
-
xml.NetworkConfig(
|
936
|
+
'xmlns' => 'http://www.vmware.com/vcloud/v1.5',
|
937
|
+
'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1') {
|
938
|
+
xml['ovf'].Info 'Network configuration'
|
939
|
+
xml.NetworkConfig('networkName' => network_name) {
|
870
940
|
xml.Configuration {
|
871
|
-
xml.ParentNetwork(
|
941
|
+
xml.ParentNetwork('href' => "#{@api_url}/network/#{config[:parent_network]}")
|
872
942
|
xml.FenceMode(config[:fence_mode] || 'isolated')
|
873
943
|
xml.Features {
|
874
944
|
xml.NatService {
|
875
|
-
xml.IsEnabled
|
876
|
-
xml.NatType
|
877
|
-
xml.Policy(config[:nat_policy_type] ||
|
945
|
+
xml.IsEnabled 'true'
|
946
|
+
xml.NatType 'portForwarding'
|
947
|
+
xml.Policy(config[:nat_policy_type] || 'allowTraffic')
|
878
948
|
config[:nat_rules].each do |nat_rule|
|
879
949
|
xml.NatRule {
|
880
950
|
xml.VmRule {
|
881
951
|
xml.ExternalPort nat_rule[:nat_external_port]
|
882
952
|
xml.VAppScopedVmId nat_rule[:vapp_scoped_local_id]
|
883
|
-
xml.VmNicId(nat_rule[:nat_vmnic_id] ||
|
953
|
+
xml.VmNicId(nat_rule[:nat_vmnic_id] || '0')
|
884
954
|
xml.InternalPort nat_rule[:nat_internal_port]
|
885
|
-
xml.Protocol(nat_rule[:nat_protocol] ||
|
955
|
+
xml.Protocol(nat_rule[:nat_protocol] || 'TCP')
|
886
956
|
}
|
887
957
|
}
|
888
958
|
end
|
@@ -894,352 +964,414 @@ module VagrantPlugins
|
|
894
964
|
end
|
895
965
|
|
896
966
|
params = {
|
897
|
-
'method'
|
898
|
-
'command' => "/vApp/vapp-#{
|
967
|
+
'method' => :put,
|
968
|
+
'command' => "/vApp/vapp-#{vapp_id}/networkConfigSection"
|
899
969
|
}
|
900
970
|
|
901
|
-
|
971
|
+
_response, headers = send_request(
|
972
|
+
params,
|
973
|
+
builder.to_xml,
|
974
|
+
'application/vnd.vmware.vcloud.networkConfigSection+xml'
|
975
|
+
)
|
902
976
|
|
903
|
-
task_id = headers[
|
977
|
+
task_id = headers['Location'].gsub("#{@api_url}/task/", '')
|
904
978
|
task_id
|
905
979
|
end
|
906
980
|
|
907
981
|
##
|
908
982
|
# Add vApp port forwarding rules
|
909
983
|
#
|
910
|
-
# -
|
984
|
+
# - vapp_id: id of the vapp to be modified
|
911
985
|
# - network_name: name of the vapp network to be modified
|
912
|
-
# - config: hash with network configuration specifications,
|
913
|
-
|
914
|
-
#
|
915
|
-
|
916
|
-
|
986
|
+
# - config: hash with network configuration specifications,
|
987
|
+
# must contain an array inside :nat_rules with the nat rules to add.
|
988
|
+
# nat_rules << {
|
989
|
+
# :nat_external_port => j.to_s,
|
990
|
+
# :nat_internal_port => "22",
|
991
|
+
# :nat_protocol => "TCP",
|
992
|
+
# :vm_scoped_local_id => value[:vapp_scoped_local_id]
|
993
|
+
# }
|
994
|
+
|
995
|
+
def add_vapp_port_forwarding_rules(vapp_id, network_name, config = {})
|
917
996
|
builder = Nokogiri::XML::Builder.new do |xml|
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
997
|
+
xml.NetworkConfigSection(
|
998
|
+
'xmlns' => 'http://www.vmware.com/vcloud/v1.5',
|
999
|
+
'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1') {
|
1000
|
+
xml['ovf'].Info 'Network configuration'
|
1001
|
+
xml.NetworkConfig('networkName' => network_name) {
|
1002
|
+
xml.Configuration {
|
1003
|
+
xml.ParentNetwork('href' => "#{@api_url}/network/#{config[:parent_network]}")
|
1004
|
+
xml.FenceMode(config[:fence_mode] || 'isolated')
|
1005
|
+
xml.Features {
|
1006
|
+
xml.NatService {
|
1007
|
+
xml.IsEnabled 'true'
|
1008
|
+
xml.NatType 'portForwarding'
|
1009
|
+
xml.Policy(config[:nat_policy_type] || 'allowTraffic')
|
1010
|
+
|
1011
|
+
pre_existing = get_vapp_port_forwarding_rules(vapp_id)
|
1012
|
+
|
1013
|
+
config[:nat_rules].concat(pre_existing)
|
1014
|
+
|
1015
|
+
config[:nat_rules].each do |nat_rule|
|
1016
|
+
xml.NatRule {
|
1017
|
+
xml.VmRule {
|
1018
|
+
xml.ExternalPort nat_rule[:nat_external_port]
|
1019
|
+
xml.VAppScopedVmId nat_rule[:vapp_scoped_local_id]
|
1020
|
+
xml.VmNicId(nat_rule[:nat_vmnic_id] || '0')
|
1021
|
+
xml.InternalPort nat_rule[:nat_internal_port]
|
1022
|
+
xml.Protocol(nat_rule[:nat_protocol] || 'TCP')
|
1023
|
+
}
|
944
1024
|
}
|
945
|
-
|
946
|
-
|
1025
|
+
end
|
1026
|
+
}
|
947
1027
|
}
|
948
1028
|
}
|
949
1029
|
}
|
950
1030
|
}
|
951
|
-
}
|
952
1031
|
end
|
953
1032
|
|
954
1033
|
params = {
|
955
|
-
'method'
|
956
|
-
'command' => "/vApp/vapp-#{
|
1034
|
+
'method' => :put,
|
1035
|
+
'command' => "/vApp/vapp-#{vapp_id}/networkConfigSection"
|
957
1036
|
}
|
958
1037
|
|
959
|
-
|
1038
|
+
_response, headers = send_request(
|
1039
|
+
params,
|
1040
|
+
builder.to_xml,
|
1041
|
+
'application/vnd.vmware.vcloud.networkConfigSection+xml'
|
1042
|
+
)
|
960
1043
|
|
961
|
-
task_id = headers[
|
1044
|
+
task_id = headers['Location'].gsub("#{@api_url}/task/", '')
|
962
1045
|
task_id
|
963
1046
|
end
|
964
|
-
|
965
|
-
|
966
|
-
|
967
1047
|
##
|
968
1048
|
# Get vApp port forwarding rules
|
969
1049
|
#
|
970
|
-
# -
|
971
|
-
|
972
|
-
# nat_rules << { :nat_external_port => j.to_s, :nat_internal_port => "22", :nat_protocol => "TCP", :vm_scoped_local_id => value[:vapp_scoped_local_id]}
|
973
|
-
|
974
|
-
def get_vapp_port_forwarding_rules(vAppId)
|
1050
|
+
# - vapp_id: id of the vApp
|
1051
|
+
def get_vapp_port_forwarding_rules(vapp_id)
|
975
1052
|
params = {
|
976
|
-
'method'
|
977
|
-
'command' => "/vApp/vapp-#{
|
1053
|
+
'method' => :get,
|
1054
|
+
'command' => "/vApp/vapp-#{vapp_id}/networkConfigSection"
|
978
1055
|
}
|
979
1056
|
|
980
|
-
response,
|
1057
|
+
response, _headers = send_request(params)
|
981
1058
|
|
982
1059
|
# FIXME: this will return nil if the vApp uses multiple vApp Networks
|
983
1060
|
# with Edge devices in natRouted/portForwarding mode.
|
984
|
-
config = response.css(
|
985
|
-
|
986
|
-
|
1061
|
+
config = response.css(
|
1062
|
+
'NetworkConfigSection/NetworkConfig/Configuration'
|
1063
|
+
)
|
1064
|
+
fence_mode = config.css('/FenceMode').text
|
1065
|
+
nat_type = config.css('/Features/NatService/NatType').text
|
1066
|
+
|
1067
|
+
unless fence_mode == 'natRouted'
|
1068
|
+
raise InvalidStateError,
|
1069
|
+
'Invalid request because FenceMode must be natRouted.'
|
1070
|
+
end
|
987
1071
|
|
988
|
-
|
989
|
-
|
1072
|
+
unless nat_type == 'portForwarding'
|
1073
|
+
raise InvalidStateError,
|
1074
|
+
'Invalid request because NatType must be portForwarding.'
|
1075
|
+
end
|
990
1076
|
|
991
1077
|
nat_rules = []
|
992
1078
|
config.css('/Features/NatService/NatRule').each do |rule|
|
993
1079
|
# portforwarding rules information
|
994
|
-
|
995
|
-
vmRule = rule.css('VmRule')
|
1080
|
+
vm_rule = rule.css('VmRule')
|
996
1081
|
|
997
1082
|
nat_rules << {
|
998
|
-
:nat_external_ip =>
|
999
|
-
:nat_external_port =>
|
1000
|
-
:vapp_scoped_local_id =>
|
1001
|
-
:vm_nic_id =>
|
1002
|
-
:nat_internal_port =>
|
1003
|
-
:nat_protocol =>
|
1083
|
+
:nat_external_ip => vm_rule.css('ExternalIpAddress').text,
|
1084
|
+
:nat_external_port => vm_rule.css('ExternalPort').text,
|
1085
|
+
:vapp_scoped_local_id => vm_rule.css('VAppScopedVmId').text,
|
1086
|
+
:vm_nic_id => vm_rule.css('VmNicId').text,
|
1087
|
+
:nat_internal_port => vm_rule.css('InternalPort').text,
|
1088
|
+
:nat_protocol => vm_rule.css('Protocol').text
|
1004
1089
|
}
|
1005
1090
|
end
|
1006
1091
|
nat_rules
|
1007
1092
|
end
|
1008
1093
|
|
1009
1094
|
##
|
1010
|
-
# Get vApp port forwarding rules external ports used and returns a set
|
1011
|
-
# of an HASH.
|
1095
|
+
# Get vApp port forwarding rules external ports used and returns a set
|
1096
|
+
# instead of an HASH.
|
1012
1097
|
#
|
1013
|
-
# -
|
1014
|
-
def get_vapp_port_forwarding_external_ports(
|
1098
|
+
# - vapp_id: id of the vApp
|
1099
|
+
def get_vapp_port_forwarding_external_ports(vapp_id)
|
1015
1100
|
params = {
|
1016
|
-
'method'
|
1017
|
-
'command' => "/vApp/vapp-#{
|
1101
|
+
'method' => :get,
|
1102
|
+
'command' => "/vApp/vapp-#{vapp_id}/networkConfigSection"
|
1018
1103
|
}
|
1019
1104
|
|
1020
|
-
|
1021
|
-
|
1022
|
-
response, headers = send_request(params)
|
1105
|
+
response, _headers = send_request(params)
|
1023
1106
|
|
1024
1107
|
# FIXME: this will return nil if the vApp uses multiple vApp Networks
|
1025
1108
|
# with Edge devices in natRouted/portForwarding mode.
|
1026
|
-
config = response.css(
|
1027
|
-
|
1028
|
-
|
1109
|
+
config = response.css(
|
1110
|
+
'NetworkConfigSection/NetworkConfig/Configuration'
|
1111
|
+
)
|
1112
|
+
fence_mode = config.css('/FenceMode').text
|
1113
|
+
nat_type = config.css('/Features/NatService/NatType').text
|
1029
1114
|
|
1030
|
-
|
1031
|
-
|
1115
|
+
unless fence_mode == 'natRouted'
|
1116
|
+
raise InvalidStateError,
|
1117
|
+
'Invalid request because FenceMode must be natRouted.'
|
1118
|
+
end
|
1119
|
+
|
1120
|
+
unless nat_type == 'portForwarding'
|
1121
|
+
raise InvalidStateError,
|
1122
|
+
'Invalid request because NatType must be portForwarding.'
|
1123
|
+
end
|
1032
1124
|
|
1033
1125
|
nat_rules = Set.new
|
1034
1126
|
config.css('/Features/NatService/NatRule').each do |rule|
|
1035
1127
|
# portforwarding rules information
|
1036
|
-
|
1037
|
-
nat_rules.add(
|
1128
|
+
vm_rule = rule.css('VmRule')
|
1129
|
+
nat_rules.add(vm_rule.css('ExternalPort').text.to_i)
|
1038
1130
|
end
|
1039
1131
|
nat_rules
|
1040
1132
|
end
|
1041
1133
|
|
1042
|
-
|
1134
|
+
##
|
1135
|
+
# Find an edge gateway id from the edge name and vdc_id
|
1136
|
+
#
|
1137
|
+
# - edge_gateway_name: Name of the vSE
|
1138
|
+
# - vdc_id: virtual datacenter id
|
1139
|
+
#
|
1043
1140
|
def find_edge_gateway_id(edge_gateway_name, vdc_id)
|
1044
1141
|
params = {
|
1045
|
-
'method'
|
1046
|
-
'command' =>
|
1142
|
+
'method' => :get,
|
1143
|
+
'command' => '/query?type=edgeGateway&' \
|
1144
|
+
'format=records&' \
|
1145
|
+
"filter=vdc==#{@api_url}/vdc/#{vdc_id}&" +
|
1146
|
+
"filter=name==#{edge_gateway_name}"
|
1047
1147
|
}
|
1048
1148
|
|
1049
|
-
response,
|
1149
|
+
response, _headers = send_request(params)
|
1050
1150
|
|
1051
|
-
|
1151
|
+
edge_gateway = response.css('EdgeGatewayRecord').first
|
1052
1152
|
|
1053
|
-
if
|
1054
|
-
return
|
1153
|
+
if edge_gateway
|
1154
|
+
return edge_gateway['href'].gsub(
|
1155
|
+
"#{@api_url}/admin/edgeGateway/", ''
|
1156
|
+
)
|
1055
1157
|
else
|
1056
1158
|
return nil
|
1057
1159
|
end
|
1058
1160
|
end
|
1059
1161
|
|
1060
|
-
|
1162
|
+
##
|
1163
|
+
# Find an edge gateway network from the edge name and vdc_id, and ip
|
1164
|
+
#
|
1165
|
+
# - edge_gateway_name: Name of the vSE
|
1166
|
+
# - vdc_id: virtual datacenter id
|
1167
|
+
# - edge_gateway_ip: public ip associated to that vSE
|
1168
|
+
#
|
1061
1169
|
|
1170
|
+
def find_edge_gateway_network(edge_gateway_name, vdc_id, edge_gateway_ip)
|
1062
1171
|
params = {
|
1063
|
-
'method'
|
1064
|
-
'command' =>
|
1172
|
+
'method' => :get,
|
1173
|
+
'command' => '/query?type=edgeGateway&' \
|
1174
|
+
'format=records&' \
|
1175
|
+
"filter=vdc==#{@api_url}/vdc/#{vdc_id}&" +
|
1176
|
+
"filter=name==#{edge_gateway_name}"
|
1065
1177
|
}
|
1066
1178
|
|
1067
|
-
response,
|
1179
|
+
response, _headers = send_request(params)
|
1068
1180
|
|
1069
|
-
|
1181
|
+
edge_gateway = response.css('EdgeGatewayRecord').first
|
1070
1182
|
|
1071
|
-
if
|
1072
|
-
|
1183
|
+
if edge_gateway
|
1184
|
+
edge_gateway_id = edge_gateway['href'].gsub(
|
1185
|
+
"#{@api_url}/admin/edgeGateway/", ''
|
1186
|
+
)
|
1073
1187
|
end
|
1074
1188
|
|
1075
1189
|
params = {
|
1076
|
-
'method'
|
1077
|
-
'command' => "/admin/edgeGateway/#{
|
1190
|
+
'method' => :get,
|
1191
|
+
'command' => "/admin/edgeGateway/#{edge_gateway_id}"
|
1078
1192
|
}
|
1079
1193
|
|
1080
|
-
response,
|
1081
|
-
response.css(
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1194
|
+
response, _headers = send_request(params)
|
1195
|
+
response.css(
|
1196
|
+
'EdgeGateway Configuration GatewayInterfaces GatewayInterface'
|
1197
|
+
).each do |gw|
|
1198
|
+
# Only check uplinks, avoid another check
|
1199
|
+
if gw.css('InterfaceType').text == 'uplink'
|
1200
|
+
|
1086
1201
|
# Loop on all sub-allocation pools
|
1087
|
-
gw.css(
|
1202
|
+
gw.css('SubnetParticipation IpRanges IpRange').each do |cur_range|
|
1088
1203
|
|
1089
|
-
|
1090
|
-
|
1204
|
+
low_ip = cur_range.css('StartAddress').first.text
|
1205
|
+
high_ip = cur_range.css('EndAddress').first.text
|
1091
1206
|
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1207
|
+
range_ip_low = NetAddr.ip_to_i(low_ip)
|
1208
|
+
range_ip_high = NetAddr.ip_to_i(high_ip)
|
1209
|
+
test_ip = NetAddr.ip_to_i(edge_gateway_ip)
|
1095
1210
|
|
1096
|
-
|
1097
|
-
|
1211
|
+
# FIXME: replace "===" (tsugliani)
|
1212
|
+
if (range_ip_low..range_ip_high) === test_ip
|
1213
|
+
return gw.css('Network').first[:href]
|
1098
1214
|
end
|
1099
1215
|
end
|
1100
1216
|
end
|
1101
|
-
|
1102
1217
|
end
|
1103
|
-
|
1104
1218
|
end
|
1105
1219
|
|
1106
|
-
|
1107
1220
|
##
|
1108
1221
|
# Set Org Edge port forwarding and firewall rules
|
1109
1222
|
#
|
1110
|
-
# -
|
1223
|
+
# - vapp_id: id of the vapp to be modified
|
1111
1224
|
# - network_name: name of the vapp network to be modified
|
1112
|
-
# - config: hash with network configuration specifications,
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1225
|
+
# - config: hash with network configuration specifications,
|
1226
|
+
# must contain an array inside :nat_rules with the nat rules
|
1227
|
+
# to be applied.
|
1228
|
+
def set_edge_gateway_rules(edge_gateway_name, vdc_id, edge_gateway_ip, vapp_id)
|
1229
|
+
edge_vapp_ip = get_vapp_edge_public_ip(vapp_id)
|
1230
|
+
edge_network_id = find_edge_gateway_network(
|
1231
|
+
edge_gateway_name,
|
1232
|
+
vdc_id,
|
1233
|
+
edge_gateway_ip
|
1234
|
+
)
|
1117
1235
|
edge_gateway_id = find_edge_gateway_id(edge_gateway_name, vdc_id)
|
1118
1236
|
|
1237
|
+
### FIXME: tsugliani
|
1238
|
+
# We need to check the previous variables, especially (edge_*)
|
1239
|
+
# which can fail in some *weird* situations.
|
1119
1240
|
params = {
|
1120
|
-
'method'
|
1121
|
-
'command'
|
1241
|
+
'method' => :get,
|
1242
|
+
'command' => "/admin/edgeGateway/#{edge_gateway_id}"
|
1122
1243
|
}
|
1123
1244
|
|
1124
|
-
response,
|
1245
|
+
response, _headers = send_request(params)
|
1125
1246
|
|
1126
|
-
interesting = response.css(
|
1247
|
+
interesting = response.css(
|
1248
|
+
'EdgeGateway Configuration EdgeGatewayServiceConfiguration'
|
1249
|
+
)
|
1127
1250
|
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1251
|
+
nat_rule_1 = Nokogiri::XML::Node.new 'NatRule', response
|
1252
|
+
rule_type = Nokogiri::XML::Node.new 'RuleType', response
|
1253
|
+
rule_type.content = 'DNAT'
|
1254
|
+
nat_rule_1.add_child ruleType
|
1132
1255
|
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1256
|
+
is_enabled = Nokogiri::XML::Node.new 'IsEnabled', response
|
1257
|
+
is_enabled.content = 'true'
|
1258
|
+
nat_rule_1.add_child is_enabled
|
1136
1259
|
|
1137
|
-
|
1138
|
-
|
1260
|
+
gateway_nat_rule = Nokogiri::XML::Node.new 'GatewayNatRule',
|
1261
|
+
response
|
1262
|
+
nat_rule_1.add_child gateway_nat_rule
|
1139
1263
|
|
1140
|
-
|
1141
|
-
|
1264
|
+
interface = Nokogiri::XML::Node.new 'Interface', response
|
1265
|
+
interface['href'] = edge_network_id
|
1142
1266
|
|
1143
|
-
|
1267
|
+
gateway_nat_rule.add_child interface
|
1144
1268
|
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1269
|
+
original_ip = Nokogiri::XML::Node.new 'OriginalIp', response
|
1270
|
+
original_ip.content = edge_gateway_ip
|
1271
|
+
gatewayNatRule.add_child original_ip
|
1148
1272
|
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1273
|
+
original_port = Nokogiri::XML::Node.new 'OriginalPort', response
|
1274
|
+
original_port.content = 'any'
|
1275
|
+
gateway_nat_rule.add_child original_port
|
1152
1276
|
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1277
|
+
translated_ip = Nokogiri::XML::Node.new 'TranslatedIp', response
|
1278
|
+
translated_ip.content = edge_vapp_ip
|
1279
|
+
gateway_nat_rule.add_child translated_ip
|
1156
1280
|
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1281
|
+
translated_port = Nokogiri::XML::Node.new 'TranslatedPort',
|
1282
|
+
response
|
1283
|
+
translated_port.content = 'any'
|
1284
|
+
gateway_nat_rule.add_child translated_port
|
1160
1285
|
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1286
|
+
protocol = Nokogiri::XML::Node.new 'Protocol', response
|
1287
|
+
protocol.content = 'any'
|
1288
|
+
gateway_nat_rule.add_child protocol
|
1164
1289
|
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1290
|
+
# FIXME: frapposelli/tsugliani we should be able to remove this
|
1291
|
+
# FIXME: test this against a vCloud Director 5.1.x installation
|
1292
|
+
# icmpSubType = Nokogiri::XML::Node.new 'IcmpSubType', response
|
1293
|
+
# icmpSubType.content = "any"
|
1294
|
+
# gatewayNatRule.add_child icmpSubType
|
1170
1295
|
|
1296
|
+
nat_rule_2 = Nokogiri::XML::Node.new 'NatRule', response
|
1171
1297
|
|
1172
|
-
|
1298
|
+
rule_type = Nokogiri::XML::Node.new 'RuleType', response
|
1299
|
+
rule_type.content = 'SNAT'
|
1300
|
+
nat_rule_2.add_child rule_type
|
1173
1301
|
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1302
|
+
is_enabled = Nokogiri::XML::Node.new 'IsEnabled', response
|
1303
|
+
is_enabled.content = 'true'
|
1304
|
+
nat_rule_2.add_child isEnabled
|
1177
1305
|
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1306
|
+
gateway_nat_rule = Nokogiri::XML::Node.new 'GatewayNatRule',
|
1307
|
+
response
|
1308
|
+
nat_rule_2.add_child gatewayNatRule
|
1181
1309
|
|
1182
|
-
|
1183
|
-
|
1310
|
+
interface = Nokogiri::XML::Node.new 'Interface', response
|
1311
|
+
interface['href'] = edge_network_id
|
1184
1312
|
|
1185
|
-
|
1186
|
-
interface["href"] = edge_network_id
|
1187
|
-
|
1188
|
-
gatewayNatRule.add_child interface
|
1313
|
+
gateway_nat_rule.add_child interface
|
1189
1314
|
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1315
|
+
original_ip = Nokogiri::XML::Node.new 'OriginalIp', response
|
1316
|
+
original_ip.content = edge_vapp_ip
|
1317
|
+
gatewayNatRule.add_child original_ip
|
1193
1318
|
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1319
|
+
translated_ip = Nokogiri::XML::Node.new 'TranslatedIp', response
|
1320
|
+
translated_ip.content = edge_gateway_ip
|
1321
|
+
gateway_nat_rule.add_child translated_ip
|
1197
1322
|
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1323
|
+
protocol = Nokogiri::XML::Node.new 'Protocol', response
|
1324
|
+
protocol.content = 'any'
|
1325
|
+
gateway_nat_rule.add_child protocol
|
1201
1326
|
|
1327
|
+
firewall_rule_1 = Nokogiri::XML::Node.new 'FirewallRule', response
|
1202
1328
|
|
1203
|
-
|
1329
|
+
is_enabled = Nokogiri::XML::Node.new 'IsEnabled', response
|
1330
|
+
is_enabled.content = 'true'
|
1331
|
+
firewall_rule_1.add_child is_enabled
|
1204
1332
|
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1333
|
+
description = Nokogiri::XML::Node.new 'Description', response
|
1334
|
+
description.content = 'Allow Vagrant Communications'
|
1335
|
+
firewall_rule_1.add_child description
|
1208
1336
|
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1337
|
+
policy = Nokogiri::XML::Node.new 'Policy', response
|
1338
|
+
policy.content = 'allow'
|
1339
|
+
firewall_rule_1.add_child policy
|
1212
1340
|
|
1213
|
-
|
1214
|
-
|
1215
|
-
firewallRule1.add_child policy
|
1341
|
+
protocols = Nokogiri::XML::Node.new 'Protocols', response
|
1342
|
+
firewall_rule_1.add_child protocols
|
1216
1343
|
|
1217
|
-
|
1218
|
-
|
1344
|
+
any = Nokogiri::XML::Node.new 'Any', response
|
1345
|
+
any.content = 'true'
|
1346
|
+
protocols.add_child any
|
1219
1347
|
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1348
|
+
destination_port_range =
|
1349
|
+
Nokogiri::XML::Node.new 'DestinationPortRange', response
|
1350
|
+
destination_port_range.content = 'Any'
|
1351
|
+
firewall_rule_1.add_child destination_port_range
|
1223
1352
|
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1353
|
+
destination_ip = Nokogiri::XML::Node.new 'DestinationIp', response
|
1354
|
+
destination_ip.content = edge_gateway_ip
|
1355
|
+
firewall_rule_1.add_child destination_ip
|
1356
|
+
source_port_range = Nokogiri::XML::Node.new 'SourcePortRange',
|
1227
1357
|
|
1228
|
-
|
1229
|
-
|
1230
|
-
|
1358
|
+
response
|
1359
|
+
source_port_range.content = 'Any'
|
1360
|
+
firewall_rule_1.add_child source_port_range
|
1231
1361
|
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1362
|
+
source_ip = Nokogiri::XML::Node.new 'SourceIp', response
|
1363
|
+
source_ip.content = 'Any'
|
1364
|
+
firewall_rule_1.add_child source_ip
|
1235
1365
|
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1366
|
+
enable_logging = Nokogiri::XML::Node.new 'EnableLogging', response
|
1367
|
+
enable_logging.content = 'false'
|
1368
|
+
firewall_rule_1.add_child enable_logging
|
1239
1369
|
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1370
|
+
# FIXME: Think about adding an outgoing rule
|
1371
|
+
# (which could not be available)
|
1372
|
+
# by default in all cases vapp_edge_ip -> any
|
1373
|
+
# Especially for provisionners using apt-get, internet, etc...
|
1374
|
+
# (tsugliani)
|
1243
1375
|
|
1244
1376
|
builder = Nokogiri::XML::Builder.new
|
1245
1377
|
builder << interesting
|
@@ -1248,56 +1380,141 @@ module VagrantPlugins
|
|
1248
1380
|
config.default_xml.noblanks
|
1249
1381
|
end
|
1250
1382
|
|
1251
|
-
nat_rules = set_edge_rules.at_css(
|
1383
|
+
nat_rules = set_edge_rules.at_css('NatService')
|
1384
|
+
nat_rules << nat_rule_1
|
1385
|
+
nat_rules << nat_rule_2
|
1252
1386
|
|
1253
|
-
|
1254
|
-
|
1387
|
+
fw_rules = set_edge_rules.at_css('FirewallService')
|
1388
|
+
fw_rules << firewall_rule_1
|
1255
1389
|
|
1256
|
-
|
1257
|
-
|
1390
|
+
xml = set_edge_rules.at_css 'EdgeGatewayServiceConfiguration'
|
1391
|
+
xml['xmlns'] = 'http://www.vmware.com/vcloud/v1.5'
|
1392
|
+
|
1393
|
+
params = {
|
1394
|
+
'method' => :post,
|
1395
|
+
'command' => "/admin/edgeGateway/#{edge_gateway_id}/action/" +
|
1396
|
+
'configureServices'
|
1397
|
+
}
|
1258
1398
|
|
1259
|
-
|
1260
|
-
|
1399
|
+
_response, headers = send_request(
|
1400
|
+
params,
|
1401
|
+
set_edge_rules.to_xml,
|
1402
|
+
'application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml'
|
1403
|
+
)
|
1404
|
+
|
1405
|
+
task_id = headers['Location'].gsub("#{@api_url}/task/", '')
|
1406
|
+
task_id
|
1407
|
+
end
|
1408
|
+
|
1409
|
+
##
|
1410
|
+
# Get Org Edge port forwarding and firewall rules
|
1411
|
+
#
|
1412
|
+
# - vapp_id: id of the vapp to be modified
|
1413
|
+
# - network_name: name of the vapp network to be modified
|
1414
|
+
# - config: hash with network configuration specifications,
|
1415
|
+
# must contain an array inside :nat_rules with the nat rules
|
1416
|
+
# to be applied.
|
1417
|
+
def get_edge_gateway_rules(edge_gateway_name, vdc_id)
|
1418
|
+
edge_gateway_id = find_edge_gateway_id(edge_gateway_name, vdc_id)
|
1261
1419
|
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
}
|
1420
|
+
params = {
|
1421
|
+
'method' => :get,
|
1422
|
+
'command' => "/admin/edgeGateway/#{edge_gateway_id}"
|
1423
|
+
}
|
1267
1424
|
|
1268
|
-
|
1425
|
+
response, _headers = send_request(params)
|
1269
1426
|
|
1270
|
-
|
1271
|
-
task_id
|
1427
|
+
nat_fw_rules = []
|
1272
1428
|
|
1273
|
-
|
1429
|
+
interesting = response.css(
|
1430
|
+
'EdgeGateway Configuration EdgeGatewayServiceConfiguration'
|
1431
|
+
)
|
1432
|
+
interesting.css('NatService NatRule').each do |node|
|
1433
|
+
if node.css('RuleType').text == 'DNAT'
|
1434
|
+
gw_node = node.css('GatewayNatRule')
|
1435
|
+
nat_fw_rules << {
|
1436
|
+
:rule_type => 'DNAT',
|
1437
|
+
:original_ip => gw_node.css('OriginalIp').text,
|
1438
|
+
:original_port => gw_node.css('OriginalPort').text,
|
1439
|
+
:translated_ip => gw_node.css('TranslatedIp').text,
|
1440
|
+
:translated_port => gw_node.css('TranslatedPort').text,
|
1441
|
+
:protocol => gw_node.css('Protocol').text,
|
1442
|
+
:is_enabled => node.css('IsEnabled').text
|
1443
|
+
}
|
1274
1444
|
|
1275
|
-
|
1445
|
+
end
|
1446
|
+
if node.css('RuleType').text == 'SNAT'
|
1447
|
+
gw_node = node.css('gatewayNatRule')
|
1448
|
+
nat_fw_rules << {
|
1449
|
+
:rule_type => 'SNAT',
|
1450
|
+
:interface_name => gw_node.css('Interface').first['name'],
|
1451
|
+
:original_ip => gw_node.css('OriginalIp').text,
|
1452
|
+
:translated_ip => gw_node.css('TranslatedIp').text,
|
1453
|
+
:is_enabled => node.css('IsEnabled').text
|
1454
|
+
}
|
1455
|
+
end
|
1456
|
+
end
|
1276
1457
|
|
1277
|
-
|
1458
|
+
interesting.css('FirewallService FirewallRule').each do |node|
|
1459
|
+
if node.css('Port').text == '-1'
|
1460
|
+
nat_fw_rules << {
|
1461
|
+
:rule_type => 'Firewall',
|
1462
|
+
:id => node.css('Id').text,
|
1463
|
+
:policy => node.css('Policy').text,
|
1464
|
+
:description => node.css('Description').text,
|
1465
|
+
:destination_ip => node.css('DestinationIp').text,
|
1466
|
+
:destination_portrange => node.css('DestinationPortRange').text,
|
1467
|
+
:source_ip => node.css('SourceIp').text,
|
1468
|
+
:source_portrange => node.css('SourcePortRange').text,
|
1469
|
+
:is_enabled => node.css('IsEnabled').text
|
1470
|
+
}
|
1471
|
+
end
|
1472
|
+
end
|
1473
|
+
|
1474
|
+
nat_fw_rules
|
1475
|
+
end
|
1476
|
+
|
1477
|
+
##
|
1478
|
+
# Remove NAT/FW rules from a edge gateway device
|
1479
|
+
#
|
1480
|
+
# - edge_gateway_name: Name of the vSE
|
1481
|
+
# - vdc_id: virtual datacenter id
|
1482
|
+
# - edge_gateway_ip: public ip associated the vSE
|
1483
|
+
# - vapp_id: vApp identifier to correlate with the vApp Edge
|
1484
|
+
|
1485
|
+
def remove_edge_gateway_rules(edge_gateway_name, vdc_id, edge_gateway_ip, vapp_id)
|
1486
|
+
edge_vapp_ip = get_vapp_edge_public_ip(vapp_id)
|
1278
1487
|
edge_gateway_id = find_edge_gateway_id(edge_gateway_name, vdc_id)
|
1279
1488
|
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1489
|
+
params = {
|
1490
|
+
'method' => :get,
|
1491
|
+
'command' => "/admin/edgeGateway/#{edge_gateway_id}"
|
1492
|
+
}
|
1284
1493
|
|
1285
|
-
response,
|
1286
|
-
|
1287
|
-
interesting = response.css(
|
1288
|
-
|
1289
|
-
|
1494
|
+
response, _headers = send_request(params)
|
1495
|
+
|
1496
|
+
interesting = response.css(
|
1497
|
+
'EdgeGateway Configuration EdgeGatewayServiceConfiguration'
|
1498
|
+
)
|
1499
|
+
interesting.css('NatService NatRule').each do |node|
|
1500
|
+
if node.css('RuleType').text == 'DNAT' &&
|
1501
|
+
node.css('GatewayNatRule/OriginalIp').text == edge_gateway_ip &&
|
1502
|
+
node.css('GatewayNatRule/TranslatedIp').text == edge_vapp_ip
|
1290
1503
|
node.remove
|
1291
|
-
end
|
1292
|
-
if node.css(
|
1504
|
+
end
|
1505
|
+
if node.css('RuleType').text == 'SNAT' &&
|
1506
|
+
node.css('GatewayNatRule/OriginalIp').text == edge_vapp_ip &&
|
1507
|
+
node.css('GatewayNatRule/TranslatedIp').text == edge_gateway_ip
|
1293
1508
|
node.remove
|
1294
|
-
end
|
1509
|
+
end
|
1295
1510
|
end
|
1296
1511
|
|
1297
|
-
interesting.css(
|
1298
|
-
if node.css(
|
1512
|
+
interesting.css('FirewallService FirewallRule').each do |node|
|
1513
|
+
if node.css('Port').text == '-1' &&
|
1514
|
+
node.css('DestinationIp').text == edge_gateway_ip &&
|
1515
|
+
node.css('DestinationPortRange').text == 'Any'
|
1299
1516
|
node.remove
|
1300
|
-
end
|
1517
|
+
end
|
1301
1518
|
end
|
1302
1519
|
|
1303
1520
|
builder = Nokogiri::XML::Builder.new
|
@@ -1305,22 +1522,24 @@ module VagrantPlugins
|
|
1305
1522
|
|
1306
1523
|
remove_edge_rules = Nokogiri::XML(builder.to_xml)
|
1307
1524
|
|
1308
|
-
|
1309
|
-
|
1310
|
-
|
1525
|
+
xml = remove_edge_rules.at_css 'EdgeGatewayServiceConfiguration'
|
1526
|
+
xml['xmlns'] = 'http://www.vmware.com/vcloud/v1.5'
|
1527
|
+
|
1311
1528
|
params = {
|
1312
|
-
'method'
|
1313
|
-
'command' => "/admin/edgeGateway/#{edge_gateway_id}/action/
|
1529
|
+
'method' => :post,
|
1530
|
+
'command' => "/admin/edgeGateway/#{edge_gateway_id}/action/" +
|
1531
|
+
'configureServices'
|
1314
1532
|
}
|
1315
1533
|
|
1316
|
-
|
1534
|
+
_response, headers = send_request(
|
1535
|
+
params,
|
1536
|
+
remove_edge_rules.to_xml,
|
1537
|
+
'application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml'
|
1538
|
+
)
|
1317
1539
|
|
1318
|
-
task_id = headers[
|
1540
|
+
task_id = headers['Location'].gsub("#{@api_url}/task/", '')
|
1319
1541
|
task_id
|
1320
|
-
|
1321
|
-
|
1322
|
-
|
1323
|
-
|
1542
|
+
end
|
1324
1543
|
|
1325
1544
|
##
|
1326
1545
|
# get vApp edge public IP from the vApp ID
|
@@ -1328,149 +1547,195 @@ module VagrantPlugins
|
|
1328
1547
|
# - vApp needs to be poweredOn
|
1329
1548
|
# - FenceMode is set to "natRouted"
|
1330
1549
|
# - NatType" is set to "portForwarding
|
1331
|
-
# This will be required to know how to connect to VMs behind the Edge
|
1332
|
-
|
1550
|
+
# This will be required to know how to connect to VMs behind the Edge
|
1551
|
+
# device.
|
1552
|
+
def get_vapp_edge_public_ip(vapp_id)
|
1333
1553
|
# Check the network configuration section
|
1334
1554
|
params = {
|
1335
1555
|
'method' => :get,
|
1336
|
-
'command' => "/vApp/vapp-#{
|
1556
|
+
'command' => "/vApp/vapp-#{vapp_id}/networkConfigSection"
|
1337
1557
|
}
|
1338
1558
|
|
1339
|
-
response,
|
1559
|
+
response, _headers = send_request(params)
|
1340
1560
|
|
1341
1561
|
# FIXME: this will return nil if the vApp uses multiple vApp Networks
|
1342
1562
|
# with Edge devices in natRouted/portForwarding mode.
|
1343
|
-
config = response.css(
|
1563
|
+
config = response.css(
|
1564
|
+
'NetworkConfigSection/NetworkConfig/Configuration'
|
1565
|
+
)
|
1566
|
+
|
1567
|
+
fence_mode = config.css('/FenceMode').text
|
1568
|
+
nat_type = config.css('/Features/NatService/NatType').text
|
1344
1569
|
|
1345
|
-
|
1346
|
-
|
1570
|
+
unless fence_mode == 'natRouted'
|
1571
|
+
raise InvalidStateError,
|
1572
|
+
'Invalid request because FenceMode must be natRouted.'
|
1573
|
+
end
|
1347
1574
|
|
1348
|
-
|
1349
|
-
|
1575
|
+
unless nat_type == 'portForwarding'
|
1576
|
+
raise InvalidStateError,
|
1577
|
+
'Invalid request because NatType must be portForwarding.'
|
1578
|
+
end
|
1350
1579
|
|
1351
|
-
# Check the routerInfo configuration where the global external IP
|
1352
|
-
|
1353
|
-
|
1580
|
+
# Check the routerInfo configuration where the global external IP
|
1581
|
+
# is defined
|
1582
|
+
edge_ip = config.css('/RouterInfo/ExternalIp').text
|
1583
|
+
if edge_ip == ''
|
1354
1584
|
return nil
|
1355
1585
|
else
|
1356
|
-
return
|
1586
|
+
return edge_ip
|
1357
1587
|
end
|
1358
1588
|
end
|
1359
1589
|
|
1360
1590
|
##
|
1361
1591
|
# Upload an OVF package
|
1362
|
-
# -
|
1592
|
+
# - vdc_id
|
1363
1593
|
# - vappName
|
1364
1594
|
# - vappDescription
|
1365
1595
|
# - ovfFile
|
1366
1596
|
# - catalogId
|
1367
1597
|
# - uploadOptions {}
|
1368
|
-
def upload_ovf(
|
1369
|
-
|
1598
|
+
def upload_ovf(vdc_id, vapp_name, vapp_description, ovf_file, catalog_id, upload_options = {})
|
1370
1599
|
# if send_manifest is not set, setting it true
|
1371
|
-
if
|
1372
|
-
|
1600
|
+
if upload_options[:send_manifest].nil? ||
|
1601
|
+
upload_options[:send_manifest]
|
1602
|
+
upload_manifest = 'true'
|
1373
1603
|
else
|
1374
|
-
|
1604
|
+
upload_manifest = 'false'
|
1375
1605
|
end
|
1376
1606
|
|
1377
1607
|
builder = Nokogiri::XML::Builder.new do |xml|
|
1378
1608
|
xml.UploadVAppTemplateParams(
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
xml.Description
|
1609
|
+
'xmlns' => 'http://www.vmware.com/vcloud/v1.5',
|
1610
|
+
'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1',
|
1611
|
+
'manifestRequired' => upload_manifest,
|
1612
|
+
'name' => vapp_name) {
|
1613
|
+
xml.Description vapp_description
|
1384
1614
|
}
|
1385
1615
|
end
|
1386
1616
|
|
1387
1617
|
params = {
|
1388
|
-
'method'
|
1389
|
-
'command' => "/vdc/#{
|
1618
|
+
'method' => :post,
|
1619
|
+
'command' => "/vdc/#{vdc_id}/action/uploadVAppTemplate"
|
1390
1620
|
}
|
1391
1621
|
|
1392
|
-
@logger.debug(
|
1622
|
+
@logger.debug('Sending uploadVAppTemplate request...')
|
1393
1623
|
|
1394
1624
|
response, headers = send_request(
|
1395
|
-
params,
|
1625
|
+
params,
|
1396
1626
|
builder.to_xml,
|
1397
|
-
|
1627
|
+
'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
|
1398
1628
|
)
|
1399
1629
|
|
1400
|
-
# Get vAppTemplate Link from location
|
1401
|
-
|
1402
|
-
|
1403
|
-
|
1404
|
-
|
1630
|
+
# Get vAppTemplate Link from location
|
1631
|
+
vapp_template = headers['Location'].gsub(
|
1632
|
+
"#{@api_url}/vAppTemplate/vappTemplate-", ''
|
1633
|
+
)
|
1634
|
+
|
1635
|
+
@logger.debug("Getting vAppTemplate ID: #{vapp_template}")
|
1636
|
+
descriptor_upload = response.css(
|
1637
|
+
"Files Link [rel='upload:default']"
|
1638
|
+
).first[:href].gsub("#{@host_url}/transfer/", '')
|
1639
|
+
transfer_guid = descriptor_upload.gsub('/descriptor.ovf', '')
|
1405
1640
|
|
1406
|
-
|
1407
|
-
|
1641
|
+
ovf_file_basename = File.basename(ovf_file, '.ovf')
|
1642
|
+
ovf_dir = File.dirname(ovf_file)
|
1408
1643
|
|
1409
1644
|
# Send OVF Descriptor
|
1410
|
-
@logger.debug(
|
1411
|
-
|
1412
|
-
|
1413
|
-
upload_file(
|
1645
|
+
@logger.debug('Sending OVF Descriptor...')
|
1646
|
+
upload_url = "/transfer/#{descriptor_upload}"
|
1647
|
+
upload_filename = "#{ovf_dir}/#{ovf_file_basename}.ovf"
|
1648
|
+
upload_file(
|
1649
|
+
upload_url,
|
1650
|
+
upload_filename,
|
1651
|
+
vapp_template,
|
1652
|
+
upload_options
|
1653
|
+
)
|
1414
1654
|
|
1415
1655
|
# Begin the catch for upload interruption
|
1416
1656
|
begin
|
1417
1657
|
params = {
|
1418
|
-
'method'
|
1419
|
-
'command' => "/vAppTemplate/vappTemplate-#{
|
1658
|
+
'method' => :get,
|
1659
|
+
'command' => "/vAppTemplate/vappTemplate-#{vapp_template}"
|
1420
1660
|
}
|
1421
1661
|
|
1422
|
-
response,
|
1662
|
+
response, _headers = send_request(params)
|
1423
1663
|
|
1424
|
-
task = response.css(
|
1425
|
-
|
1664
|
+
task = response.css(
|
1665
|
+
"VAppTemplate Task[operationName='vdcUploadOvfContents']"
|
1666
|
+
).first
|
1667
|
+
task_id = task['href'].gsub("#{@api_url}/task/", '')
|
1426
1668
|
|
1427
|
-
# Loop to wait for the upload links to show up in the vAppTemplate
|
1428
|
-
|
1669
|
+
# Loop to wait for the upload links to show up in the vAppTemplate
|
1670
|
+
# we just created
|
1671
|
+
@logger.debug(
|
1672
|
+
'Waiting for the upload links to show up in the vAppTemplate ' \
|
1673
|
+
'we just created.'
|
1674
|
+
)
|
1429
1675
|
while true
|
1430
|
-
response,
|
1431
|
-
@logger.debug(
|
1676
|
+
response, _headers = send_request(params)
|
1677
|
+
@logger.debug('Request...')
|
1432
1678
|
break unless response.css("Files Link [rel='upload:default']").count == 1
|
1433
1679
|
sleep 1
|
1434
1680
|
end
|
1435
1681
|
|
1436
|
-
if
|
1437
|
-
|
1438
|
-
|
1439
|
-
upload_file(
|
1682
|
+
if upload_manifest == 'true'
|
1683
|
+
upload_url = "/transfer/#{transfer_guid}/descriptor.mf"
|
1684
|
+
upload_filename = "#{ovf_dir}/#{ovf_file_basename}.mf"
|
1685
|
+
upload_file(
|
1686
|
+
upload_url,
|
1687
|
+
upload_filename,
|
1688
|
+
vapp_template,
|
1689
|
+
upload_options
|
1690
|
+
)
|
1440
1691
|
end
|
1441
1692
|
|
1442
1693
|
# Start uploading OVF VMDK files
|
1443
1694
|
params = {
|
1444
|
-
'method'
|
1445
|
-
'command' => "/vAppTemplate/vappTemplate-#{
|
1695
|
+
'method' => :get,
|
1696
|
+
'command' => "/vAppTemplate/vappTemplate-#{vapp_template}"
|
1446
1697
|
}
|
1447
|
-
response,
|
1448
|
-
response.css(
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1698
|
+
response, _headers = send_request(params)
|
1699
|
+
response.css(
|
1700
|
+
"Files File [bytesTransferred='0'] Link [rel='upload:default']"
|
1701
|
+
).each do |file|
|
1702
|
+
file_name = file[:href].gsub(
|
1703
|
+
"#{@host_url}/transfer/#{transfer_guid}/", ''
|
1704
|
+
)
|
1705
|
+
upload_filename = "#{ovf_dir}/#{file_name}"
|
1706
|
+
upload_url = "/transfer/#{transfer_guid}/#{file_name}"
|
1707
|
+
upload_file(
|
1708
|
+
upload_url,
|
1709
|
+
upload_filename,
|
1710
|
+
vapp_template,
|
1711
|
+
upload_options
|
1712
|
+
)
|
1453
1713
|
end
|
1454
1714
|
|
1455
|
-
# Add item to the catalog
|
1715
|
+
# Add item to the catalog catalog_id
|
1456
1716
|
builder = Nokogiri::XML::Builder.new do |xml|
|
1457
1717
|
xml.CatalogItem(
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
xml.Description
|
1718
|
+
'xmlns' => 'http://www.vmware.com/vcloud/v1.5',
|
1719
|
+
'type' => 'application/vnd.vmware.vcloud.catalogItem+xml',
|
1720
|
+
'name' => vapp_name) {
|
1721
|
+
xml.Description vapp_description
|
1462
1722
|
xml.Entity(
|
1463
|
-
|
1723
|
+
'href' => "#{@api_url}/vAppTemplate/" +
|
1724
|
+
"vappTemplate-#{vapp_template}"
|
1464
1725
|
)
|
1465
1726
|
}
|
1466
1727
|
end
|
1467
1728
|
|
1468
1729
|
params = {
|
1469
|
-
'method'
|
1470
|
-
'command' => "/catalog/#{
|
1730
|
+
'method' => :post,
|
1731
|
+
'command' => "/catalog/#{catalog_id}/catalogItems"
|
1471
1732
|
}
|
1472
|
-
|
1473
|
-
|
1733
|
+
# No debug here (tsugliani)
|
1734
|
+
_response, _headers = send_request(
|
1735
|
+
params,
|
1736
|
+
builder.to_xml,
|
1737
|
+
'application/vnd.vmware.vcloud.catalogItem+xml'
|
1738
|
+
)
|
1474
1739
|
|
1475
1740
|
task_id
|
1476
1741
|
|
@@ -1478,132 +1743,163 @@ module VagrantPlugins
|
|
1478
1743
|
|
1479
1744
|
rescue Exception => e
|
1480
1745
|
puts "Exception detected: #{e.message}."
|
1481
|
-
puts
|
1746
|
+
puts 'Aborting task...'
|
1482
1747
|
|
1483
1748
|
# Get vAppTemplate Task
|
1484
1749
|
params = {
|
1485
|
-
'method'
|
1486
|
-
'command' => "/vAppTemplate/vappTemplate-#{
|
1750
|
+
'method' => :get,
|
1751
|
+
'command' => "/vAppTemplate/vappTemplate-#{vapp_template}"
|
1487
1752
|
}
|
1488
|
-
response,
|
1753
|
+
response, _headers = send_request(params)
|
1489
1754
|
|
1490
1755
|
# Cancel Task
|
1491
|
-
|
1756
|
+
cancel_hook = response.css(
|
1757
|
+
"Tasks Task Link [rel='task:cancel']"
|
1758
|
+
).first[:href].gsub("#{@api_url}", '')
|
1759
|
+
|
1492
1760
|
params = {
|
1493
|
-
'method'
|
1494
|
-
'command' =>
|
1761
|
+
'method' => :post,
|
1762
|
+
'command' => cancel_hook
|
1495
1763
|
}
|
1496
|
-
|
1764
|
+
# No debug here (tsugliani)
|
1765
|
+
_response, _headers = send_request(params)
|
1497
1766
|
raise
|
1498
1767
|
end
|
1499
1768
|
end
|
1500
1769
|
|
1501
1770
|
##
|
1502
1771
|
# Fetch information for a given task
|
1503
|
-
def get_task(
|
1772
|
+
def get_task(task_id)
|
1504
1773
|
params = {
|
1505
|
-
'method'
|
1506
|
-
'command' => "/task/#{
|
1774
|
+
'method' => :get,
|
1775
|
+
'command' => "/task/#{task_id}"
|
1507
1776
|
}
|
1508
1777
|
|
1509
|
-
response,
|
1778
|
+
response, _headers = send_request(params)
|
1510
1779
|
|
1511
1780
|
task = response.css('Task').first
|
1512
1781
|
status = task['status']
|
1513
1782
|
start_time = task['startTime']
|
1514
1783
|
end_time = task['endTime']
|
1515
1784
|
|
1516
|
-
{
|
1785
|
+
{
|
1786
|
+
:status => status,
|
1787
|
+
:start_time => start_time,
|
1788
|
+
:end_time => end_time,
|
1789
|
+
:response => response
|
1790
|
+
}
|
1517
1791
|
end
|
1518
1792
|
|
1519
1793
|
##
|
1520
1794
|
# Poll a given task until completion
|
1521
|
-
def wait_task_completion(
|
1522
|
-
task,
|
1795
|
+
def wait_task_completion(task_id)
|
1796
|
+
task, errormsg = nil
|
1523
1797
|
loop do
|
1524
|
-
task = get_task(
|
1525
|
-
@logger.debug(
|
1798
|
+
task = get_task(task_id)
|
1799
|
+
@logger.debug(
|
1800
|
+
"Evaluating taskid: #{task_id}, current status #{task[:status]}"
|
1801
|
+
)
|
1526
1802
|
break if task[:status] != 'running'
|
1527
1803
|
sleep 5
|
1528
1804
|
end
|
1529
1805
|
|
1530
1806
|
if task[:status] == 'error'
|
1531
|
-
@logger.debug(
|
1532
|
-
errormsg = task[:response].css(
|
1533
|
-
@logger.debug(
|
1534
|
-
|
1807
|
+
@logger.debug('Task Error')
|
1808
|
+
errormsg = task[:response].css('Error').first
|
1809
|
+
@logger.debug(
|
1810
|
+
"Task Error Message #{errormsg['majorErrorCode']} - " +
|
1811
|
+
"#{errormsg['message']}"
|
1812
|
+
)
|
1813
|
+
errormsg =
|
1814
|
+
"Error code #{errormsg['majorErrorCode']} - #{errormsg['message']}"
|
1535
1815
|
end
|
1536
1816
|
|
1537
|
-
{
|
1817
|
+
{
|
1818
|
+
:status => task[:status],
|
1819
|
+
:errormsg => errormsg,
|
1820
|
+
:start_time => task[:start_time],
|
1821
|
+
:end_time => task[:end_time]
|
1822
|
+
}
|
1538
1823
|
end
|
1539
1824
|
|
1540
1825
|
##
|
1541
1826
|
# Set vApp Network Config
|
1542
|
-
def set_vapp_network_config(
|
1827
|
+
def set_vapp_network_config(vapp_id, network_name, config = {})
|
1543
1828
|
builder = Nokogiri::XML::Builder.new do |xml|
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
1549
|
-
xml.
|
1550
|
-
xml.
|
1551
|
-
|
1552
|
-
|
1829
|
+
xml.NetworkConfigSection(
|
1830
|
+
'xmlns' => 'http://www.vmware.com/vcloud/v1.5',
|
1831
|
+
'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1'
|
1832
|
+
) {
|
1833
|
+
xml['ovf'].Info 'Network configuration'
|
1834
|
+
xml.NetworkConfig('networkName' => network_name) {
|
1835
|
+
xml.Configuration {
|
1836
|
+
xml.FenceMode(config[:fence_mode] || 'isolated')
|
1837
|
+
xml.RetainNetInfoAcrossDeployments(config[:retain_net] || false)
|
1838
|
+
xml.ParentNetwork('href' => config[:parent_network])
|
1839
|
+
}
|
1553
1840
|
}
|
1554
1841
|
}
|
1555
|
-
}
|
1556
1842
|
end
|
1557
1843
|
|
1558
1844
|
params = {
|
1559
|
-
'method'
|
1560
|
-
'command' => "/vApp/vapp-#{
|
1845
|
+
'method' => :put,
|
1846
|
+
'command' => "/vApp/vapp-#{vapp_id}/networkConfigSection"
|
1561
1847
|
}
|
1562
1848
|
|
1563
|
-
|
1849
|
+
_response, headers = send_request(
|
1850
|
+
params,
|
1851
|
+
builder.to_xml,
|
1852
|
+
'application/vnd.vmware.vcloud.networkConfigSection+xml'
|
1853
|
+
)
|
1564
1854
|
|
1565
|
-
task_id = headers[
|
1855
|
+
task_id = headers['Location'].gsub("#{@api_url}/task/", '')
|
1566
1856
|
task_id
|
1567
1857
|
end
|
1568
1858
|
|
1569
1859
|
##
|
1570
1860
|
# Set VM Network Config
|
1571
|
-
def set_vm_network_config(
|
1861
|
+
def set_vm_network_config(vm_id, network_name, config = {})
|
1572
1862
|
builder = Nokogiri::XML::Builder.new do |xml|
|
1573
|
-
|
1574
|
-
|
1575
|
-
|
1576
|
-
|
1577
|
-
|
1578
|
-
|
1579
|
-
|
1580
|
-
|
1581
|
-
|
1582
|
-
|
1863
|
+
xml.NetworkConnectionSection(
|
1864
|
+
'xmlns' => 'http://www.vmware.com/vcloud/v1.5',
|
1865
|
+
'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1') {
|
1866
|
+
xml['ovf'].Info 'VM Network configuration'
|
1867
|
+
xml.PrimaryNetworkConnectionIndex(config[:primary_index] || 0)
|
1868
|
+
xml.NetworkConnection(
|
1869
|
+
'network' => network_name,
|
1870
|
+
'needsCustomization' => true
|
1871
|
+
) {
|
1872
|
+
xml.NetworkConnectionIndex(config[:network_index] || 0)
|
1873
|
+
xml.IpAddress config[:ip] if config[:ip]
|
1874
|
+
xml.IsConnected(config[:is_connected] || true)
|
1875
|
+
xml.IpAddressAllocationMode config[:ip_allocation_mode] if config[:ip_allocation_mode]
|
1876
|
+
}
|
1583
1877
|
}
|
1584
|
-
}
|
1585
1878
|
end
|
1586
1879
|
|
1587
1880
|
params = {
|
1588
|
-
'method'
|
1589
|
-
'command' => "/vApp/vm-#{
|
1881
|
+
'method' => :put,
|
1882
|
+
'command' => "/vApp/vm-#{vm_id}/networkConnectionSection"
|
1590
1883
|
}
|
1591
1884
|
|
1592
|
-
|
1885
|
+
_response, headers = send_request(
|
1886
|
+
params,
|
1887
|
+
builder.to_xml,
|
1888
|
+
'application/vnd.vmware.vcloud.networkConnectionSection+xml'
|
1889
|
+
)
|
1593
1890
|
|
1594
|
-
task_id = headers[
|
1891
|
+
task_id = headers['Location'].gsub("#{@api_url}/task/", '')
|
1595
1892
|
task_id
|
1596
1893
|
end
|
1597
1894
|
|
1598
|
-
|
1599
1895
|
##
|
1600
1896
|
# Set VM Guest Customization Config
|
1601
|
-
def set_vm_guest_customization(
|
1897
|
+
def set_vm_guest_customization(vm_id, computer_name, config = {})
|
1602
1898
|
builder = Nokogiri::XML::Builder.new do |xml|
|
1603
1899
|
xml.GuestCustomizationSection(
|
1604
|
-
|
1605
|
-
|
1606
|
-
xml['ovf'].Info
|
1900
|
+
'xmlns' => 'http://www.vmware.com/vcloud/v1.5',
|
1901
|
+
'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1') {
|
1902
|
+
xml['ovf'].Info 'VM Guest Customization configuration'
|
1607
1903
|
xml.Enabled config[:enabled] if config[:enabled]
|
1608
1904
|
xml.AdminPasswordEnabled config[:admin_passwd_enabled] if config[:admin_passwd_enabled]
|
1609
1905
|
xml.AdminPassword config[:admin_passwd] if config[:admin_passwd]
|
@@ -1612,26 +1908,32 @@ module VagrantPlugins
|
|
1612
1908
|
end
|
1613
1909
|
|
1614
1910
|
params = {
|
1615
|
-
'method'
|
1616
|
-
'command' => "/vApp/vm-#{
|
1911
|
+
'method' => :put,
|
1912
|
+
'command' => "/vApp/vm-#{vm_id}/guestCustomizationSection"
|
1617
1913
|
}
|
1618
1914
|
|
1619
|
-
|
1620
|
-
|
1915
|
+
_response, headers = send_request(
|
1916
|
+
params,
|
1917
|
+
builder.to_xml,
|
1918
|
+
'application/vnd.vmware.vcloud.guestCustomizationSection+xml'
|
1919
|
+
)
|
1920
|
+
task_id = headers['Location'].gsub("#{@api_url}/task/", '')
|
1621
1921
|
task_id
|
1622
1922
|
end
|
1623
1923
|
|
1624
1924
|
##
|
1625
1925
|
# Fetch details about a given VM
|
1626
|
-
def get_vm(
|
1926
|
+
def get_vm(vm_id)
|
1627
1927
|
params = {
|
1628
|
-
'method'
|
1629
|
-
'command' => "/vApp/vm-#{
|
1928
|
+
'method' => :get,
|
1929
|
+
'command' => "/vApp/vm-#{vm_id}"
|
1630
1930
|
}
|
1631
1931
|
|
1632
|
-
response,
|
1932
|
+
response, _headers = send_request(params)
|
1633
1933
|
|
1634
|
-
os_desc = response.css(
|
1934
|
+
os_desc = response.css(
|
1935
|
+
'ovf|OperatingSystemSection ovf|Description'
|
1936
|
+
).first.text
|
1635
1937
|
|
1636
1938
|
networks = {}
|
1637
1939
|
response.css('NetworkConnection').each do |network|
|
@@ -1639,31 +1941,51 @@ module VagrantPlugins
|
|
1639
1941
|
ip = ip.text if ip
|
1640
1942
|
|
1641
1943
|
networks[network['network']] = {
|
1642
|
-
:index
|
1643
|
-
|
1644
|
-
|
1645
|
-
:
|
1646
|
-
:
|
1944
|
+
:index => network.css(
|
1945
|
+
'NetworkConnectionIndex'
|
1946
|
+
).first.text,
|
1947
|
+
:ip => ip,
|
1948
|
+
:is_connected => network.css(
|
1949
|
+
'IsConnected'
|
1950
|
+
).first.text,
|
1951
|
+
:mac_address => network.css(
|
1952
|
+
'MACAddress'
|
1953
|
+
).first.text,
|
1954
|
+
:ip_allocation_mode => network.css(
|
1955
|
+
'IpAddressAllocationMode'
|
1956
|
+
).first.text
|
1647
1957
|
}
|
1648
1958
|
end
|
1649
1959
|
|
1650
|
-
admin_password = response.css(
|
1960
|
+
admin_password = response.css(
|
1961
|
+
'GuestCustomizationSection AdminPassword'
|
1962
|
+
).first
|
1651
1963
|
admin_password = admin_password.text if admin_password
|
1652
1964
|
|
1965
|
+
# make the lines shorter by adjusting the nokogiri css namespace
|
1966
|
+
guest_css = response.css('GuestCustomizationSection')
|
1653
1967
|
guest_customizations = {
|
1654
|
-
:enabled
|
1655
|
-
:admin_passwd_enabled
|
1656
|
-
|
1657
|
-
|
1658
|
-
:
|
1659
|
-
|
1968
|
+
:enabled => guest_css.css('Enabled').first.text,
|
1969
|
+
:admin_passwd_enabled => guest_css.css(
|
1970
|
+
'AdminPasswordEnabled'
|
1971
|
+
).first.text,
|
1972
|
+
:admin_passwd_auto => guest_css.css(
|
1973
|
+
'AdminPasswordAuto'
|
1974
|
+
).first.text,
|
1975
|
+
:admin_passwd => admin_password,
|
1976
|
+
:reset_passwd_required => guest_css.css(
|
1977
|
+
'ResetPasswordRequired'
|
1978
|
+
).first.text,
|
1979
|
+
:computer_name => guest_css.css('ComputerName').first.text
|
1660
1980
|
}
|
1661
1981
|
|
1662
|
-
{
|
1982
|
+
{
|
1983
|
+
:os_desc => os_desc,
|
1984
|
+
:networks => networks,
|
1985
|
+
:guest_customizations => guest_customizations
|
1986
|
+
}
|
1663
1987
|
end
|
1664
|
-
|
1665
|
-
|
1666
|
-
|
1667
|
-
|
1668
|
-
end
|
1669
|
-
end
|
1988
|
+
end # Class Version 5.1
|
1989
|
+
end # Module Driver
|
1990
|
+
end # Module VCloud
|
1991
|
+
end # Module VagrantPlugins
|