Class: VCloudClient::Connection

Inherits:
Object
  • Object
show all
Defined in:
lib/vcloud-rest/connection.rb,
lib/vcloud-rest/vcloud/vm.rb,
lib/vcloud-rest/vcloud/org.rb,
lib/vcloud-rest/vcloud/ovf.rb,
lib/vcloud-rest/vcloud/vdc.rb,
lib/vcloud-rest/vcloud/disk.rb,
lib/vcloud-rest/vcloud/vapp.rb,
lib/vcloud-rest/vcloud/media.rb,
lib/vcloud-rest/vcloud/catalog.rb,
lib/vcloud-rest/vcloud/network.rb,
lib/vcloud-rest/vcloud/extensibility.rb,
lib/vcloud-rest/vcloud/vapp_networking.rb

Overview

Main class to access vCloud rest APIs

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(host, username, password, org_name, api_version) ⇒ Connection

Returns a new instance of Connection.



52
53
54
55
56
57
58
59
60
61
62
# File 'lib/vcloud-rest/connection.rb', line 52

def initialize(host, username, password, org_name, api_version)
  @host = host
  @api_url = "#{host}/api"
  @host_url = "#{host}"
  @username = username
  @password = password
  @org_name = org_name
  @api_version = (api_version || "5.1")

  init_logger
end

Instance Attribute Details

#api_urlObject (readonly)

Returns the value of attribute api_url.



49
50
51
# File 'lib/vcloud-rest/connection.rb', line 49

def api_url
  @api_url
end

#auth_keyObject (readonly)

Returns the value of attribute auth_key.



49
50
51
# File 'lib/vcloud-rest/connection.rb', line 49

def auth_key
  @auth_key
end

#extensibilityObject (readonly)

Returns the value of attribute extensibility.



50
51
52
# File 'lib/vcloud-rest/connection.rb', line 50

def extensibility
  @extensibility
end

Instance Method Details

#acquire_ticket_vm(vmId) ⇒ Object

Retrieve a screen ticket that you can use with the VMRC browser plug-in to gain access to the console of a running VM.



478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
# File 'lib/vcloud-rest/vcloud/vm.rb', line 478

def acquire_ticket_vm(vmId)

  params = {
    'method' => :post,
    'command' => "/vApp/vm-#{vmId}/screen/action/acquireTicket"
  }

  response, headers = send_request(params)

  screen_ticket = response.css("ScreenTicket").text

  result = {}

  if screen_ticket =~ /mks:\/\/([^\/]*)\/([^\?]*)\?ticket=(.*)/
    result = { host: $1, moid: $2, token: $3 }
    result[:token] = URI.unescape result[:token]
  end
  result
end

#add_internal_network_to_vapp(vAppId, network, config) ⇒ Object

Add an existing network (from Org) to vApp



67
68
69
70
# File 'lib/vcloud-rest/vcloud/vapp_networking.rb', line 67

def add_internal_network_to_vapp(vAppId, network, config)
  network_section = generate_network_section(vAppId, network, config, :internal)
  add_network_to_vapp(vAppId, network_section)
end

#add_org_network_to_vapp(vAppId, network, config) ⇒ Object

Add an existing network (from Org) to vApp



60
61
62
63
# File 'lib/vcloud-rest/vcloud/vapp_networking.rb', line 60

def add_org_network_to_vapp(vAppId, network, config)
  network_section = generate_network_section(vAppId, network, config, :external)
  add_network_to_vapp(vAppId, network_section)
end

#add_vm_network(vmId, network, config = {}) ⇒ Object

Add a new network to a VM



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/vcloud-rest/vcloud/vm.rb', line 176

def add_vm_network(vmId, network, config={})
  params = {
    'method' => :get,
    'command' => "/vApp/vm-#{vmId}/networkConnectionSection"
  }

  netconfig_response, headers = send_request(params)

  parent_section = netconfig_response.css('NetworkConnectionSection').first

  # For some reasons these elements must be removed
  netconfig_response.css("Link").each {|n| n.remove}

  # Delete placeholder network if present (since vcloud 5.5 has been removed)
  none_network = netconfig_response.css('NetworkConnection').find{|n| n.attribute('network').text == 'none'}
  none_network.remove if none_network

  networks_count = netconfig_response.css('NetworkConnection').count

  primary_index_node = netconfig_response.css('PrimaryNetworkConnectionIndex').first
  unless primary_index_node
    primary_index_node = Nokogiri::XML::Node.new "PrimaryNetworkConnectionIndex", parent_section
    parent_section.add_child(primary_index_node)
  end
  primary_index_node.content = config[:primary_index] || 0

  new_network = Nokogiri::XML::Node.new "NetworkConnection", parent_section
  new_network["network"] = network[:name]
  new_network["needsCustomization"] = true

  idx_node = Nokogiri::XML::Node.new "NetworkConnectionIndex", new_network
  idx_node.content = config[:network_index] || networks_count
  new_network.add_child(idx_node)

  if config[:ip]
    ip_node = Nokogiri::XML::Node.new "IpAddress", new_network
    ip_node.content = config[:ip]
    new_network.add_child(ip_node)
  end

  is_connected_node = Nokogiri::XML::Node.new "IsConnected", new_network
  is_connected_node.content = config[:is_connected] || true
  new_network.add_child(is_connected_node)

  allocation_node = Nokogiri::XML::Node.new "IpAddressAllocationMode", new_network
  allocation_node.content = config[:ip_allocation_mode] || "POOL"
  new_network.add_child(allocation_node)

  parent_section.add_child(new_network)

  params = {
    'method' => :put,
    'command' => "/vApp/vm-#{vmId}/networkConnectionSection"
  }

  put_response, headers = send_request(params, netconfig_response.to_xml, "application/vnd.vmware.vcloud.networkConnectionSection+xml")

  task_id = headers[:location].gsub(/.*\/task\//, "")
  task_id
end

#add_vm_to_vapp(vapp, vm, network_config = {}) ⇒ Object

Create a new virtual machine from a template in an existing vApp.

Params:

  • vapp: the target vapp

  • vm: hash with template ID and new VM name

  • network_config: hash of the network configuration for the VM



337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'lib/vcloud-rest/vcloud/vapp.rb', line 337

def add_vm_to_vapp(vapp, vm, network_config={})
  builder = Nokogiri::XML::Builder.new do |xml|
    xml.RecomposeVAppParams(
      "xmlns" => "http://www.vmware.com/vcloud/v1.5",
      "xmlns:ovf" => "http://schemas.dmtf.org/ovf/envelope/1",
      "name" => vapp[:name]) {
        xml.SourcedItem {
          xml.Source("href" => "#{@api_url}/vAppTemplate/vm-#{vm[:template_id]}", "name" => vm[:vm_name])
          xml.InstantiationParams {
            xml.NetworkConnectionSection(
              "xmlns:ovf" => "http://schemas.dmtf.org/ovf/envelope/1",
              "type" => "application/vnd.vmware.vcloud.networkConnectionSection+xml",
              "href" => "#{@api_url}/vAppTemplate/vm-#{vm[:template_id]}/networkConnectionSection/") {
                xml['ovf'].Info "Network config for sourced item"
                xml.PrimaryNetworkConnectionIndex "0"
                xml.NetworkConnection("network" => network_config[:name]) {
                  xml.NetworkConnectionIndex "0"
                  xml.IsConnected "true"
                  xml.IpAddressAllocationMode(network_config[:ip_allocation_mode] || "POOL")
                }
              }
          }
          xml.NetworkAssignment("containerNetwork" => network_config[:name], "innerNetwork" => network_config[:name])
        }
        xml.AllEULAsAccepted "true"
      }
  end

  params = {
    "method" => :post,
    "command" => "/vApp/vapp-#{vapp[:id]}/action/recomposeVApp"
  }

  response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.recomposeVAppParams+xml")

  task = response.css("Task[operationName='vdcRecomposeVapp']").first
  task_id = task["href"].gsub(/.*\/task\//, "")
  task_id
end

#attach_disk_to_vm(disk_id, vm_id) ⇒ Object



70
71
72
# File 'lib/vcloud-rest/vcloud/disk.rb', line 70

def attach_disk_to_vm(disk_id, vm_id)
  disk_attach_action(disk_id, vm_id, 'attach')
end

#cancel_task(id) ⇒ Object

Cancel a given task

The task will be marked for cancellation



133
134
135
136
137
138
139
140
141
142
143
# File 'lib/vcloud-rest/vcloud/org.rb', line 133

def cancel_task(id)
  params = {
    'method' => :post,
    'command' => "/task/#{id}/action/cancel"
  }

  # Nothing useful is returned here
  # If return code is 20x return true
  send_request(params)
  true
end

#clone_vapp(vdc_id, source_vapp_id, name, deploy = "true", poweron = "false", linked = "false", delete_source = "false") ⇒ Object

Clone a vapp in a given VDC to a new Vapp



413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
# File 'lib/vcloud-rest/vcloud/vapp.rb', line 413

def clone_vapp(vdc_id, source_vapp_id, name, deploy="true", poweron="false", linked="false", delete_source="false")
  params = {
      "method" => :post,
      "command" => "/vdc/#{vdc_id}/action/cloneVApp"
  }
  builder = Nokogiri::XML::Builder.new do |xml|
    xml.CloneVAppParams(
        "xmlns" => "http://www.vmware.com/vcloud/v1.5",
        "name" => name,
        "deploy"=>  deploy,
        "linkedClone"=> linked,
        "powerOn"=> poweron
    ) {
      xml.Source "href" => "#{@api_url}/vApp/vapp-#{source_vapp_id}"
      xml.IsSourceDelete delete_source
    }
  end
  response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.cloneVAppParams+xml")

  vapp_id = headers[:location].gsub(/.*\/vApp\/vapp\-/, "")

  task = response.css("VApp Task[operationName='vdcCopyVapp']").first
  task_id = task["href"].gsub(/.*\/task\//, "")

  {:vapp_id => vapp_id, :task_id => task_id}
end

#compose_vapp_from_vm(vdc, vapp_name, vapp_description, vm_list = {}, network_config = {}) ⇒ Object

Compose a vapp using existing virtual machines

Params:

  • vdc: the associated VDC

  • vapp_name: name of the target vapp

  • vapp_description: description of the target vapp

  • vm_list: hash with IDs of the VMs to be used in the composing process

  • network_config: hash of the network configuration for the vapp



244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/vcloud-rest/vcloud/vapp.rb', line 244

def compose_vapp_from_vm(vdc, vapp_name, vapp_description, vm_list={}, network_config={})
  builder = Nokogiri::XML::Builder.new do |xml|
  xml.ComposeVAppParams(
    "xmlns" => "http://www.vmware.com/vcloud/v1.5",
    "xmlns:ovf" => "http://schemas.dmtf.org/ovf/envelope/1",
    "name" => vapp_name) {
    xml.Description vapp_description
    xml.InstantiationParams {
      xml.NetworkConfigSection {
        xml['ovf'].Info "Configuration parameters for logical networks"
        xml.NetworkConfig("networkName" => network_config[:name]) {
          xml.Configuration {
            xml.IpScopes {
              xml.IpScope {
                xml.IsInherited(network_config[:is_inherited] || "false")
                xml.Gateway network_config[:gateway]
                xml.Netmask network_config[:netmask]
                xml.Dns1 network_config[:dns1] if network_config[:dns1]
                xml.Dns2 network_config[:dns2] if network_config[:dns2]
                xml.DnsSuffix network_config[:dns_suffix] if network_config[:dns_suffix]
                xml.IpRanges {
                  xml.IpRange {
                    xml.StartAddress network_config[:start_address]
                    xml.EndAddress network_config[:end_address]
                  }
                }
              }
            }
            xml.ParentNetwork("href" => "#{@api_url}/network/#{network_config[:parent_network]}")
            xml.FenceMode network_config[:fence_mode]

            xml.Features {
              xml.FirewallService {
                xml.IsEnabled(network_config[:enable_firewall] || "false")
              }
              if network_config.has_key? :nat_type
                xml.NatService {
                  xml.IsEnabled "true"
                  xml.NatType network_config[:nat_type]
                  xml.Policy(network_config[:nat_policy_type] || "allowTraffic")
                }
              end
            }
          }
        }
      }
    }
    vm_list.each do |vm_name, vm_id|
      xml.SourcedItem {
        xml.Source("href" => "#{@api_url}/vAppTemplate/vm-#{vm_id}", "name" => vm_name)
        xml.InstantiationParams {
          xml.NetworkConnectionSection(
            "xmlns:ovf" => "http://schemas.dmtf.org/ovf/envelope/1",
            "type" => "application/vnd.vmware.vcloud.networkConnectionSection+xml",
            "href" => "#{@api_url}/vAppTemplate/vm-#{vm_id}/networkConnectionSection/") {
              xml['ovf'].Info "Network config for sourced item"
              xml.PrimaryNetworkConnectionIndex "0"
              xml.NetworkConnection("network" => network_config[:name]) {
                xml.NetworkConnectionIndex "0"
                xml.IsConnected "true"
                xml.IpAddressAllocationMode(network_config[:ip_allocation_mode] || "POOL")
            }
          }
        }
        xml.NetworkAssignment("containerNetwork" => network_config[:name], "innerNetwork" => network_config[:name])
      }
    end
    xml.AllEULAsAccepted "true"
  }
  end

  params = {
    "method" => :post,
    "command" => "/vdc/#{vdc}/action/composeVApp"
  }

  response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.composeVAppParams+xml")

  vapp_id = headers[:location].gsub(/.*\/vApp\/vapp\-/, "")

  task = response.css("VApp Task[operationName='vdcComposeVapp']").first
  task_id = task["href"].gsub(/.*\/task\//, "")

  { :vapp_id => vapp_id, :task_id => task_id }
end

#create_disk(name, size, vdc_id, description = "") ⇒ Object



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/vcloud-rest/vcloud/disk.rb', line 11

def create_disk(name, size, vdc_id, description="")
  builder = Nokogiri::XML::Builder.new do |xml|
    xml.DiskCreateParams(
      "xmlns" => "http://www.vmware.com/vcloud/v1.5") {
      xml.Disk("name" => name, "size" => size) {
        xml.Description description
      }
    }
  end

  params = {
    'method' => :post,
    'command' => "/vdc/#{vdc_id}/disk"
  }

  @logger.debug "Creating independent disk #{name} in VDC #{vdc_id}"
  response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.diskCreateParams+xml")

  # Get the id of the new disk
  disk_url = response.css("Disk").first[:href]
  disk_id = disk_url.gsub(/.*\/disk\//, "")
  @logger.debug "Independent disk created = #{disk_id}"

  task = response.css("Task[operationName='vdcCreateDisk']").first
  task_id = task["href"].gsub(/.*\/task\//, "")

  { :disk_id => disk_id, :task_id => task_id }
end

#create_snapshot(vmId, description = "New Snapshot") ⇒ Object

Create a new vm snapshot (overwrites any existing) DEPRECATED - use create_vapp_snapshot instead.



380
381
382
383
# File 'lib/vcloud-rest/vcloud/vapp.rb', line 380

def create_snapshot(vmId, description="New Snapshot")
  @logger.warn 'DEPRECATION WARNING: use [create,revert]_vapp_snapshot instead.'
  create_snapshot_action(vmId, description, :vapp)
end

#create_vapp_from_template(vdc, vapp_name, vapp_description, vapp_templateid, poweron = false) ⇒ Object

Create a vapp starting from a template

Params:

  • vdc: the associated VDC

  • vapp_name: name of the target vapp

  • vapp_description: description of the target vapp

  • vapp_templateid: ID of the vapp template



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/vcloud-rest/vcloud/vapp.rb', line 207

def create_vapp_from_template(vdc, vapp_name, vapp_description, vapp_templateid, poweron=false)
  builder = Nokogiri::XML::Builder.new do |xml|
  xml.InstantiateVAppTemplateParams(
    "xmlns" => "http://www.vmware.com/vcloud/v1.5",
    "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
    "xmlns:ovf" => "http://schemas.dmtf.org/ovf/envelope/1",
    "name" => vapp_name,
    "deploy" => "true",
    "powerOn" => poweron) {
    xml.Description vapp_description
    xml.Source("href" => "#{@api_url}/vAppTemplate/#{vapp_templateid}")
  }
  end

  params = {
    "method" => :post,
    "command" => "/vdc/#{vdc}/action/instantiateVAppTemplate"
  }

  response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.instantiateVAppTemplateParams+xml")

  vapp_id = headers[:location].gsub(/.*\/vApp\/vapp\-/, "")
  task = response.css("VApp Task[operationName='vdcInstantiateVapp']").first
  task_id = task["href"].gsub(/.*\/task\//, "")

  { :vapp_id => vapp_id, :task_id => task_id }
end

#create_vapp_snapshot(vmId, description = "New Snapshot") ⇒ Object

Create a new vm snapshot (overwrites any existing)



395
396
397
# File 'lib/vcloud-rest/vcloud/vapp.rb', line 395

def create_vapp_snapshot(vmId, description="New Snapshot")
  create_snapshot_action(vmId, description, :vapp)
end

#create_vm_snapshot(vmId, description = "New Snapshot") ⇒ Object

Create a new vm snapshot (overwrites any existing)



459
460
461
# File 'lib/vcloud-rest/vcloud/vm.rb', line 459

def create_vm_snapshot(vmId, description="New Snapshot")
  create_snapshot_action(vmId, description, :vm)
end

#delete_disk(disk_id) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/vcloud-rest/vcloud/disk.rb', line 78

def delete_disk(disk_id)
  params = {
    'method' => :delete,
    'command' => "/disk/#{disk_id}"
  }

  @logger.debug "Deleting independent disk #{disk_id}"
  response, headers = send_request(params)

  task = response.css("Task").first
  task_id = task["href"].gsub(/.*\/task\//, "")
end

#delete_vapp(vAppId) ⇒ Object

Delete a given vapp NOTE: It doesn’t verify that the vapp is shutdown



132
133
134
135
136
137
138
139
140
141
# File 'lib/vcloud-rest/vcloud/vapp.rb', line 132

def delete_vapp(vAppId)
  params = {
    'method' => :delete,
    'command' => "/vApp/vapp-#{vAppId}"
  }

  response, headers = send_request(params)
  task_id = headers[:location].gsub(/.*\/task\//, "")
  task_id
end

#delete_vapp_network(vAppId, network) ⇒ Object

Remove an existing network

Raises:



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/vcloud-rest/vcloud/vapp_networking.rb', line 74

def delete_vapp_network(vAppId, network)
  params = {
    'method' => :get,
    'command' => "/vApp/vapp-#{vAppId}/networkConfigSection"
  }

  netconfig_response, headers = send_request(params)

  picked_network = netconfig_response.css("NetworkConfig").select do |net|
    net.attribute('networkName').text == network[:name]
  end.first

  raise WrongItemIDError, "Network #{network[:name]} not found on this vApp." unless picked_network

  picked_network.remove

  params = {
    'method' => :put,
    'command' => "/vApp/vapp-#{vAppId}/networkConfigSection"
  }

  put_response, headers = send_request(params, netconfig_response.to_xml, "application/vnd.vmware.vcloud.networkConfigSection+xml")

  task_id = headers[:location].gsub(/.*\/task\//, "")
  task_id
end

#delete_vm_network(vmId, network) ⇒ Object

Remove an existing network

Raises:



239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/vcloud-rest/vcloud/vm.rb', line 239

def delete_vm_network(vmId, network)
  params = {
    'method' => :get,
    'command' => "/vApp/vm-#{vmId}/networkConnectionSection"
  }

  netconfig_response, headers = send_request(params)

  picked_network = netconfig_response.css("NetworkConnection").select do |net|
    net.attribute('network').text == network[:name]
  end.first

  raise WrongItemIDError, "Network #{network[:name]} not found on this VM." unless picked_network

  picked_network.remove

  params = {
    'method' => :put,
    'command' => "/vApp/vm-#{vmId}/networkConnectionSection"
  }

  put_response, headers = send_request(params, netconfig_response.to_xml, "application/vnd.vmware.vcloud.networkConnectionSection+xml")

  task_id = headers[:location].gsub(/.*\/task\//, "")
  task_id
end

#detach_disk_from_vm(disk_id, vm_id) ⇒ Object



74
75
76
# File 'lib/vcloud-rest/vcloud/disk.rb', line 74

def detach_disk_from_vm(disk_id, vm_id)
  disk_attach_action(disk_id, vm_id, 'detach')
end

#discard_suspend_state_vapp(vAppId) ⇒ Object

Discard suspended state of a given vapp



172
173
174
# File 'lib/vcloud-rest/vcloud/vapp.rb', line 172

def discard_suspend_state_vapp(vAppId)
  discard_suspended_state_action(vAppId, :vapp)
end

#discard_suspend_state_vm(vmId) ⇒ Object

Discard suspended state of a given vm



432
433
434
# File 'lib/vcloud-rest/vcloud/vm.rb', line 432

def discard_suspend_state_vm(vmId)
  discard_suspended_state_action(vmId, :vm)
end

#discard_vapp_snapshot(vmId) ⇒ Object

Discard all existing snapshots



407
408
409
# File 'lib/vcloud-rest/vcloud/vapp.rb', line 407

def discard_vapp_snapshot(vmId)
  discard_snapshot_action(vmId, :vapp)
end

#discard_vm_snapshot(vmId) ⇒ Object

Discard all existing snapshots



471
472
473
# File 'lib/vcloud-rest/vcloud/vm.rb', line 471

def discard_vm_snapshot(vmId)
  discard_snapshot_action(vmId, :vm)
end

#edit_vm_network(vmId, network, config = {}) ⇒ Object

Edit VM Network Config

Retrieve the existing network config section and edit it to ensure settings are not lost

Raises:



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/vcloud-rest/vcloud/vm.rb', line 124

def edit_vm_network(vmId, network, config={})
  params = {
    'method' => :get,
    'command' => "/vApp/vm-#{vmId}/networkConnectionSection"
  }

  netconfig_response, headers = send_request(params)

  if config[:primary_index]
    node = netconfig_response.css('PrimaryNetworkConnectionIndex').first
    node.content = config[:primary_index]
  end

  picked_network = netconfig_response.css("NetworkConnection").select do |net|
    net.attribute('network').text == network[:name]
  end.first

  raise WrongItemIDError, "Network named #{network[:name]} not found." unless picked_network

  if config[:ip_allocation_mode]
    node = picked_network.css('IpAddressAllocationMode').first
    node.content = config[:ip_allocation_mode]
  end

  if config[:network_index]
    node = picked_network.css('NetworkConnectionIndex').first
    node.content = config[:network_index]
  end

  if config[:is_connected]
    node = picked_network.css('IsConnected').first
    node.content = config[:is_connected]
  end

  if config[:ip]
    node = picked_network.css('IpAddress').first
    node.content = config[:ip]
  end

  params = {
    'method' => :put,
    'command' => "/vApp/vm-#{vmId}/networkConnectionSection"
  }

  response, headers = send_request(params, netconfig_response.to_xml, "application/vnd.vmware.vcloud.networkConnectionSection+xml")

  task_id = headers[:location].gsub(/.*\/task\//, "")
  task_id
end

#force_customization_vapp(vappId) ⇒ Object

Force a guest customization



479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
# File 'lib/vcloud-rest/vcloud/vapp.rb', line 479

def force_customization_vapp(vappId)
  builder = Nokogiri::XML::Builder.new do |xml|
    xml.DeployVAppParams(
      "xmlns" => "http://www.vmware.com/vcloud/v1.5",
      "forceCustomization" => "true")
  end

  params = {
    "method" => :post,
    "command" => "/vApp/vapp-#{vappId}/action/deploy"
  }

  response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.deployVAppParams+xml")
  task_id = headers[:location].gsub(/.*\/task\//, "")
  task_id
end

#force_customization_vm(vmId) ⇒ Object

Force a guest customization



295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/vcloud-rest/vcloud/vm.rb', line 295

def force_customization_vm(vmId)
  builder = Nokogiri::XML::Builder.new do |xml|
    xml.DeployVAppParams(
      "xmlns" => "http://www.vmware.com/vcloud/v1.5",
      "forceCustomization" => "true")
  end

  params = {
    "method" => :post,
    "command" => "/vApp/vm-#{vmId}/action/deploy"
  }

  response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.deployVAppParams+xml")
  task_id = headers[:location].gsub(/.*\/task\//, "")
  task_id
end

#get_catalog(catalogId) ⇒ Object

Fetch details about a given catalog



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/vcloud-rest/vcloud/catalog.rb', line 5

def get_catalog(catalogId)
  params = {
    'method' => :get,
    'command' => "/catalog/#{catalogId}"
  }

  response, headers = send_request(params)
  description = response.css("Description").first
  description = description.text unless description.nil?

  items = {}
  response.css("CatalogItem[type='application/vnd.vmware.vcloud.catalogItem+xml']").each do |item|
    items[item['name']] = item['href'].gsub(/.*\/catalogItem\//, "")
  end
  { :id => catalogId, :description => description, :items => items }
end

#get_catalog_by_name(organization, catalogName) ⇒ Object

Friendly helper method to fetch an catalog by name

  • organization hash (from get_organization/get_organization_by_name)

  • catalog name



42
43
44
45
46
47
48
49
50
51
52
# File 'lib/vcloud-rest/vcloud/catalog.rb', line 42

def get_catalog_by_name(organization, catalogName)
  result = nil

  organization[:catalogs].each do |catalog|
    if catalog[0].downcase == catalogName.downcase
      result = get_catalog(catalog[1])
    end
  end

  result
end

#get_catalog_id_by_name(organization, catalogName) ⇒ Object

Friendly helper method to fetch an catalog id by name

  • organization hash (from get_organization/get_organization_by_name)

  • catalog name



26
27
28
29
30
31
32
33
34
35
36
# File 'lib/vcloud-rest/vcloud/catalog.rb', line 26

def get_catalog_id_by_name(organization, catalogName)
  result = nil

  organization[:catalogs].each do |catalog|
    if catalog[0].downcase == catalogName.downcase
      result = catalog[1]
    end
  end

  result
end

#get_catalog_item(catalogItemId) ⇒ Object

Fetch details about a given catalog item:

  • description

  • vApp templates



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/vcloud-rest/vcloud/catalog.rb', line 58

def get_catalog_item(catalogItemId)
  params = {
    'method' => :get,
    'command' => "/catalogItem/#{catalogItemId}"
  }

  response, headers = send_request(params)
  description = response.css("Description").first
  description = description.text unless description.nil?

  items = []
  # manage two different types of catalog items: vAppTemplate and media
  if response.css("Entity[type='application/vnd.vmware.vcloud.vAppTemplate+xml']").size > 0
    response.css("Entity[type='application/vnd.vmware.vcloud.vAppTemplate+xml']").each do |item|
      itemId = item['href'].gsub(/.*\/vAppTemplate\/vappTemplate\-/, "")

      # Fetch the catalogItemId information
      params = {
        'method' => :get,
        'command' => "/vAppTemplate/vappTemplate-#{itemId}"
      }
      response, headers = send_request(params)

      # VMs Hash for all the vApp VM entities
      vms_hash = {}
      response.css("/VAppTemplate/Children/Vm").each do |vmElem|
        vmName = vmElem["name"]
        vmId = vmElem["href"].gsub(/.*\/vAppTemplate\/vm\-/, "")

        # Add the VM name/id to the VMs Hash
        vms_hash[vmName] = { :id => vmId }
      end

      items << { :id => itemId,
                 :name => item['name'],
                 :vms_hash => vms_hash }
    end

    { :id => catalogItemId, :description => description, :items => items, :type => 'vAppTemplate' }
  elsif response.css("Entity[type='application/vnd.vmware.vcloud.media+xml']").size > 0
    name = response.css("Entity[type='application/vnd.vmware.vcloud.media+xml']").first['name']
    { :id => catalogItemId, :description => description, :name => name, :type => 'media' }
  else
    @logger.warn 'WARNING: either this catalog item is empty or contains something not managed by vcloud-rest'
    { :id => catalogItemId, :description => description, :type => 'unknown' }
  end
end

#get_catalog_item_by_name(catalogId, catalogItemName) ⇒ Object

friendly helper method to fetch an catalogItem by name

  • catalogId (use get_catalog_name(org, name))

  • catalagItemName



110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/vcloud-rest/vcloud/catalog.rb', line 110

def get_catalog_item_by_name(catalogId, catalogItemName)
  result = nil
  catalogElems = get_catalog(catalogId)

  catalogElems[:items].each do |k, v|
    if (k.downcase == catalogItemName.downcase)
      result = get_catalog_item(v)
    end
  end

  result
end

#get_disk(disk_id) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/vcloud-rest/vcloud/disk.rb', line 40

def get_disk(disk_id)
  params = {
    'method' => :get,
    'command' => "/disk/#{disk_id}"
  }

  @logger.debug "Fetching independent disk #{disk_id}"
  response, headers = send_request(params)

  name = response.css("Disk").attribute("name").text
  size = response.css("Disk").attribute("size").text
  description = response.css("Description").first
  description = description.text unless description.nil?
  storage_profile = response.css("StorageProfile").first[:name]
  owner = response.css("User").first[:name]
  { :id => disk_id, :name => name, :size => size, :description => description, :storage_profile => storage_profile, :owner => owner }
end

#get_disk_by_name(organization, vdcName, diskName) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
# File 'lib/vcloud-rest/vcloud/disk.rb', line 58

def get_disk_by_name(organization, vdcName, diskName)
  result = nil

  get_vdc_by_name(organization, vdcName)[:disks].each do |disk|
    if disk[0].downcase == diskName.downcase
      result = get_disk(disk[1])
    end
  end

  result
end

#get_extensibilityObject



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/vcloud-rest/vcloud/extensibility.rb', line 3

def get_extensibility
  params = {
    'method' => :get,
    'command' => "/extensibility"
  }

  response, headers = send_request(params)

  down_service = response.css("Link[@rel='down:service']").first['href']
  down_apidefinitions = response.css("Link[@rel='down:apidefinitions']").first['href']
  down_files = response.css("Link[@rel='down:files']").first['href']

  {
    :down_service => down_service,
    :down_apidefinitions => down_apidefinitions,
    :down_files => down_files,
  }
end

#get_network(networkId) ⇒ Object

Fetch details about a given Org VDC network



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/vcloud-rest/vcloud/network.rb', line 5

def get_network(networkId)
  response = get_base_network(networkId)

  name = response.css('OrgVdcNetwork').attribute('name').text

  description = response.css("Description").first
  description = description.text unless description.nil?

  gateway = response.css('Gateway')
  gateway = gateway.text unless gateway.nil?

  netmask = response.css('Netmask')
  netmask = netmask.text unless netmask.nil?

  fence_mode = response.css('FenceMode')
  fence_mode = fence_mode.text unless fence_mode.nil?

  start_address = response.css('StartAddress')
  start_address = start_address.text unless start_address.nil?

  end_address = response.css('EndAddress')
  end_address = end_address.text unless end_address.nil?


  { :id => networkId, :name => name, :description => description,
    :gateway => gateway, :netmask => netmask, :fence_mode => fence_mode,
    :start_address => start_address, :end_address => end_address }
end

#get_network_by_name(organization, networkName) ⇒ Object

Friendly helper method to fetch an network by name

  • organization hash (from get_organization/get_organization_by_name)

  • network name



54
55
56
57
58
59
60
61
62
63
64
# File 'lib/vcloud-rest/vcloud/network.rb', line 54

def get_network_by_name(organization, networkName)
  result = nil

  organization[:networks].each do |network|
    if network[0].downcase == networkName.downcase
      result = get_network(network[1])
    end
  end

  result
end

#get_network_id_by_name(organization, networkName) ⇒ Object

Friendly helper method to fetch an network id by name

  • organization hash (from get_organization/get_organization_by_name)

  • network name



38
39
40
41
42
43
44
45
46
47
48
# File 'lib/vcloud-rest/vcloud/network.rb', line 38

def get_network_id_by_name(organization, networkName)
  result = nil

  organization[:networks].each do |network|
    if network[0].downcase == networkName.downcase
      result = network[1]
    end
  end

  result
end

#get_organization(orgId) ⇒ Object

Fetch details about an organization:

  • catalogs

  • vdcs

  • networks

  • task lists



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/vcloud-rest/vcloud/org.rb', line 62

def get_organization(orgId)
  params = {
    'method' => :get,
    'command' => "/org/#{orgId}"
  }

  response, headers = send_request(params)
  catalogs = {}
  response.css("Link[type='application/vnd.vmware.vcloud.catalog+xml']").each do |item|
    catalogs[item['name']] = item['href'].gsub(/.*\/catalog\//, "")
  end

  vdcs = {}
  response.css("Link[type='application/vnd.vmware.vcloud.vdc+xml']").each do |item|
    vdcs[item['name']] = item['href'].gsub(/.*\/vdc\//, "")
  end

  networks = {}
  response.css("Link[type='application/vnd.vmware.vcloud.orgNetwork+xml']").each do |item|
    networks[item['name']] = item['href'].gsub(/.*\/network\//, "")
  end

  tasklists = {}
  response.css("Link[type='application/vnd.vmware.vcloud.tasksList+xml']").each do |item|
    tasklists[item['name']] = item['href'].gsub(/.*\/tasksList\//, "")
  end

  { :catalogs => catalogs, :vdcs => vdcs, :networks => networks, :tasklists => tasklists }
end

#get_organization_by_name(name) ⇒ Object

friendly helper method to fetch an Organization by name

  • name (this isn’t case sensitive)



42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/vcloud-rest/vcloud/org.rb', line 42

def get_organization_by_name(name)
  result = nil

  # Fetch all organizations
  organizations = get_organizations()

  organizations.each do |organization|
    if organization[0].downcase == name.downcase
      result = get_organization(organization[1])
    end
  end
  result
end

#get_organization_id_by_name(name) ⇒ Object

friendly helper method to fetch an Organization Id by name

  • name (this isn’t case sensitive)



24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/vcloud-rest/vcloud/org.rb', line 24

def get_organization_id_by_name(name)
  result = nil

  # Fetch all organizations
  organizations = get_organizations()

  organizations.each do |organization|
    if organization[0].downcase == name.downcase
      result = organization[1]
    end
  end
  result
end

#get_organizationsObject

Fetch existing organizations and their IDs



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/vcloud-rest/vcloud/org.rb', line 5

def get_organizations
  params = {
    'method' => :get,
    'command' => '/org'
  }

  response, headers = send_request(params)
  orgs = response.css('OrgList Org')

  results = {}
  orgs.each do |org|
    results[org['name']] = org['href'].gsub(/.*\/org\//, "")
  end
  results
end

#get_task(taskid) ⇒ Object

Fetch information for a given task



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/vcloud-rest/connection.rb', line 100

def get_task(taskid)
  params = {
    'method' => :get,
    'command' => "/task/#{taskid}"
  }

  response, headers = send_request(params)

  task = response.css('Task').first
  status = task['status']
  start_time = task['startTime']
  end_time = task['endTime']

  { :status => status, :start_time => start_time, :end_time => end_time, :response => response }
end

#get_tasks_list(id) ⇒ Object

Fetch tasks from a given task list

Note: id can be retrieved using get_organization



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/vcloud-rest/vcloud/org.rb', line 96

def get_tasks_list(id)
  params = {
    'method' => :get,
    'command' => "/tasksList/#{id}"
  }

  response, headers = send_request(params)

  tasks = []

  response.css('Task').each do |task|
    id = task['href'].gsub(/.*\/task\//, "")
    operation = task['operationName']
    status = task['status']
    error = nil
    error = task.css('Error').first['message'] if task['status'] == 'error'
    start_time = task['startTime']
    end_time = task['endTime']
    user_canceled = task['cancelRequested'] == 'true'

    tasks << {
      :id => id,
      :operation => operation,
      :status => status,
      :error => error,
      :start_time => start_time,
      :end_time => end_time,
      :user_canceled => user_canceled
     }
  end
  tasks
end

#get_vapp(vAppId) ⇒ Object

Fetch details about a given vapp:

  • name

  • description

  • status

  • IP

  • Children VMs: – IP addresses – status – ID



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/vcloud-rest/vcloud/vapp.rb', line 15

def get_vapp(vAppId)
  params = {
    'method' => :get,
    'command' => "/vApp/vapp-#{vAppId}"
  }

  response, headers = send_request(params)

  vapp_node = response.css('VApp').first
  if vapp_node
    name = vapp_node['name']
    status = convert_vapp_status(vapp_node['status'])
  end

  description = response.css("Description").first
  description = description.text unless description.nil?

  ip = response.css('IpAddress').first
  ip = ip.text unless ip.nil?

  networks = response.css('NetworkConfig').reject{|n| n.attribute('networkName').text == 'none'}.
    collect do |network|
      net_id = network.css('Link[rel="repair"]')
      net_id = net_id.attribute('href').text.gsub(/.*\/network\/(.*)\/action.*/, '\1') unless net_id.empty?

      net_name = network.attribute('networkName').text

      gateway = network.css('Gateway')
      gateway = gateway.text unless gateway.nil?

      netmask = network.css('Netmask')
      netmask = netmask.text unless netmask.nil?

      fence_mode = network.css('FenceMode')
      fence_mode = fence_mode.text unless fence_mode.nil?

      parent_network = network.css('ParentNetwork')
      parent_network = parent_network.attribute('name').text unless parent_network.empty?
      parent_network = nil if parent_network.empty?

      retain_network = network.css('RetainNetInfoAcrossDeployments')
      retain_network = retain_network.text unless retain_network.nil?

      # TODO: handle multiple scopes?
      ipscope =  {
          :gateway => gateway,
          :netmask => netmask,
          :fence_mode => fence_mode,
          :parent_network => parent_network,
          :retain_network => retain_network
        }

      {
        :id => net_id,
        :name => net_name,
        :scope => ipscope
      }
    end

  vapp_snapshot = nil
  response.css('SnapshotSection').each do |snapshot_section|
    if snapshot_section['href'] =~ /.*\/vApp\/vapp\-/
      snapshot = snapshot_section.css("Snapshot").first
      if snapshot
        vapp_snapshot = {
          :size => snapshot['size'],
          :creation_date => snapshot['created']
        }
      end
      break
    end
  end

  vms = response.css('Children Vm')
  vms_hash = {}

  vms.each do |vm|
    vapp_local_id = vm.css('VAppScopedLocalId')
    addresses = vm.css('rasd|Connection').collect do |n|
      address = n.attribute('vcloud:ipAddress')
      address = n.attribute('ipAddress') unless address
      address = address.text if address
    end

    vms_hash[vm['name']] = {
      :addresses => addresses,
      :status => convert_vapp_status(vm['status']),
      :id => vm['href'].gsub(/.*\/vApp\/vm\-/, ""),
      :vapp_scoped_local_id => vapp_local_id.text
    }
  end

  { :id => vAppId, :name => name, :description => description,
    :status => status, :ip => ip, :networks => networks,
    :vapp_snapshot => vapp_snapshot, :vms_hash => vms_hash }
end

#get_vapp_by_name(organization, vdcName, vAppName) ⇒ Object

Friendly helper method to fetch a vApp by name

  • Organization object

  • Organization VDC Name

  • vApp name



117
118
119
120
121
122
123
124
125
126
127
# File 'lib/vcloud-rest/vcloud/vapp.rb', line 117

def get_vapp_by_name(organization, vdcName, vAppName)
  result = nil

  get_vdc_by_name(organization, vdcName)[:vapps].each do |vapp|
    if vapp[0].downcase == vAppName.downcase
      result = get_vapp(vapp[1])
    end
  end

  result
end

#get_vapp_edge_public_ip(vAppId) ⇒ Object

get vApp edge public IP from the vApp ID Only works when:

  • vApp needs to be poweredOn

  • FenceMode is set to “natRouted”

  • NatType“ is set to ”portForwarding

This will be required to know how to connect to VMs behind the Edge device.

Raises:



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/vcloud-rest/vcloud/vapp_networking.rb', line 197

def get_vapp_edge_public_ip(vAppId)
  # Check the network configuration section
  params = {
    'method' => :get,
    'command' => "/vApp/vapp-#{vAppId}/networkConfigSection"
  }

  response, headers = send_request(params)

  # FIXME: this will return nil if the vApp uses multiple vApp Networks
  # with Edge devices in natRouted/portForwarding mode.
  config = response.css('NetworkConfigSection/NetworkConfig/Configuration')

  fenceMode = config.css('/FenceMode').text
  natType = config.css('/Features/NatService/NatType').text

  raise InvalidStateError, "Invalid request because FenceMode must be set to natRouted." unless fenceMode == "natRouted"
  raise InvalidStateError, "Invalid request because NatType must be set to portForwarding." unless natType == "portForwarding"

  # Check the routerInfo configuration where the global external IP is defined
  edgeIp = config.css('/RouterInfo/ExternalIp')
  edgeIp = edgeIp.text unless edgeIp.nil?
end

#get_vapp_port_forwarding_rules(vAppId) ⇒ Object

Get vApp port forwarding rules

  • vappid: id of the vApp

Raises:



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/vcloud-rest/vcloud/vapp_networking.rb', line 155

def get_vapp_port_forwarding_rules(vAppId)
  params = {
    'method' => :get,
    'command' => "/vApp/vapp-#{vAppId}/networkConfigSection"
  }

  response, headers = send_request(params)

  # FIXME: this will return nil if the vApp uses multiple vApp Networks
  # with Edge devices in natRouted/portForwarding mode.
  config = response.css('NetworkConfigSection/NetworkConfig/Configuration')
  fenceMode = config.css('/FenceMode').text
  natType = config.css('/Features/NatService/NatType').text

  raise InvalidStateError, "Invalid request because FenceMode must be set to natRouted." unless fenceMode == "natRouted"
  raise InvalidStateError, "Invalid request because NatType must be set to portForwarding." unless natType == "portForwarding"

  nat_rules = {}
  config.css('/Features/NatService/NatRule').each do |rule|
    # portforwarding rules information
    ruleId = rule.css('Id').text
    vmRule = rule.css('VmRule')

    nat_rules[rule.css('Id').text] = {
      :ExternalIpAddress  => vmRule.css('ExternalIpAddress').text,
      :ExternalPort       => vmRule.css('ExternalPort').text,
      :VAppScopedVmId     => vmRule.css('VAppScopedVmId').text,
      :VmNicId            => vmRule.css('VmNicId').text,
      :InternalPort       => vmRule.css('InternalPort').text,
      :Protocol           => vmRule.css('Protocol').text
    }
  end
  nat_rules
end

#get_vapp_template(vAppId) ⇒ Object

Fetch details about a given vapp template:

  • name

  • description

  • Children VMs: – ID



445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
# File 'lib/vcloud-rest/vcloud/vapp.rb', line 445

def get_vapp_template(vAppId)
  params = {
    'method' => :get,
    'command' => "/vAppTemplate/vappTemplate-#{vAppId}"
  }

  response, headers = send_request(params)

  vapp_node = response.css('VAppTemplate').first
  if vapp_node
    name = vapp_node['name']
    status = convert_vapp_status(vapp_node['status'])
  end

  description = response.css("Description").first
  description = description.text unless description.nil?

  ip = response.css('IpAddress').first
  ip = ip.text unless ip.nil?

  vms = response.css('Children Vm')
  vms_hash = {}

  vms.each do |vm|
    vms_hash[vm['name']] = {
      :id => vm['href'].gsub(/.*\/vAppTemplate\/vm\-/, "")
    }
  end

  { :name => name, :description => description, :vms_hash => vms_hash }
end

#get_vdc(vdcId) ⇒ Object

Fetch details about a given vdc:

  • description

  • vapps

  • templates

  • disks

  • networks



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/vcloud-rest/vcloud/vdc.rb', line 10

def get_vdc(vdcId)
  params = {
    'method' => :get,
    'command' => "/vdc/#{vdcId}"
  }

  response, headers = send_request(params)

  name = response.css("Vdc").attribute("name")
  name = name.text unless name.nil?

  description = response.css("Description").first
  description = description.text unless description.nil?

  vapps = {}
  response.css("ResourceEntity[type='application/vnd.vmware.vcloud.vApp+xml']").each do |item|
    vapps[item['name']] = item['href'].gsub(/.*\/vApp\/vapp\-/, "")
  end

  templates = {}
  response.css("ResourceEntity[type='application/vnd.vmware.vcloud.vAppTemplate+xml']").each do |item|
    templates[item['name']] = item['href'].gsub(/.*\/vAppTemplate\/vappTemplate\-/, "")
  end

  disks = {}
  response.css("ResourceEntity[type='application/vnd.vmware.vcloud.disk+xml']").each do |item|
    disks[item['name']] = item['href'].gsub(/.*\/disk\//, "")
  end

  networks = {}
  response.css("Network[type='application/vnd.vmware.vcloud.network+xml']").each do |item|
    networks[item['name']] = item['href'].gsub(/.*\/network\//, "")
  end

  { :id => vdcId, :name => name, :description => description,
    :vapps => vapps, :templates => templates, :disks => disks,
    :networks => networks }
end

#get_vdc_by_name(organization, vdcName) ⇒ Object

Friendly helper method to fetch a Organization VDC by name

  • Organization object

  • Organization VDC Name



69
70
71
72
73
74
75
76
77
78
79
# File 'lib/vcloud-rest/vcloud/vdc.rb', line 69

def get_vdc_by_name(organization, vdcName)
  result = nil

  organization[:vdcs].each do |vdc|
    if vdc[0].downcase == vdcName.downcase
      result = get_vdc(vdc[1])
    end
  end

  result
end

#get_vdc_id_by_name(organization, vdcName) ⇒ Object

Friendly helper method to fetch a Organization VDC Id by name

  • Organization object

  • Organization VDC Name



53
54
55
56
57
58
59
60
61
62
63
# File 'lib/vcloud-rest/vcloud/vdc.rb', line 53

def get_vdc_id_by_name(organization, vdcName)
  result = nil

  organization[:vdcs].each do |vdc|
    if vdc[0].downcase == vdcName.downcase
      result = vdc[1]
    end
  end

  result
end

#get_vm(vmId) ⇒ Object

Fetch details about a given VM



330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
# File 'lib/vcloud-rest/vcloud/vm.rb', line 330

def get_vm(vmId)
  params = {
    'method' => :get,
    'command' => "/vApp/vm-#{vmId}"
  }

  response, headers = send_request(params)

  vm_name = response.css('Vm').attribute("name")
  vm_name = vm_name.text unless vm_name.nil?

  status = convert_vapp_status(response.css('Vm').attribute("status").text)

  os_desc = response.css('ovf|OperatingSystemSection ovf|Description').first.text

  networks = {}
  response.css('NetworkConnection').each do |network|
    ip = network.css('IpAddress').first
    ip = ip.text if ip

    external_ip = network.css('ExternalIpAddress').first
    external_ip = external_ip.text if external_ip

    # Append NetworkConnectionIndex to network name to generate a unique hash key,
    # otherwise different interfaces on the same network would use the same hash key
    key = "#{network['network']}_#{network.css('NetworkConnectionIndex').first.text}"

    networks[key] = {
      :index => network.css('NetworkConnectionIndex').first.text,
      :ip => ip,
      :external_ip => external_ip,
      :is_connected => network.css('IsConnected').first.text,
      :mac_address => network.css('MACAddress').first.text,
      :ip_allocation_mode => network.css('IpAddressAllocationMode').first.text
    }
  end

  admin_password = response.css('GuestCustomizationSection AdminPassword').first
  admin_password = admin_password.text if admin_password

  guest_customizations = {
    :enabled => response.css('GuestCustomizationSection Enabled').first.text,
    :admin_passwd_enabled => response.css('GuestCustomizationSection AdminPasswordEnabled').first.text,
    :admin_passwd_auto => response.css('GuestCustomizationSection AdminPasswordAuto').first.text,
    :admin_passwd => admin_password,
    :reset_passwd_required => response.css('GuestCustomizationSection ResetPasswordRequired').first.text,
    :computer_name => response.css('GuestCustomizationSection ComputerName').first.text
  }

  { :id => vmId,
    :vm_name => vm_name, :os_desc => os_desc, :networks => networks,
    :guest_customizations => guest_customizations, :status => status
  }
end

#get_vm_by_name(organization, vdcName, vAppName, vmName) ⇒ Object

Friendly helper method to fetch a vApp by name

  • Organization object

  • Organization VDC Name

  • vApp Name

  • VM Name



391
392
393
394
395
396
397
398
399
400
401
# File 'lib/vcloud-rest/vcloud/vm.rb', line 391

def get_vm_by_name(organization, vdcName, vAppName, vmName)
  result = nil

  get_vapp_by_name(organization, vdcName, vAppName)[:vms_hash].each do |key, values|
    if key.downcase == vmName.downcase
      result = get_vm(values[:id])
    end
  end

  result
end

#get_vm_disk_info(vmid) ⇒ Object

Retrieve information about Disks



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/vcloud-rest/vcloud/vm.rb', line 33

def get_vm_disk_info(vmid)
  response, headers = __get_disk_info(vmid)

  disks = []
  response.css("Item").each do |entry|
    # Pick only entries with node "HostResource"
    resource = entry.css("rasd|HostResource").first
    next unless resource

    name = entry.css("rasd|ElementName").first
    name = name.text unless name.nil?
    capacity = resource.attribute("capacity").text

    disks << {
      :name => name,
      :capacity => "#{capacity} MB"
    }
  end
  disks
end

#get_vm_info(vmid) ⇒ Object

Retrieve information (i.e., memory and CPUs)



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/vcloud-rest/vcloud/vm.rb', line 5

def get_vm_info(vmid)
  params = {
    'method' => :get,
    'command' => "/vApp/vm-#{vmid}/virtualHardwareSection"
  }

  response, headers = send_request(params)

  result = {}
  response.css("ovf|Item[vcloud|href]").each do |item|
    item_name = item.attribute('href').text.gsub(/.*\/vApp\/vm\-(\w+(-?))+\/virtualHardwareSection\//, "")
    name = item.css("rasd|ElementName")
    name = name.text unless name.nil?

    description = item.css("rasd|Description")
    description = description.text unless description.nil?

    result[item_name] = {
      :name => name,
      :description => description
    }
  end

  result
end

#loginObject

Authenticate against the specified server



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/vcloud-rest/connection.rb', line 66

def 
  params = {
    'method' => :post,
    'command' => '/sessions'
  }

  response, headers = send_request(params)

  if !headers.has_key?(:x_vcloud_authorization)
    raise "Unable to authenticate: missing x_vcloud_authorization header"
  end

  extensibility_link = response.css("Link[rel='down:extensibility']")
  @extensibility = extensibility_link.first['href'] unless extensibility_link.empty?

  @auth_key = headers[:x_vcloud_authorization]
end

#logoutObject

Destroy the current session



86
87
88
89
90
91
92
93
94
95
96
# File 'lib/vcloud-rest/connection.rb', line 86

def logout
  params = {
    'method' => :delete,
    'command' => '/session'
  }

  response, headers = send_request(params)
ensure
  # reset auth key to nil
  @auth_key = nil
end

#poweroff_vapp(vAppId) ⇒ Object

Shutdown a given vapp



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/vcloud-rest/vcloud/vapp.rb', line 145

def poweroff_vapp(vAppId)
  builder = Nokogiri::XML::Builder.new do |xml|
  xml.UndeployVAppParams(
    "xmlns" => "http://www.vmware.com/vcloud/v1.5") {
    xml.UndeployPowerAction 'powerOff'
  }
  end

  params = {
    'method' => :post,
    'command' => "/vApp/vapp-#{vAppId}/action/undeploy"
  }

  response, headers = send_request(params, builder.to_xml,
                  "application/vnd.vmware.vcloud.undeployVAppParams+xml")
  task_id = headers[:location].gsub(/.*\/task\//, "")
  task_id
end

#poweroff_vm(vmId) ⇒ Object

Shutdown a given vm



405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
# File 'lib/vcloud-rest/vcloud/vm.rb', line 405

def poweroff_vm(vmId)
  builder = Nokogiri::XML::Builder.new do |xml|
  xml.UndeployVAppParams(
    "xmlns" => "http://www.vmware.com/vcloud/v1.5") {
    xml.UndeployPowerAction 'powerOff'
  }
  end

  params = {
    'method' => :post,
    'command' => "/vApp/vm-#{vmId}/action/undeploy"
  }

  response, headers = send_request(params, builder.to_xml,
                  "application/vnd.vmware.vcloud.undeployVAppParams+xml")
  task_id = headers[:location].gsub(/.*\/task\//, "")
  task_id
end

#poweron_vapp(vAppId) ⇒ Object

Boot a given vapp



195
196
197
# File 'lib/vcloud-rest/vcloud/vapp.rb', line 195

def poweron_vapp(vAppId)
  power_action(vAppId, 'powerOn')
end

#poweron_vm(vmId) ⇒ Object

Boot a given vm



453
454
455
# File 'lib/vcloud-rest/vcloud/vm.rb', line 453

def poweron_vm(vmId)
  power_action(vmId, 'powerOn', :vm)
end

#reboot_vapp(vAppId) ⇒ Object

reboot a given vapp This will basically initial a guest OS reboot, and will only work if VMware-tools are installed on the underlying VMs. vShield Edge devices are not affected



181
182
183
# File 'lib/vcloud-rest/vcloud/vapp.rb', line 181

def reboot_vapp(vAppId)
  power_action(vAppId, 'reboot')
end

#reboot_vm(vmId) ⇒ Object

reboot a given vm This will basically initial a guest OS reboot, and will only work if VMware-tools are installed on the underlying VMs. vShield Edge devices are not affected



441
442
443
# File 'lib/vcloud-rest/vcloud/vm.rb', line 441

def reboot_vm(vmId)
  power_action(vmId, 'reboot', :vm)
end

#rename_vm(vmId, name) ⇒ Object



312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'lib/vcloud-rest/vcloud/vm.rb', line 312

def rename_vm(vmId, name)
  params = {
    'method' => :get,
    'command' => "/vApp/vm-#{vmId}"
  }

  response, headers = send_request(params)
  response.css('Vm').attribute("name").content = name

  params['method'] = :put
  response, headers = send_request(params, response.to_xml,
                                "application/vnd.vmware.vcloud.vm+xml")
  task_id = headers[:location].gsub(/.*\/task\//, "")
  task_id
end

#reset_vapp(vAppId) ⇒ Object

reset a given vapp This will basically reset the VMs within the vApp vShield Edge devices are not affected.



189
190
191
# File 'lib/vcloud-rest/vcloud/vapp.rb', line 189

def reset_vapp(vAppId)
  power_action(vAppId, 'reset')
end

#reset_vm(vmId) ⇒ Object

reset a given vm



447
448
449
# File 'lib/vcloud-rest/vcloud/vm.rb', line 447

def reset_vm(vmId)
  power_action(vmId, 'reset', :vm)
end

#revert_snapshot(vmId) ⇒ Object

Revert to an existing snapshot DEPRECATED - use revert_vapp_snapshot instead.



388
389
390
391
# File 'lib/vcloud-rest/vcloud/vapp.rb', line 388

def revert_snapshot(vmId)
  @logger.warn 'DEPRECATION WARNING: use [create,revert]_vapp_snapshot instead.'
  revert_snapshot_action(vmId, :vapp)
end

#revert_vapp_snapshot(vmId) ⇒ Object

Revert to an existing snapshot



401
402
403
# File 'lib/vcloud-rest/vcloud/vapp.rb', line 401

def revert_vapp_snapshot(vmId)
  revert_snapshot_action(vmId, :vapp)
end

#revert_vm_snapshot(vmId) ⇒ Object

Revert to an existing snapshot



465
466
467
# File 'lib/vcloud-rest/vcloud/vm.rb', line 465

def revert_vm_snapshot(vmId)
  revert_snapshot_action(vmId, :vm)
end

#set_vapp_network_config(vappid, network, config = {}) ⇒ Object

Set vApp Network Config

Retrieve the existing network config section and edit it to ensure settings are not lost

Raises:



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/vcloud-rest/vcloud/vapp_networking.rb', line 8

def set_vapp_network_config(vappid, network, config={})
  params = {
    'method' => :get,
    'command' => "/vApp/vapp-#{vappid}/networkConfigSection"
  }

  netconfig_response, headers = send_request(params)

  picked_network = netconfig_response.css("NetworkConfig").select do |net|
    net.attribute('networkName').text == network[:name]
  end.first

  raise WrongItemIDError, "Network named #{network[:name]} not found." unless picked_network

  picked_network.css('FenceMode').first.content = config[:fence_mode] if config[:fence_mode]
  picked_network.css('IsInherited').first.content = "true"
  picked_network.css('RetainNetInfoAcrossDeployments').first.content = config[:retain_network] if config[:retain_network]

  if config[:parent_network]
    parent_network = picked_network.css('ParentNetwork').first
    new_parent = false

    unless parent_network
      new_parent = true
      ipscopes = picked_network.css('IpScopes').first
      parent_network = Nokogiri::XML::Node.new "ParentNetwork", ipscopes.parent
    end

    parent_network["name"] = "#{config[:parent_network][:name]}"
    parent_network["id"] = "#{config[:parent_network][:id]}"
    parent_network["href"] = "#{@api_url}/admin/network/#{config[:parent_network][:id]}"
    ipscopes.add_next_sibling(parent_network) if new_parent
  end

  data = netconfig_response.to_xml

  params = {
    'method' => :put,
    'command' => "/vApp/vapp-#{vappid}/networkConfigSection"
  }

  response, headers = send_request(params, data, "application/vnd.vmware.vcloud.networkConfigSection+xml")

  task_id = headers[:location].gsub(/.*\/task\//, "")
  task_id
end

#set_vapp_port_forwarding_rules(vappid, network_name, config = {}) ⇒ Object

Set vApp port forwarding rules

  • vappid: id of the vapp to be modified

  • network_name: name of the vapp network to be modified

  • config: hash with network configuration specifications, must contain an array inside :nat_rules with the nat rules to be applied.



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/vcloud-rest/vcloud/vapp_networking.rb', line 107

def set_vapp_port_forwarding_rules(vappid, network_name, config={})
  builder = Nokogiri::XML::Builder.new do |xml|
  xml.NetworkConfigSection(
    "xmlns" => "http://www.vmware.com/vcloud/v1.5",
    "xmlns:ovf" => "http://schemas.dmtf.org/ovf/envelope/1") {
    xml['ovf'].Info "Network configuration"
    xml.NetworkConfig("networkName" => network_name) {
      xml.Configuration {
        xml.ParentNetwork("href" => "#{@api_url}/network/#{config[:parent_network]}")
        xml.FenceMode(config[:fence_mode] || 'isolated')
        xml.Features {
          xml.NatService {
            xml.IsEnabled "true"
            xml.NatType "portForwarding"
            xml.Policy(config[:nat_policy_type] || "allowTraffic")
            config[:nat_rules].each do |nat_rule|
              xml.NatRule {
                xml.VmRule {
                  xml.ExternalPort nat_rule[:nat_external_port]
                  xml.VAppScopedVmId nat_rule[:vm_scoped_local_id]
                  xml.VmNicId(nat_rule[:nat_vmnic_id] || "0")
                  xml.InternalPort nat_rule[:nat_internal_port]
                  xml.Protocol(nat_rule[:nat_protocol] || "TCP")
                }
              }
            end
          }
        }
      }
    }
  }
  end

  params = {
    'method' => :put,
    'command' => "/vApp/vapp-#{vappid}/networkConfigSection"
  }

  response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.networkConfigSection+xml")

  task_id = headers[:location].gsub(/.*\/task\//, "")
  task_id
end

#set_vm_cpus(vmid, cpu_number) ⇒ Object

Set VM CPUs



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/vcloud-rest/vcloud/vm.rb', line 79

def set_vm_cpus(vmid, cpu_number)
  params = {
    'method' => :get,
    'command' => "/vApp/vm-#{vmid}/virtualHardwareSection/cpu"
  }

  get_response, headers = send_request(params)

  # Change attributes from the previous invocation
  get_response.css("rasd|ElementName").first.content = "#{cpu_number} virtual CPU(s)"
  get_response.css("rasd|VirtualQuantity").first.content = cpu_number

  params['method'] = :put
  put_response, headers = send_request(params, get_response.to_xml, "application/vnd.vmware.vcloud.rasdItem+xml")

  task_id = headers[:location].gsub(/.*\/task\//, "")
  task_id
end

#set_vm_disk_info(vmid, disk_info = {}) ⇒ Object

Set information about Disks

Disks can be added, deleted or modified



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/vcloud-rest/vcloud/vm.rb', line 58

def set_vm_disk_info(vmid, disk_info={})
  get_response, headers = __get_disk_info(vmid)

  if disk_info[:add]
    data = add_disk(get_response, disk_info)
  else
    data = edit_disk(get_response, disk_info)
  end

  params = {
    'method' => :put,
    'command' => "/vApp/vm-#{vmid}/virtualHardwareSection/disks"
  }
  put_response, headers = send_request(params, data, "application/vnd.vmware.vcloud.rasdItemsList+xml")

  task_id = headers[:location].gsub(/.*\/task\//, "")
  task_id
end

#set_vm_guest_customization(vmid, computer_name, config = {}) ⇒ Object

Set VM Guest Customization Config



268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/vcloud-rest/vcloud/vm.rb', line 268

def set_vm_guest_customization(vmid, computer_name, config={})
  builder = Nokogiri::XML::Builder.new do |xml|
  xml.GuestCustomizationSection(
    "xmlns" => "http://www.vmware.com/vcloud/v1.5",
    "xmlns:ovf" => "http://schemas.dmtf.org/ovf/envelope/1") {
      xml['ovf'].Info "VM Guest Customization configuration"
      xml.Enabled config[:enabled] if config[:enabled]
      xml.AdminPasswordEnabled config[:admin_passwd_enabled] if config[:admin_passwd_enabled]
      xml.AdminPassword config[:admin_passwd] if config[:admin_passwd]
      xml.CustomizationScript config[:customization_script] if config[:customization_script]
      xml.ComputerName computer_name
  }
  end

  params = {
    'method' => :put,
    'command' => "/vApp/vm-#{vmid}/guestCustomizationSection"
  }

  response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.guestCustomizationSection+xml")

  task_id = headers[:location].gsub(/.*\/task\//, "")
  task_id
end

#set_vm_ram(vmid, memory_size) ⇒ Object

Set VM RAM



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/vcloud-rest/vcloud/vm.rb', line 100

def set_vm_ram(vmid, memory_size)
  params = {
    'method' => :get,
    'command' => "/vApp/vm-#{vmid}/virtualHardwareSection/memory"
  }

  get_response, headers = send_request(params)

  # Change attributes from the previous invocation
  get_response.css("rasd|ElementName").first.content = "#{memory_size} MB of memory"
  get_response.css("rasd|VirtualQuantity").first.content = memory_size

  params['method'] = :put
  put_response, headers = send_request(params, get_response.to_xml, "application/vnd.vmware.vcloud.rasdItem+xml")

  task_id = headers[:location].gsub(/.*\/task\//, "")
  task_id
end

#suspend_vapp(vAppId) ⇒ Object

Suspend a given vapp



166
167
168
# File 'lib/vcloud-rest/vcloud/vapp.rb', line 166

def suspend_vapp(vAppId)
  power_action(vAppId, 'suspend')
end

#suspend_vm(vmId) ⇒ Object

Suspend a given vm



426
427
428
# File 'lib/vcloud-rest/vcloud/vm.rb', line 426

def suspend_vm(vmId)
  power_action(vmId, 'suspend', :vm)
end

#upload_media(vdcId, mediaName, mediaDescription, mediaFile, type, catalogId, uploadOptions = {}) ⇒ Object

Raises:

  • (::IOError)


4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/vcloud-rest/vcloud/media.rb', line 4

def upload_media(vdcId, mediaName, mediaDescription, mediaFile, type, catalogId, uploadOptions={})
  raise ::IOError, "File #{mediaFile} is missing." unless File.exists?(mediaFile)

  fileName = File.basename(mediaFile)
  mediaName = File.basename(fileName, ".*") if mediaName.nil? || mediaName.empty?
  type = File.extname(fileName).delete(".") if type.nil? || type.empty?
  size = File.size(mediaFile)

  builder = Nokogiri::XML::Builder.new do |xml|
    xml.Media(
      "xmlns" => "http://www.vmware.com/vcloud/v1.5",
      "xmlns:ovf" => "http://schemas.dmtf.org/ovf/envelope/1",
      "size" => size,
      "imageType" => type,
      "name" => mediaName) {
        xml.Description mediaDescription
      }
  end

  params = {
    'method' => :post,
    'command' => "/vdc/#{vdcId}/media"
  }

  @logger.debug "Creating Media item"
  response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.media+xml")

  # Get the new media id from response
  mediaUrl = response.css("Media").first[:href]
  mediaId = mediaUrl.gsub(/.*\/media\//, "")
  @logger.debug "Media item created - #{mediaId}"

  # Get File upload:default link from response
  uploadHref = response.css("Files Link [rel='upload:default']").first[:href]
  fileUpload = uploadHref.gsub(/(.*)(\/transfer\/.*)/, "\\2")

  begin
    @logger.debug "Uploading #{mediaFile}"
    upload_file(fileUpload, mediaFile, "/media/#{mediaId}", uploadOptions)

    # Add item to the catalog catalogId
    builder = Nokogiri::XML::Builder.new do |xml|
      xml.CatalogItem(
        "xmlns" => "http://www.vmware.com/vcloud/v1.5",
        "type" => "application/vnd.vmware.vcloud.catalogItem+xml",
        "name" => mediaName) {
        xml.Description mediaDescription
        xml.Entity("href" => "#{@api_url}/media/#{mediaId}")
      }
    end

    params = {
      'method' => :post,
      'command' => "/catalog/#{catalogId}/catalogItems"
    }

    @logger.debug "Adding media item #{mediaName} to catalog."
    response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.catalogItem+xml")

    # TODO: the best thing would detect the real importing status.
    entity = response.css("Entity").first
    result = {}
    if entity
      result[:id] = entity['href'].gsub(/.*\/media\//, "")
      result[:name] = entity['name']
    end
    result
  rescue Exception => e
    @logger.error "Exception detected: #{e.message}."

    # Get Media Task
    params = {
      'method' => :get,
      'command' => "/media/#{mediaId}"
    }
    response, headers = send_request(params)

    # Cancel Task
    # Note that it might not exist (i.e., error for existing vdc entity)
    tasks = response.css("Tasks")
    unless tasks.empty?
      tasks.css("Task").each do |task|
        if task['status'] == 'error'
          @logger.error task.css('Error').first['message']
        else
          id = task['href'].gsub(/.*\/task\//, "")
          @logger.error "Aborting task #{id}..."
          cancel_task(id)
        end
      end
    end

    raise e
  end
end

#upload_ovf(vdcId, vappName, vappDescription, ovfFile, catalogId, uploadOptions = {}) ⇒ Object

Upload an OVF package

  • vdcId

  • vappName

  • vappDescription

  • ovfFile

  • catalogId

  • uploadOptions {}

Raises:

  • (::IOError)


11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/vcloud-rest/vcloud/ovf.rb', line 11

def upload_ovf(vdcId, vappName, vappDescription, ovfFile, catalogId, uploadOptions={})
  raise ::IOError, "OVF #{ovfFile} is missing." unless File.exists?(ovfFile)
  raise ::IOError, "Only .ovf files are supported" unless File.extname(ovfFile) =~ /\.ovf/ 

  # if send_manifest is not set, setting it true
  if uploadOptions[:send_manifest].nil? || uploadOptions[:send_manifest]
    uploadManifest = "true"
  else
    uploadManifest = "false"
  end

  builder = Nokogiri::XML::Builder.new do |xml|
    xml.UploadVAppTemplateParams(
      "xmlns" => "http://www.vmware.com/vcloud/v1.5",
      "xmlns:ovf" => "http://schemas.dmtf.org/ovf/envelope/1",
      "manifestRequired" => uploadManifest,
      "name" => vappName) {
      xml.Description vappDescription
    }
  end

  params = {
    'method' => :post,
    'command' => "/vdc/#{vdcId}/action/uploadVAppTemplate"
  }

  response, headers = send_request(
    params,
    builder.to_xml,
    "application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml"
  )

  # Get vAppTemplate Link from location
  vAppTemplate = headers[:location].gsub(/.*\/vAppTemplate\/vappTemplate\-/, "")
  uploadHref = response.css("Files Link [rel='upload:default']").first[:href]
  descriptorUpload = uploadHref.gsub(/.*\/transfer\//, "")

  transferGUID = descriptorUpload.gsub("/descriptor.ovf", "")

  ovfFileBasename = File.basename(ovfFile, ".ovf")
  ovfDir = File.dirname(ovfFile)

  # Send OVF Descriptor
  uploadURL = "/transfer/#{descriptorUpload}"
  uploadFile = "#{ovfDir}/#{ovfFileBasename}.ovf"
  upload_file(uploadURL, uploadFile, "/vAppTemplate/vappTemplate-#{vAppTemplate}", uploadOptions)

  @logger.debug "OVF Descriptor uploaded."

  # Begin the catch for upload interruption
  begin
    params = {
      'method' => :get,
      'command' => "/vAppTemplate/vappTemplate-#{vAppTemplate}"
    }

    # Loop to wait for the upload links to show up in the vAppTemplate we just created
    while true
      response, headers = send_request(params)

      errored_task = response.css("Tasks Task [status='error']").first
      if errored_task
        error_msg = errored_task.css('Error').first['message']
        raise OVFError, "OVF Upload failed: #{error_msg}"
      end

      break unless response.css("Files Link [rel='upload:default']").count == 1
      sleep 1
    end

    if uploadManifest == "true"
      uploadURL = "/transfer/#{transferGUID}/descriptor.mf"
      uploadFile = "#{ovfDir}/#{ovfFileBasename}.mf"
      upload_file(uploadURL, uploadFile, "/vAppTemplate/vappTemplate-#{vAppTemplate}", uploadOptions)
      @logger.debug "OVF Manifest uploaded."
    end

    # Start uploading OVF VMDK files
    params = {
      'method' => :get,
      'command' => "/vAppTemplate/vappTemplate-#{vAppTemplate}"
    }
    response, headers = send_request(params)
    response.css("Files File [bytesTransferred='0'] Link [rel='upload:default']").each do |file|
      fileName = file[:href].gsub(/.*\/transfer\/#{transferGUID}\//, "")
      uploadFile = "#{ovfDir}/#{fileName}"
      uploadURL = "/transfer/#{transferGUID}/#{fileName}"
      upload_file(uploadURL, uploadFile, "/vAppTemplate/vappTemplate-#{vAppTemplate}", uploadOptions)
    end

    # Add item to the catalog catalogId
    builder = Nokogiri::XML::Builder.new do |xml|
      xml.CatalogItem(
        "xmlns" => "http://www.vmware.com/vcloud/v1.5",
        "type" => "application/vnd.vmware.vcloud.catalogItem+xml",
        "name" => vappName) {
        xml.Description vappDescription
        xml.Entity(
          "href" => "#{@api_url}/vAppTemplate/vappTemplate-#{vAppTemplate}"
          )
      }
    end

    params = {
      'method' => :post,
      'command' => "/catalog/#{catalogId}/catalogItems"
    }

    @logger.debug "Add item to catalog."
    response, headers = send_request(params, builder.to_xml,
                    "application/vnd.vmware.vcloud.catalogItem+xml")

    entity = response.css("Entity").first

    # TODO: the best thing would detect the real importing status.
    result = {}
    if entity
      result[:id] = entity['href'].gsub(/.*\/vAppTemplate\/vappTemplate\-/, "")
      result[:name] = entity['name']
    end
    result
  rescue Exception => e
    @logger.error "Exception detected: #{e.message}."

    # Get vAppTemplate Task
    params = {
      'method' => :get,
      'command' => "/vAppTemplate/vappTemplate-#{vAppTemplate}"
    }
    response, headers = send_request(params)

    # Cancel Task
    # Note that it might not exist (i.e., error for existing vdc entity)
    tasks = response.css("Tasks")
    unless tasks.empty?
      tasks.css("Task").each do |task|
        if task['status'] == 'error'
          @logger.error task.css('Error').first['message']
        else
          id = task['href'].gsub(/.*\/task\//, "")
          @logger.error "Aborting task #{id}..."
          cancel_task(id)
        end
      end
    end

    raise e
  end
end

#wait_task_completion(taskid) ⇒ Object

Poll a given task until completion



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/vcloud-rest/connection.rb', line 118

def wait_task_completion(taskid)
  errormsg = nil
  task = {}

  loop do
    task = get_task(taskid)
    break if task[:status] != 'running'
    sleep 1
  end

  if task[:status] == 'error'
    errormsg = task[:response].css("Error").first
    errormsg = "Error code #{errormsg['majorErrorCode']} - #{errormsg['message']}"
  end

  { :status => task[:status], :errormsg => errormsg,
    :start_time => task[:start_time], :end_time => task[:end_time] }
end