vcloud-rest 0.3.0 → 1.0.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/CHANGELOG.md +60 -0
- data/README.md +21 -9
- data/lib/vcloud-rest/connection.rb +72 -890
- data/lib/vcloud-rest/vcloud/catalog.rb +113 -0
- data/lib/vcloud-rest/vcloud/network.rb +78 -0
- data/lib/vcloud-rest/vcloud/org.rb +145 -0
- data/lib/vcloud-rest/vcloud/ovf.rb +251 -0
- data/lib/vcloud-rest/vcloud/vapp.rb +428 -0
- data/lib/vcloud-rest/vcloud/vapp_networking.rb +331 -0
- data/lib/vcloud-rest/vcloud/vdc.rb +67 -0
- data/lib/vcloud-rest/vcloud/vm.rb +396 -0
- data/lib/vcloud-rest/version.rb +3 -0
- metadata +18 -9
@@ -0,0 +1,396 @@
|
|
1
|
+
module VCloudClient
|
2
|
+
class Connection
|
3
|
+
##
|
4
|
+
# Retrieve information (i.e., memory and CPUs)
|
5
|
+
def get_vm_info(vmid)
|
6
|
+
params = {
|
7
|
+
'method' => :get,
|
8
|
+
'command' => "/vApp/vm-#{vmid}/virtualHardwareSection"
|
9
|
+
}
|
10
|
+
|
11
|
+
response, headers = send_request(params)
|
12
|
+
|
13
|
+
result = {}
|
14
|
+
response.css("ovf|Item [vcloud|href]").each do |item|
|
15
|
+
item_name = item.attribute('href').text.gsub(/.*\/vApp\/vm\-(\w+(-?))+\/virtualHardwareSection\//, "")
|
16
|
+
name = item.css("rasd|ElementName")
|
17
|
+
name = name.text unless name.nil?
|
18
|
+
|
19
|
+
description = item.css("rasd|Description")
|
20
|
+
description = description.text unless description.nil?
|
21
|
+
|
22
|
+
result[item_name] = {
|
23
|
+
:name => name,
|
24
|
+
:description => description
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
result
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Retrieve information about Disks
|
33
|
+
def get_vm_disk_info(vmid)
|
34
|
+
response, headers = __get_disk_info(vmid)
|
35
|
+
|
36
|
+
disks = []
|
37
|
+
response.css("Item").each do |entry|
|
38
|
+
# Pick only entries with node "HostResource"
|
39
|
+
resource = entry.css("rasd|HostResource").first
|
40
|
+
next unless resource
|
41
|
+
|
42
|
+
name = entry.css("rasd|ElementName").first
|
43
|
+
name = name.text unless name.nil?
|
44
|
+
capacity = resource.attribute("capacity").text
|
45
|
+
|
46
|
+
disks << {
|
47
|
+
:name => name,
|
48
|
+
:capacity => "#{capacity} MB"
|
49
|
+
}
|
50
|
+
end
|
51
|
+
disks
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Set information about Disks
|
56
|
+
#
|
57
|
+
# Disks can be added, deleted or modified
|
58
|
+
def set_vm_disk_info(vmid, disk_info={})
|
59
|
+
get_response, headers = __get_disk_info(vmid)
|
60
|
+
|
61
|
+
if disk_info[:add]
|
62
|
+
data = add_disk(get_response, disk_info)
|
63
|
+
else
|
64
|
+
data = edit_disk(get_response, disk_info)
|
65
|
+
end
|
66
|
+
|
67
|
+
params = {
|
68
|
+
'method' => :put,
|
69
|
+
'command' => "/vApp/vm-#{vmid}/virtualHardwareSection/disks"
|
70
|
+
}
|
71
|
+
put_response, headers = send_request(params, data, "application/vnd.vmware.vcloud.rasdItemsList+xml")
|
72
|
+
|
73
|
+
task_id = headers[:location].gsub(/.*\/task\//, "")
|
74
|
+
task_id
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Set VM CPUs
|
79
|
+
def set_vm_cpus(vmid, cpu_number)
|
80
|
+
params = {
|
81
|
+
'method' => :get,
|
82
|
+
'command' => "/vApp/vm-#{vmid}/virtualHardwareSection/cpu"
|
83
|
+
}
|
84
|
+
|
85
|
+
get_response, headers = send_request(params)
|
86
|
+
|
87
|
+
# Change attributes from the previous invocation
|
88
|
+
get_response.css("rasd|ElementName").first.content = "#{cpu_number} virtual CPU(s)"
|
89
|
+
get_response.css("rasd|VirtualQuantity").first.content = cpu_number
|
90
|
+
|
91
|
+
params['method'] = :put
|
92
|
+
put_response, headers = send_request(params, get_response.to_xml, "application/vnd.vmware.vcloud.rasdItem+xml")
|
93
|
+
|
94
|
+
task_id = headers[:location].gsub(/.*\/task\//, "")
|
95
|
+
task_id
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# Set VM RAM
|
100
|
+
def set_vm_ram(vmid, memory_size)
|
101
|
+
params = {
|
102
|
+
'method' => :get,
|
103
|
+
'command' => "/vApp/vm-#{vmid}/virtualHardwareSection/memory"
|
104
|
+
}
|
105
|
+
|
106
|
+
get_response, headers = send_request(params)
|
107
|
+
|
108
|
+
# Change attributes from the previous invocation
|
109
|
+
get_response.css("rasd|ElementName").first.content = "#{memory_size} MB of memory"
|
110
|
+
get_response.css("rasd|VirtualQuantity").first.content = memory_size
|
111
|
+
|
112
|
+
params['method'] = :put
|
113
|
+
put_response, headers = send_request(params, get_response.to_xml, "application/vnd.vmware.vcloud.rasdItem+xml")
|
114
|
+
|
115
|
+
task_id = headers[:location].gsub(/.*\/task\//, "")
|
116
|
+
task_id
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# Set VM Network Config
|
121
|
+
def set_vm_network_config(vmid, network_name, config={})
|
122
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
123
|
+
xml.NetworkConnectionSection(
|
124
|
+
"xmlns" => "http://www.vmware.com/vcloud/v1.5",
|
125
|
+
"xmlns:ovf" => "http://schemas.dmtf.org/ovf/envelope/1") {
|
126
|
+
xml['ovf'].Info "VM Network configuration"
|
127
|
+
xml.PrimaryNetworkConnectionIndex(config[:primary_index] || 0)
|
128
|
+
xml.NetworkConnection("network" => network_name, "needsCustomization" => true) {
|
129
|
+
xml.NetworkConnectionIndex(config[:network_index] || 0)
|
130
|
+
xml.IpAddress config[:ip] if config[:ip]
|
131
|
+
xml.IsConnected(config[:is_connected] || true)
|
132
|
+
xml.IpAddressAllocationMode config[:ip_allocation_mode] if config[:ip_allocation_mode]
|
133
|
+
}
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
params = {
|
138
|
+
'method' => :put,
|
139
|
+
'command' => "/vApp/vm-#{vmid}/networkConnectionSection"
|
140
|
+
}
|
141
|
+
|
142
|
+
response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.networkConnectionSection+xml")
|
143
|
+
|
144
|
+
task_id = headers[:location].gsub(/.*\/task\//, "")
|
145
|
+
task_id
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
##
|
150
|
+
# Set VM Guest Customization Config
|
151
|
+
def set_vm_guest_customization(vmid, computer_name, config={})
|
152
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
153
|
+
xml.GuestCustomizationSection(
|
154
|
+
"xmlns" => "http://www.vmware.com/vcloud/v1.5",
|
155
|
+
"xmlns:ovf" => "http://schemas.dmtf.org/ovf/envelope/1") {
|
156
|
+
xml['ovf'].Info "VM Guest Customization configuration"
|
157
|
+
xml.Enabled config[:enabled] if config[:enabled]
|
158
|
+
xml.AdminPasswordEnabled config[:admin_passwd_enabled] if config[:admin_passwd_enabled]
|
159
|
+
xml.AdminPassword config[:admin_passwd] if config[:admin_passwd]
|
160
|
+
xml.CustomizationScript config[:customization_script] if config[:customization_script]
|
161
|
+
xml.ComputerName computer_name
|
162
|
+
}
|
163
|
+
end
|
164
|
+
|
165
|
+
params = {
|
166
|
+
'method' => :put,
|
167
|
+
'command' => "/vApp/vm-#{vmid}/guestCustomizationSection"
|
168
|
+
}
|
169
|
+
|
170
|
+
response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.guestCustomizationSection+xml")
|
171
|
+
|
172
|
+
task_id = headers[:location].gsub(/.*\/task\//, "")
|
173
|
+
task_id
|
174
|
+
end
|
175
|
+
|
176
|
+
##
|
177
|
+
# Force a guest customization
|
178
|
+
def force_customization_vm(vmId)
|
179
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
180
|
+
xml.DeployVAppParams(
|
181
|
+
"xmlns" => "http://www.vmware.com/vcloud/v1.5",
|
182
|
+
"forceCustomization" => "true")
|
183
|
+
end
|
184
|
+
|
185
|
+
params = {
|
186
|
+
"method" => :post,
|
187
|
+
"command" => "/vApp/vm-#{vmId}/action/deploy"
|
188
|
+
}
|
189
|
+
|
190
|
+
response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.deployVAppParams+xml")
|
191
|
+
task_id = headers[:location].gsub(/.*\/task\//, "")
|
192
|
+
task_id
|
193
|
+
end
|
194
|
+
|
195
|
+
def rename_vm(vmId, name)
|
196
|
+
params = {
|
197
|
+
'method' => :get,
|
198
|
+
'command' => "/vApp/vm-#{vmId}"
|
199
|
+
}
|
200
|
+
|
201
|
+
response, headers = send_request(params)
|
202
|
+
response.css('Vm').attribute("name").content = name
|
203
|
+
|
204
|
+
params['method'] = :put
|
205
|
+
response, headers = send_request(params, response.to_xml,
|
206
|
+
"application/vnd.vmware.vcloud.vm+xml")
|
207
|
+
task_id = headers[:location].gsub(/.*\/task\//, "")
|
208
|
+
task_id
|
209
|
+
end
|
210
|
+
|
211
|
+
##
|
212
|
+
# Fetch details about a given VM
|
213
|
+
def get_vm(vmId)
|
214
|
+
params = {
|
215
|
+
'method' => :get,
|
216
|
+
'command' => "/vApp/vm-#{vmId}"
|
217
|
+
}
|
218
|
+
|
219
|
+
response, headers = send_request(params)
|
220
|
+
|
221
|
+
vm_name = response.css('Vm').attribute("name")
|
222
|
+
vm_name = vm_name.text unless vm_name.nil?
|
223
|
+
|
224
|
+
status = convert_vapp_status(response.css('Vm').attribute("status").text)
|
225
|
+
|
226
|
+
os_desc = response.css('ovf|OperatingSystemSection ovf|Description').first.text
|
227
|
+
|
228
|
+
networks = {}
|
229
|
+
response.css('NetworkConnection').each do |network|
|
230
|
+
ip = network.css('IpAddress').first
|
231
|
+
ip = ip.text if ip
|
232
|
+
|
233
|
+
external_ip = network.css('ExternalIpAddress').first
|
234
|
+
external_ip = external_ip.text if external_ip
|
235
|
+
|
236
|
+
networks[network['network']] = {
|
237
|
+
:index => network.css('NetworkConnectionIndex').first.text,
|
238
|
+
:ip => ip,
|
239
|
+
:external_ip => external_ip,
|
240
|
+
:is_connected => network.css('IsConnected').first.text,
|
241
|
+
:mac_address => network.css('MACAddress').first.text,
|
242
|
+
:ip_allocation_mode => network.css('IpAddressAllocationMode').first.text
|
243
|
+
}
|
244
|
+
end
|
245
|
+
|
246
|
+
admin_password = response.css('GuestCustomizationSection AdminPassword').first
|
247
|
+
admin_password = admin_password.text if admin_password
|
248
|
+
|
249
|
+
guest_customizations = {
|
250
|
+
:enabled => response.css('GuestCustomizationSection Enabled').first.text,
|
251
|
+
:admin_passwd_enabled => response.css('GuestCustomizationSection AdminPasswordEnabled').first.text,
|
252
|
+
:admin_passwd_auto => response.css('GuestCustomizationSection AdminPasswordAuto').first.text,
|
253
|
+
:admin_passwd => admin_password,
|
254
|
+
:reset_passwd_required => response.css('GuestCustomizationSection ResetPasswordRequired').first.text,
|
255
|
+
:computer_name => response.css('GuestCustomizationSection ComputerName').first.text
|
256
|
+
}
|
257
|
+
|
258
|
+
{ :id => vmId,
|
259
|
+
:vm_name => vm_name, :os_desc => os_desc, :networks => networks,
|
260
|
+
:guest_customizations => guest_customizations, :status => status
|
261
|
+
}
|
262
|
+
end
|
263
|
+
|
264
|
+
##
|
265
|
+
# Friendly helper method to fetch a vApp by name
|
266
|
+
# - Organization object
|
267
|
+
# - Organization VDC Name
|
268
|
+
# - vApp Name
|
269
|
+
# - VM Name
|
270
|
+
def get_vm_by_name(organization, vdcName, vAppName, vmName)
|
271
|
+
result = nil
|
272
|
+
|
273
|
+
get_vapp_by_name(organization, vdcName, vAppName)[:vms_hash].each do |key, values|
|
274
|
+
if key.downcase == vmName.downcase
|
275
|
+
result = get_vm(values[:id])
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
result
|
280
|
+
end
|
281
|
+
|
282
|
+
##
|
283
|
+
# Shutdown a given vm
|
284
|
+
def poweroff_vm(vmId)
|
285
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
286
|
+
xml.UndeployVAppParams(
|
287
|
+
"xmlns" => "http://www.vmware.com/vcloud/v1.5") {
|
288
|
+
xml.UndeployPowerAction 'powerOff'
|
289
|
+
}
|
290
|
+
end
|
291
|
+
|
292
|
+
params = {
|
293
|
+
'method' => :post,
|
294
|
+
'command' => "/vApp/vm-#{vmId}/action/undeploy"
|
295
|
+
}
|
296
|
+
|
297
|
+
response, headers = send_request(params, builder.to_xml,
|
298
|
+
"application/vnd.vmware.vcloud.undeployVAppParams+xml")
|
299
|
+
task_id = headers[:location].gsub(/.*\/task\//, "")
|
300
|
+
task_id
|
301
|
+
end
|
302
|
+
|
303
|
+
##
|
304
|
+
# Suspend a given vm
|
305
|
+
def suspend_vm(vmId)
|
306
|
+
power_action(vmId, 'suspend', :vm)
|
307
|
+
end
|
308
|
+
|
309
|
+
##
|
310
|
+
# reboot a given vm
|
311
|
+
# This will basically initial a guest OS reboot, and will only work if
|
312
|
+
# VMware-tools are installed on the underlying VMs.
|
313
|
+
# vShield Edge devices are not affected
|
314
|
+
def reboot_vm(vmId)
|
315
|
+
power_action(vmId, 'reboot', :vm)
|
316
|
+
end
|
317
|
+
|
318
|
+
##
|
319
|
+
# reset a given vm
|
320
|
+
def reset_vm(vmId)
|
321
|
+
power_action(vmId, 'reset', :vm)
|
322
|
+
end
|
323
|
+
|
324
|
+
##
|
325
|
+
# Boot a given vm
|
326
|
+
def poweron_vm(vmId)
|
327
|
+
power_action(vmId, 'powerOn', :vm)
|
328
|
+
end
|
329
|
+
|
330
|
+
private
|
331
|
+
def add_disk(source_xml, disk_info)
|
332
|
+
disks_count = source_xml.css("Item").css("rasd|HostResource").count
|
333
|
+
|
334
|
+
# FIXME: This is a hack, but dealing with nokogiri APIs can be quite
|
335
|
+
# frustrating sometimes...
|
336
|
+
sibling = source_xml.css("Item").first
|
337
|
+
new_disk = Nokogiri::XML::Node.new "PLACEHOLDER", sibling.parent
|
338
|
+
sibling.add_next_sibling(new_disk)
|
339
|
+
result = source_xml.to_xml
|
340
|
+
|
341
|
+
result.gsub("<PLACEHOLDER/>", """
|
342
|
+
<Item>
|
343
|
+
<rasd:AddressOnParent>#{disks_count}</rasd:AddressOnParent>
|
344
|
+
<rasd:Description>Hard disk</rasd:Description>
|
345
|
+
<rasd:ElementName>Hard disk #{disks_count + 1}</rasd:ElementName>
|
346
|
+
<rasd:HostResource
|
347
|
+
xmlns:ns12=\"http://www.vmware.com/vcloud/v1.5\"
|
348
|
+
ns12:capacity=\"#{disk_info[:disk_size]}\"
|
349
|
+
ns12:busSubType=\"lsilogic\"
|
350
|
+
ns12:busType=\"6\"/>
|
351
|
+
<rasd:InstanceID>200#{disks_count}</rasd:InstanceID>
|
352
|
+
<rasd:Parent>1</rasd:Parent>
|
353
|
+
<rasd:ResourceType>17</rasd:ResourceType>
|
354
|
+
</Item>""")
|
355
|
+
end
|
356
|
+
|
357
|
+
def edit_disk(source_xml, disk_info)
|
358
|
+
changed = false
|
359
|
+
|
360
|
+
source_xml.css("Item").each do |entry|
|
361
|
+
# Pick only entries with node "HostResource"
|
362
|
+
resource = entry.css("rasd|HostResource").first
|
363
|
+
next unless resource
|
364
|
+
|
365
|
+
name = entry.css("rasd|ElementName").first
|
366
|
+
name = name.text unless name.nil?
|
367
|
+
next unless name == disk_info[:disk_name]
|
368
|
+
|
369
|
+
changed = true
|
370
|
+
|
371
|
+
if disk_info[:delete]
|
372
|
+
entry.remove
|
373
|
+
else
|
374
|
+
# Set disk size
|
375
|
+
resource.attribute("capacity").content = disk_info[:disk_size]
|
376
|
+
end
|
377
|
+
break
|
378
|
+
end
|
379
|
+
|
380
|
+
unless changed
|
381
|
+
@logger.warn "Disk #{disk_info[:disk_name]} not found."
|
382
|
+
raise WrongItemIDError, "Disk #{disk_info[:disk_name]} not found."
|
383
|
+
end
|
384
|
+
source_xml.to_xml
|
385
|
+
end
|
386
|
+
|
387
|
+
def __get_disk_info(vmid)
|
388
|
+
params = {
|
389
|
+
'method' => :get,
|
390
|
+
'command' => "/vApp/vm-#{vmid}/virtualHardwareSection/disks"
|
391
|
+
}
|
392
|
+
|
393
|
+
send_request(params)
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vcloud-rest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stefano Tortarolo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-11-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 1.
|
19
|
+
version: 1.5.10
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 1.
|
26
|
+
version: 1.5.10
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rest-client
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - ~>
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 1.
|
61
|
+
version: 1.2.0
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - ~>
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 1.
|
68
|
+
version: 1.2.0
|
69
69
|
description: Ruby bindings to create, list and manage vCloud servers
|
70
70
|
email:
|
71
71
|
- stefano.tortarolo@gmail.com
|
@@ -77,6 +77,15 @@ files:
|
|
77
77
|
- README.md
|
78
78
|
- LICENSE
|
79
79
|
- lib/vcloud-rest/connection.rb
|
80
|
+
- lib/vcloud-rest/vcloud/catalog.rb
|
81
|
+
- lib/vcloud-rest/vcloud/network.rb
|
82
|
+
- lib/vcloud-rest/vcloud/org.rb
|
83
|
+
- lib/vcloud-rest/vcloud/ovf.rb
|
84
|
+
- lib/vcloud-rest/vcloud/vapp.rb
|
85
|
+
- lib/vcloud-rest/vcloud/vapp_networking.rb
|
86
|
+
- lib/vcloud-rest/vcloud/vdc.rb
|
87
|
+
- lib/vcloud-rest/vcloud/vm.rb
|
88
|
+
- lib/vcloud-rest/version.rb
|
80
89
|
homepage: https://github.com/astratto/vcloud-rest
|
81
90
|
licenses:
|
82
91
|
- Apache 2.0
|
@@ -97,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
97
106
|
version: '0'
|
98
107
|
requirements: []
|
99
108
|
rubyforge_project:
|
100
|
-
rubygems_version: 2.
|
109
|
+
rubygems_version: 2.1.11
|
101
110
|
signing_key:
|
102
111
|
specification_version: 4
|
103
112
|
summary: Unofficial ruby bindings for VMWare vCloud's API
|