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,113 @@
|
|
1
|
+
module VCloudClient
|
2
|
+
class Connection
|
3
|
+
##
|
4
|
+
# Fetch details about a given catalog
|
5
|
+
def get_catalog(catalogId)
|
6
|
+
params = {
|
7
|
+
'method' => :get,
|
8
|
+
'command' => "/catalog/#{catalogId}"
|
9
|
+
}
|
10
|
+
|
11
|
+
response, headers = send_request(params)
|
12
|
+
description = response.css("Description").first
|
13
|
+
description = description.text unless description.nil?
|
14
|
+
|
15
|
+
items = {}
|
16
|
+
response.css("CatalogItem[type='application/vnd.vmware.vcloud.catalogItem+xml']").each do |item|
|
17
|
+
items[item['name']] = item['href'].gsub(/.*\/catalogItem\//, "")
|
18
|
+
end
|
19
|
+
{ :id => catalogId, :description => description, :items => items }
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# Friendly helper method to fetch an catalog id by name
|
24
|
+
# - organization hash (from get_organization/get_organization_by_name)
|
25
|
+
# - catalog name
|
26
|
+
def get_catalog_id_by_name(organization, catalogName)
|
27
|
+
result = nil
|
28
|
+
|
29
|
+
organization[:catalogs].each do |catalog|
|
30
|
+
if catalog[0].downcase == catalogName.downcase
|
31
|
+
result = catalog[1]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
result
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# Friendly helper method to fetch an catalog by name
|
40
|
+
# - organization hash (from get_organization/get_organization_by_name)
|
41
|
+
# - catalog name
|
42
|
+
def get_catalog_by_name(organization, catalogName)
|
43
|
+
result = nil
|
44
|
+
|
45
|
+
organization[:catalogs].each do |catalog|
|
46
|
+
if catalog[0].downcase == catalogName.downcase
|
47
|
+
result = get_catalog(catalog[1])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
result
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Fetch details about a given catalog item:
|
56
|
+
# - description
|
57
|
+
# - vApp templates
|
58
|
+
def get_catalog_item(catalogItemId)
|
59
|
+
params = {
|
60
|
+
'method' => :get,
|
61
|
+
'command' => "/catalogItem/#{catalogItemId}"
|
62
|
+
}
|
63
|
+
|
64
|
+
response, headers = send_request(params)
|
65
|
+
description = response.css("Description").first
|
66
|
+
description = description.text unless description.nil?
|
67
|
+
|
68
|
+
items = []
|
69
|
+
response.css("Entity[type='application/vnd.vmware.vcloud.vAppTemplate+xml']").each do |item|
|
70
|
+
itemId = item['href'].gsub(/.*\/vAppTemplate\/vappTemplate\-/, "")
|
71
|
+
|
72
|
+
# Fetch the catalogItemId information
|
73
|
+
params = {
|
74
|
+
'method' => :get,
|
75
|
+
'command' => "/vAppTemplate/vappTemplate-#{itemId}"
|
76
|
+
}
|
77
|
+
response, headers = send_request(params)
|
78
|
+
|
79
|
+
# VMs Hash for all the vApp VM entities
|
80
|
+
vms_hash = {}
|
81
|
+
response.css("/VAppTemplate/Children/Vm").each do |vmElem|
|
82
|
+
vmName = vmElem["name"]
|
83
|
+
vmId = vmElem["href"].gsub(/.*\/vAppTemplate\/vm\-/, "")
|
84
|
+
|
85
|
+
# Add the VM name/id to the VMs Hash
|
86
|
+
vms_hash[vmName] = { :id => vmId }
|
87
|
+
end
|
88
|
+
|
89
|
+
items << { :id => itemId,
|
90
|
+
:name => item['name'],
|
91
|
+
:vms_hash => vms_hash }
|
92
|
+
end
|
93
|
+
{ :id => catalogItemId, :description => description, :items => items }
|
94
|
+
end
|
95
|
+
|
96
|
+
##
|
97
|
+
# friendly helper method to fetch an catalogItem by name
|
98
|
+
# - catalogId (use get_catalog_name(org, name))
|
99
|
+
# - catalagItemName
|
100
|
+
def get_catalog_item_by_name(catalogId, catalogItemName)
|
101
|
+
result = nil
|
102
|
+
catalogElems = get_catalog(catalogId)
|
103
|
+
|
104
|
+
catalogElems[:items].each do |k, v|
|
105
|
+
if (k.downcase == catalogItemName.downcase)
|
106
|
+
result = get_catalog_item(v)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
result
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module VCloudClient
|
2
|
+
class Connection
|
3
|
+
##
|
4
|
+
# Fetch details about a given network
|
5
|
+
def get_network(networkId)
|
6
|
+
response = get_base_network(networkId)
|
7
|
+
|
8
|
+
name = response.css('OrgVdcNetwork').attribute('name').text
|
9
|
+
|
10
|
+
description = response.css("Description").first
|
11
|
+
description = description.text unless description.nil?
|
12
|
+
|
13
|
+
gateway = response.css('Gateway')
|
14
|
+
gateway = gateway.text unless gateway.nil?
|
15
|
+
|
16
|
+
netmask = response.css('Netmask')
|
17
|
+
netmask = netmask.text unless netmask.nil?
|
18
|
+
|
19
|
+
fence_mode = response.css('FenceMode')
|
20
|
+
fence_mode = fence_mode.text unless fence_mode.nil?
|
21
|
+
|
22
|
+
start_address = response.css('StartAddress')
|
23
|
+
start_address = start_address.text unless start_address.nil?
|
24
|
+
|
25
|
+
end_address = response.css('EndAddress')
|
26
|
+
end_address = end_address.text unless end_address.nil?
|
27
|
+
|
28
|
+
|
29
|
+
{ :id => networkId, :name => name, :description => description,
|
30
|
+
:gateway => gateway, :netmask => netmask, :fence_mode => fence_mode,
|
31
|
+
:start_address => start_address, :end_address => end_address }
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Friendly helper method to fetch an network id by name
|
36
|
+
# - organization hash (from get_organization/get_organization_by_name)
|
37
|
+
# - network name
|
38
|
+
def get_network_id_by_name(organization, networkName)
|
39
|
+
result = nil
|
40
|
+
|
41
|
+
organization[:networks].each do |network|
|
42
|
+
if network[0].downcase == networkName.downcase
|
43
|
+
result = network[1]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
result
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Friendly helper method to fetch an network by name
|
52
|
+
# - organization hash (from get_organization/get_organization_by_name)
|
53
|
+
# - network name
|
54
|
+
def get_network_by_name(organization, networkName)
|
55
|
+
result = nil
|
56
|
+
|
57
|
+
organization[:networks].each do |network|
|
58
|
+
if network[0].downcase == networkName.downcase
|
59
|
+
result = get_network(network[1])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
result
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
# Get a network configuration
|
68
|
+
def get_base_network(networkId)
|
69
|
+
params = {
|
70
|
+
'method' => :get,
|
71
|
+
'command' => "/network/#{networkId}"
|
72
|
+
}
|
73
|
+
|
74
|
+
base_network, headers = send_request(params)
|
75
|
+
base_network
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module VCloudClient
|
2
|
+
class Connection
|
3
|
+
##
|
4
|
+
# Fetch existing organizations and their IDs
|
5
|
+
def get_organizations
|
6
|
+
params = {
|
7
|
+
'method' => :get,
|
8
|
+
'command' => '/org'
|
9
|
+
}
|
10
|
+
|
11
|
+
response, headers = send_request(params)
|
12
|
+
orgs = response.css('OrgList Org')
|
13
|
+
|
14
|
+
results = {}
|
15
|
+
orgs.each do |org|
|
16
|
+
results[org['name']] = org['href'].gsub(/.*\/org\//, "")
|
17
|
+
end
|
18
|
+
results
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# friendly helper method to fetch an Organization Id by name
|
23
|
+
# - name (this isn't case sensitive)
|
24
|
+
def get_organization_id_by_name(name)
|
25
|
+
result = nil
|
26
|
+
|
27
|
+
# Fetch all organizations
|
28
|
+
organizations = get_organizations()
|
29
|
+
|
30
|
+
organizations.each do |organization|
|
31
|
+
if organization[0].downcase == name.downcase
|
32
|
+
result = organization[1]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
result
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
##
|
40
|
+
# friendly helper method to fetch an Organization by name
|
41
|
+
# - name (this isn't case sensitive)
|
42
|
+
def get_organization_by_name(name)
|
43
|
+
result = nil
|
44
|
+
|
45
|
+
# Fetch all organizations
|
46
|
+
organizations = get_organizations()
|
47
|
+
|
48
|
+
organizations.each do |organization|
|
49
|
+
if organization[0].downcase == name.downcase
|
50
|
+
result = get_organization(organization[1])
|
51
|
+
end
|
52
|
+
end
|
53
|
+
result
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# Fetch details about an organization:
|
58
|
+
# - catalogs
|
59
|
+
# - vdcs
|
60
|
+
# - networks
|
61
|
+
# - task lists
|
62
|
+
def get_organization(orgId)
|
63
|
+
params = {
|
64
|
+
'method' => :get,
|
65
|
+
'command' => "/org/#{orgId}"
|
66
|
+
}
|
67
|
+
|
68
|
+
response, headers = send_request(params)
|
69
|
+
catalogs = {}
|
70
|
+
response.css("Link[type='application/vnd.vmware.vcloud.catalog+xml']").each do |item|
|
71
|
+
catalogs[item['name']] = item['href'].gsub(/.*\/catalog\//, "")
|
72
|
+
end
|
73
|
+
|
74
|
+
vdcs = {}
|
75
|
+
response.css("Link[type='application/vnd.vmware.vcloud.vdc+xml']").each do |item|
|
76
|
+
vdcs[item['name']] = item['href'].gsub(/.*\/vdc\//, "")
|
77
|
+
end
|
78
|
+
|
79
|
+
networks = {}
|
80
|
+
response.css("Link[type='application/vnd.vmware.vcloud.orgNetwork+xml']").each do |item|
|
81
|
+
networks[item['name']] = item['href'].gsub(/.*\/network\//, "")
|
82
|
+
end
|
83
|
+
|
84
|
+
tasklists = {}
|
85
|
+
response.css("Link[type='application/vnd.vmware.vcloud.tasksList+xml']").each do |item|
|
86
|
+
tasklists[item['name']] = item['href'].gsub(/.*\/tasksList\//, "")
|
87
|
+
end
|
88
|
+
|
89
|
+
{ :catalogs => catalogs, :vdcs => vdcs, :networks => networks, :tasklists => tasklists }
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# Fetch tasks from a given task list
|
94
|
+
#
|
95
|
+
# Note: id can be retrieved using get_organization
|
96
|
+
def get_tasks_list(id)
|
97
|
+
params = {
|
98
|
+
'method' => :get,
|
99
|
+
'command' => "/tasksList/#{id}"
|
100
|
+
}
|
101
|
+
|
102
|
+
response, headers = send_request(params)
|
103
|
+
|
104
|
+
tasks = []
|
105
|
+
|
106
|
+
response.css('Task').each do |task|
|
107
|
+
id = task['href'].gsub(/.*\/task\//, "")
|
108
|
+
operation = task['operationName']
|
109
|
+
status = task['status']
|
110
|
+
error = nil
|
111
|
+
error = task.css('Error').first['message'] if task['status'] == 'error'
|
112
|
+
start_time = task['startTime']
|
113
|
+
end_time = task['endTime']
|
114
|
+
user_canceled = task['cancelRequested'] == 'true'
|
115
|
+
|
116
|
+
tasks << {
|
117
|
+
:id => id,
|
118
|
+
:operation => operation,
|
119
|
+
:status => status,
|
120
|
+
:error => error,
|
121
|
+
:start_time => start_time,
|
122
|
+
:end_time => end_time,
|
123
|
+
:user_canceled => user_canceled
|
124
|
+
}
|
125
|
+
end
|
126
|
+
tasks
|
127
|
+
end
|
128
|
+
|
129
|
+
##
|
130
|
+
# Cancel a given task
|
131
|
+
#
|
132
|
+
# The task will be marked for cancellation
|
133
|
+
def cancel_task(id)
|
134
|
+
params = {
|
135
|
+
'method' => :post,
|
136
|
+
'command' => "/task/#{id}/action/cancel"
|
137
|
+
}
|
138
|
+
|
139
|
+
# Nothing useful is returned here
|
140
|
+
# If return code is 20x return true
|
141
|
+
send_request(params)
|
142
|
+
true
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,251 @@
|
|
1
|
+
module VCloudClient
|
2
|
+
class Connection
|
3
|
+
##
|
4
|
+
# Upload an OVF package
|
5
|
+
# - vdcId
|
6
|
+
# - vappName
|
7
|
+
# - vappDescription
|
8
|
+
# - ovfFile
|
9
|
+
# - catalogId
|
10
|
+
# - uploadOptions {}
|
11
|
+
def upload_ovf(vdcId, vappName, vappDescription, ovfFile, catalogId, uploadOptions={})
|
12
|
+
raise ::IOError, "OVF #{ovfFile} is missing." unless File.exists?(ovfFile)
|
13
|
+
|
14
|
+
# if send_manifest is not set, setting it true
|
15
|
+
if uploadOptions[:send_manifest].nil? || uploadOptions[:send_manifest]
|
16
|
+
uploadManifest = "true"
|
17
|
+
else
|
18
|
+
uploadManifest = "false"
|
19
|
+
end
|
20
|
+
|
21
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
22
|
+
xml.UploadVAppTemplateParams(
|
23
|
+
"xmlns" => "http://www.vmware.com/vcloud/v1.5",
|
24
|
+
"xmlns:ovf" => "http://schemas.dmtf.org/ovf/envelope/1",
|
25
|
+
"manifestRequired" => uploadManifest,
|
26
|
+
"name" => vappName) {
|
27
|
+
xml.Description vappDescription
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
params = {
|
32
|
+
'method' => :post,
|
33
|
+
'command' => "/vdc/#{vdcId}/action/uploadVAppTemplate"
|
34
|
+
}
|
35
|
+
|
36
|
+
response, headers = send_request(
|
37
|
+
params,
|
38
|
+
builder.to_xml,
|
39
|
+
"application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml"
|
40
|
+
)
|
41
|
+
|
42
|
+
# Get vAppTemplate Link from location
|
43
|
+
vAppTemplate = headers[:location].gsub(/.*\/vAppTemplate\/vappTemplate\-/, "")
|
44
|
+
descriptorUpload = response.css("Files Link [rel='upload:default']").first[:href].gsub("#{@host_url}/transfer/", "")
|
45
|
+
transferGUID = descriptorUpload.gsub("/descriptor.ovf", "")
|
46
|
+
|
47
|
+
ovfFileBasename = File.basename(ovfFile, ".ovf")
|
48
|
+
ovfDir = File.dirname(ovfFile)
|
49
|
+
|
50
|
+
# Send OVF Descriptor
|
51
|
+
uploadURL = "/transfer/#{descriptorUpload}"
|
52
|
+
uploadFile = "#{ovfDir}/#{ovfFileBasename}.ovf"
|
53
|
+
upload_file(uploadURL, uploadFile, vAppTemplate, uploadOptions)
|
54
|
+
|
55
|
+
@logger.debug "OVF Descriptor uploaded."
|
56
|
+
|
57
|
+
# Begin the catch for upload interruption
|
58
|
+
begin
|
59
|
+
params = {
|
60
|
+
'method' => :get,
|
61
|
+
'command' => "/vAppTemplate/vappTemplate-#{vAppTemplate}"
|
62
|
+
}
|
63
|
+
|
64
|
+
# Loop to wait for the upload links to show up in the vAppTemplate we just created
|
65
|
+
while true
|
66
|
+
response, headers = send_request(params)
|
67
|
+
|
68
|
+
errored_task = response.css("Tasks Task [status='error']").first
|
69
|
+
if errored_task
|
70
|
+
error_msg = errored_task.css('Error').first['message']
|
71
|
+
raise OVFError, "OVF Upload failed: #{error_msg}"
|
72
|
+
end
|
73
|
+
|
74
|
+
break unless response.css("Files Link [rel='upload:default']").count == 1
|
75
|
+
sleep 1
|
76
|
+
end
|
77
|
+
|
78
|
+
if uploadManifest == "true"
|
79
|
+
uploadURL = "/transfer/#{transferGUID}/descriptor.mf"
|
80
|
+
uploadFile = "#{ovfDir}/#{ovfFileBasename}.mf"
|
81
|
+
upload_file(uploadURL, uploadFile, vAppTemplate, uploadOptions)
|
82
|
+
@logger.debug "OVF Manifest uploaded."
|
83
|
+
end
|
84
|
+
|
85
|
+
# Start uploading OVF VMDK files
|
86
|
+
params = {
|
87
|
+
'method' => :get,
|
88
|
+
'command' => "/vAppTemplate/vappTemplate-#{vAppTemplate}"
|
89
|
+
}
|
90
|
+
response, headers = send_request(params)
|
91
|
+
response.css("Files File [bytesTransferred='0'] Link [rel='upload:default']").each do |file|
|
92
|
+
fileName = file[:href].gsub("#{@host_url}/transfer/#{transferGUID}/","")
|
93
|
+
uploadFile = "#{ovfDir}/#{fileName}"
|
94
|
+
uploadURL = "/transfer/#{transferGUID}/#{fileName}"
|
95
|
+
upload_file(uploadURL, uploadFile, vAppTemplate, uploadOptions)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Add item to the catalog catalogId
|
99
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
100
|
+
xml.CatalogItem(
|
101
|
+
"xmlns" => "http://www.vmware.com/vcloud/v1.5",
|
102
|
+
"type" => "application/vnd.vmware.vcloud.catalogItem+xml",
|
103
|
+
"name" => vappName) {
|
104
|
+
xml.Description vappDescription
|
105
|
+
xml.Entity(
|
106
|
+
"href" => "#{@api_url}/vAppTemplate/vappTemplate-#{vAppTemplate}"
|
107
|
+
)
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
111
|
+
params = {
|
112
|
+
'method' => :post,
|
113
|
+
'command' => "/catalog/#{catalogId}/catalogItems"
|
114
|
+
}
|
115
|
+
|
116
|
+
@logger.debug "Add item to catalog."
|
117
|
+
response, headers = send_request(params, builder.to_xml,
|
118
|
+
"application/vnd.vmware.vcloud.catalogItem+xml")
|
119
|
+
|
120
|
+
entity = response.css("Entity").first
|
121
|
+
|
122
|
+
# TODO: the best thing would detect the real importing status.
|
123
|
+
result = {}
|
124
|
+
if entity
|
125
|
+
result[:id] = entity['href'].gsub(/.*\/vAppTemplate\/vappTemplate\-/, "")
|
126
|
+
result[:name] = entity['name']
|
127
|
+
end
|
128
|
+
result
|
129
|
+
rescue Exception => e
|
130
|
+
@logger.error "Exception detected: #{e.message}."
|
131
|
+
|
132
|
+
# Get vAppTemplate Task
|
133
|
+
params = {
|
134
|
+
'method' => :get,
|
135
|
+
'command' => "/vAppTemplate/vappTemplate-#{vAppTemplate}"
|
136
|
+
}
|
137
|
+
response, headers = send_request(params)
|
138
|
+
|
139
|
+
# Cancel Task
|
140
|
+
# Note that it might not exist (i.e., error for existing vdc entity)
|
141
|
+
tasks = response.css("Tasks")
|
142
|
+
unless tasks.empty?
|
143
|
+
tasks.css("Task").each do |task|
|
144
|
+
if task['status'] == 'error'
|
145
|
+
@logger.error task.css('Error').first['message']
|
146
|
+
else
|
147
|
+
id = task['href'].gsub(/.*\/task\//, "")
|
148
|
+
@logger.error "Aborting task #{id}..."
|
149
|
+
cancel_task(id)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
raise e
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
##
|
160
|
+
# Upload a large file in configurable chunks, output an optional progressbar
|
161
|
+
def upload_file(uploadURL, uploadFile, vAppTemplate, config={})
|
162
|
+
raise ::IOError, "#{uploadFile} not found." unless File.exists?(uploadFile)
|
163
|
+
|
164
|
+
# Set chunksize to 10M if not specified otherwise
|
165
|
+
chunkSize = (config[:chunksize] || 10485760)
|
166
|
+
|
167
|
+
# Set progress bar to default format if not specified otherwise
|
168
|
+
progressBarFormat = (config[:progressbar_format] || "%e <%B> %p%% %t")
|
169
|
+
|
170
|
+
# Set progress bar length to 120 if not specified otherwise
|
171
|
+
progressBarLength = (config[:progressbar_length] || 120)
|
172
|
+
|
173
|
+
# Open our file for upload
|
174
|
+
uploadFileHandle = File.new(uploadFile, "rb" )
|
175
|
+
fileName = File.basename(uploadFileHandle)
|
176
|
+
|
177
|
+
progressBarTitle = "Uploading: " + uploadFile.to_s
|
178
|
+
|
179
|
+
# Create a progressbar object if progress bar is enabled
|
180
|
+
if config[:progressbar_enable] == true && uploadFileHandle.size.to_i > chunkSize
|
181
|
+
progressbar = ProgressBar.create(
|
182
|
+
:title => progressBarTitle,
|
183
|
+
:starting_at => 0,
|
184
|
+
:total => uploadFileHandle.size.to_i,
|
185
|
+
:length => progressBarLength,
|
186
|
+
:format => progressBarFormat
|
187
|
+
)
|
188
|
+
else
|
189
|
+
@logger.info progressBarTitle
|
190
|
+
end
|
191
|
+
# Create a new HTTP client
|
192
|
+
clnt = HTTPClient.new
|
193
|
+
|
194
|
+
# Disable SSL cert verification
|
195
|
+
clnt.ssl_config.verify_mode=(OpenSSL::SSL::VERIFY_NONE)
|
196
|
+
|
197
|
+
# Suppress SSL depth message
|
198
|
+
clnt.ssl_config.verify_callback=proc{ |ok, ctx|; true };
|
199
|
+
|
200
|
+
# Perform ranged upload until the file reaches its end
|
201
|
+
until uploadFileHandle.eof?
|
202
|
+
|
203
|
+
# Create ranges for this chunk upload
|
204
|
+
rangeStart = uploadFileHandle.pos
|
205
|
+
rangeStop = uploadFileHandle.pos.to_i + chunkSize
|
206
|
+
|
207
|
+
# Read current chunk
|
208
|
+
fileContent = uploadFileHandle.read(chunkSize)
|
209
|
+
|
210
|
+
# If statement to handle last chunk transfer if is > than filesize
|
211
|
+
if rangeStop.to_i > uploadFileHandle.size.to_i
|
212
|
+
contentRange = "bytes #{rangeStart.to_s}-#{uploadFileHandle.size.to_s}/#{uploadFileHandle.size.to_s}"
|
213
|
+
rangeLen = uploadFileHandle.size.to_i - rangeStart.to_i
|
214
|
+
else
|
215
|
+
contentRange = "bytes #{rangeStart.to_s}-#{rangeStop.to_s}/#{uploadFileHandle.size.to_s}"
|
216
|
+
rangeLen = rangeStop.to_i - rangeStart.to_i
|
217
|
+
end
|
218
|
+
|
219
|
+
# Build headers
|
220
|
+
extheader = {
|
221
|
+
'x-vcloud-authorization' => @auth_key,
|
222
|
+
'Content-Range' => contentRange,
|
223
|
+
'Content-Length' => rangeLen.to_s
|
224
|
+
}
|
225
|
+
|
226
|
+
begin
|
227
|
+
uploadRequest = "#{@host_url}#{uploadURL}"
|
228
|
+
connection = clnt.request('PUT', uploadRequest, nil, fileContent, extheader)
|
229
|
+
|
230
|
+
if config[:progressbar_enable] == true && uploadFileHandle.size.to_i > chunkSize
|
231
|
+
params = {
|
232
|
+
'method' => :get,
|
233
|
+
'command' => "/vAppTemplate/vappTemplate-#{vAppTemplate}"
|
234
|
+
}
|
235
|
+
response, headers = send_request(params)
|
236
|
+
|
237
|
+
response.css("Files File [name='#{fileName}']").each do |file|
|
238
|
+
progressbar.progress=file[:bytesTransferred].to_i
|
239
|
+
end
|
240
|
+
end
|
241
|
+
rescue
|
242
|
+
retryTime = (config[:retry_time] || 5)
|
243
|
+
@logger.warn "Range #{contentRange} failed to upload, retrying the chunk in #{retryTime.to_s} seconds, to stop the action press CTRL+C."
|
244
|
+
sleep retryTime.to_i
|
245
|
+
retry
|
246
|
+
end
|
247
|
+
end
|
248
|
+
uploadFileHandle.close
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|