ovfparse 0.9.0 → 0.9.1

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.
@@ -0,0 +1,455 @@
1
+ require 'nokogiri'
2
+ require 'open-uri'
3
+
4
+ class VmCollection
5
+ @url
6
+ @base_path
7
+ @name
8
+ @version
9
+ @protocol
10
+ @size
11
+ @xml
12
+ @package_details
13
+ @ovf_namespace
14
+ @default_namespace
15
+
16
+
17
+ attr_accessor :url, :base_path, :name, :version, :state, :protocol, :size, :xml, :references, :diskSection, :networkSection, :virtualSystemCollection, :package_details
18
+
19
+ def initialize
20
+ @package_details = Array.new
21
+ end
22
+
23
+ def to_s
24
+ self.uri
25
+ end
26
+
27
+ def uri
28
+ if (nil==@protocol) then
29
+ return @url
30
+ else
31
+ return (@protocol + "://" + @url)
32
+ end
33
+ end
34
+
35
+ def initialize(uri)
36
+ @package_details = Array.new
37
+ if (URI::HTTP==uri.class) then
38
+ uri = uri.to_s
39
+ end
40
+
41
+ (@protocol, @url) = uri.split(":", 2) unless !uri
42
+ @url.sub!(/^\/{0,2}/, '')
43
+ @protocol.downcase
44
+ @url.downcase
45
+ @name = uri.split('/').last
46
+ end
47
+
48
+ def self.create uri
49
+ (@protocol, @url) = uri.split(":", 2) unless !uri
50
+ @url.sub!(/^\/{0,2}/, '')
51
+ @protocol.downcase
52
+ @url.downcase
53
+ #if @protocol=='ftp'
54
+ # FtpVmPackage.new(uri)
55
+ if @protocol=='http'
56
+ HttpVmCollection.new(uri)
57
+ #elsif @protocol=='https'
58
+ # HttpsVmPackage.new(uri)
59
+ elsif @protocol=='file'
60
+ FileVmCollection.new(uri)
61
+ end
62
+ end
63
+
64
+
65
+ def fetch
66
+ end
67
+
68
+ # Caches all of the base elements inside Envelope for fast access
69
+ def loadElementRefs
70
+ children = @xml.root.children
71
+ @ovf_namespace = xml.root.namespace_definitions.detect{|ns| ns.prefix == "ovf"}
72
+ @default_namespace = xml.root.namespace
73
+
74
+ @references = getChildByName(xml.root, 'References')
75
+ @virtualSystemCollection = getChildByName(xml.root, 'VirtualSystemCollection')
76
+
77
+ @diskSection = getChildByName(xml.root, 'DiskSection') || @virtualSystem.add_previous_sibling(xml.create_element('DiskSection', {}))
78
+ @networkSection = getChildByName(xml.root, 'NetworkSection') || @virtualSystem.add_previous_sibling(xml.create_element('NetworkSection', {}))
79
+ end
80
+
81
+ # Returns the first child node of the passed node whose name matches the passed name.
82
+ def getChildByName(node, childName)
83
+ return node.nil? ? nil : node.children.detect{ |element| element.name == childName}
84
+ end
85
+
86
+ # Returns every child node of the passed node whose name matches the passed name.
87
+ def getChildrenByName(node, childName)
88
+ return node.nil? ? [] : node.children.select{ |element| element.name == childName}
89
+ end
90
+
91
+ def checkschema(schema)
92
+ response = ""
93
+
94
+ isValid = true
95
+ schema.validate(@xml).each do |error|
96
+ response << error.message + "\n"
97
+ isValid = false
98
+ end
99
+
100
+ return [isValid, response]
101
+ end
102
+
103
+ def getVmName
104
+ return virtualSystem['id'] || ''
105
+ end
106
+
107
+ def getVmDescription
108
+ # ???
109
+ end
110
+
111
+ def getVmDisks
112
+ disks = Array.new
113
+ filenames = Hash.new
114
+ getChildrenByName(references, 'File').each { |node|
115
+ filenames[node['id']] = node['href']
116
+ }
117
+
118
+ getChildrenByName(diskSection, 'Disk').each { |node|
119
+ capacity = node['capacity']
120
+ units = node['capacityAllocationUnits']
121
+ if(units == "byte * 2^40")
122
+ capacity = (capacity.to_i * 1099511627776).to_s
123
+ elsif(units == "byte * 2^30")
124
+ capacity = (capacity.to_i * 1073741824).to_s
125
+ elsif(units == "byte * 2^20")
126
+ capacity = (capacity.to_i * 1048576).to_s
127
+ elsif(units == "byte * 2^10")
128
+ capacity = (capacity.to_i * 1024).to_s
129
+ end
130
+ thin_size = node['populatedSize']
131
+ disks.push({ 'name' => node['diskId'], 'location' => filenames[node['fileRef']], 'size' => capacity, 'thin_size' => (thin_size || "-1") })
132
+ }
133
+
134
+ return disks
135
+ end
136
+
137
+ def getVmNetworks
138
+ networks = Array.new
139
+ getChildrenByName(networkSection, 'Network').each { |node|
140
+ descriptionNode = getChildByName(node, 'Description')
141
+ text = descriptionNode.nil? ? '' : descriptionNode.text
142
+ networks.push({'location' => node['name'], 'notes' => text })
143
+ }
144
+ return networks
145
+ end
146
+
147
+ def getVirtualSystems
148
+ end
149
+
150
+ def self.constructFromVmPackages(packages)
151
+ packages.each{ |package|
152
+ addVmPackage(package)
153
+ }
154
+ end
155
+
156
+ def addVmPackage(package)
157
+ details = PackageDetails.new()
158
+ details.id = package.getVmName
159
+
160
+ newRefs = package.getVmReferences
161
+ newRefs.each { |newRef|
162
+ addFileReference(newRef, package, details)
163
+ }
164
+
165
+ newDisks = package.getVmDisks
166
+ newDisks.each { |newDisk|
167
+ addDisk(newDisk, package, details)
168
+ }
169
+
170
+ newNetworks = package.getVmNetworks
171
+ newNetworks.each { |newNetwork|
172
+ addNetwork(newNetwork, package, details)
173
+ }
174
+
175
+ package_details.push(details)
176
+ addVirtualSystem(package.virtualSystem)
177
+ end
178
+
179
+ def addFileReference(pendingRef, childPackage, details)
180
+ # Compare this one to all the existing ones to prevent duplicates
181
+ isNewRef = true
182
+ currentFileRefs = getChildrenByName(references, 'File')
183
+ currentFileRefs.each{ |oldRef|
184
+ if(compareFileReferences(oldRef, pendingRef))
185
+ isNewRef = false
186
+ break
187
+ end
188
+ }
189
+
190
+ # If it's not a dup, add it
191
+ if(isNewRef)
192
+ newFileID = "file" + (currentFileRefs.length + 1).to_s
193
+ newFile = references.add_child(xml.create_element('File', {'href' => pendingRef['href'], 'id' => newFileID, 'size' => pendingRef['size']}))
194
+ newFile.attribute("href").namespace = @ovf_namespace
195
+ newFile.attribute("id").namespace = @ovf_namespace
196
+ newFile.attribute("size").namespace = @ovf_namespace
197
+ findReplace(childPackage, pendingRef['id'], newFileID)
198
+ details.files.push(newFileID)
199
+ else
200
+ details.files.push(pendingRef['href'])
201
+ end
202
+ end
203
+
204
+ def compareFileReferences(oldRef, newRef)
205
+ return (oldRef['href'] == newRef['href']) #&& (oldRef['size'] == newRef['size'])
206
+ end
207
+
208
+ def addDisk(newDisk, childPackage, details)
209
+ # Always add a disk, even if it's a clone, cause this VM will need its own copy
210
+ isNewDisk = true
211
+ currentDisks = getChildrenByName(diskSection, 'Disk')
212
+ #currentDisks.each{ |oldDisk|
213
+ # if(compareDisks(oldDisk, newDisk))
214
+ # isNewDisk = false
215
+ # break
216
+ # end
217
+ #}
218
+
219
+ if(isNewDisk)
220
+ newDiskID = "vmdisk" + (currentDisks.length + 1).to_s
221
+ newDiskNode = diskSection.add_child(xml.create_element('Disk', {
222
+ 'capacity' => newDisk['size'],
223
+ 'diskId' => newDiskID,
224
+ 'fileRef' => getChildrenByName(references, 'File').detect{ |ref| ref['href'] == newDisk['location'] }['id'],
225
+ 'format' => "http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized",
226
+ 'populatedSize' => newDisk['thin_size']
227
+ }))
228
+ newDiskNode.attribute("capacity").namespace = @ovf_namespace
229
+ newDiskNode.attribute("diskId").namespace = @ovf_namespace
230
+ newDiskNode.attribute("fileRef").namespace = @ovf_namespace
231
+ newDiskNode.attribute("format").namespace = @ovf_namespace
232
+ newDiskNode.attribute("populatedSize").namespace = @ovf_namespace
233
+ findReplace(childPackage, newDisk['name'], newDiskID)
234
+ details.disks.push(newDiskID)
235
+ end
236
+ end
237
+
238
+ def compareDisks(oldDisk, newDisk)
239
+ filename = getChildrenByName(references, 'File').detect{ |ref| ref['id'] == oldDisk['fileRef'] }['href']
240
+ return (filename == newDisk['location'])
241
+ end
242
+
243
+ def addNetwork(newNetwork, childPackage, details)
244
+ isNewNetwork = true
245
+ currentNetworks = getChildrenByName(networkSection, 'Network')
246
+ currentNetworks.each{ |oldNetwork|
247
+ if(compareNetworks(oldNetwork, newNetwork))
248
+ isNewNetwork = false
249
+ break
250
+ end
251
+ }
252
+
253
+ if(isNewNetwork)
254
+ newNode = networkSection.add_child(xml.create_element('Network', {'name' => newNetwork['location']}))
255
+ newNode.attribute("name").namespace = @ovf_namespace
256
+ newNode.add_child(xml.create_element('Description', newNetwork['notes']))
257
+ end
258
+ details.networks.push(newNetwork['location'])
259
+ end
260
+
261
+ def compareNetworks(oldNetwork, newNetwork)
262
+ return (oldNetwork['name'] == newNetwork['location'])
263
+ end
264
+
265
+ def addVirtualSystem(newSystem)
266
+ ovfNamespace = xml.root.namespace
267
+ newNode = virtualSystemCollection.add_child(newSystem.clone)
268
+ newNode.namespace = ovfNamespace
269
+ end
270
+
271
+ def findReplace(package, oldval, newval)
272
+ package.xml.xpath("//*").each{ |node|
273
+ if(node.children.length == 1 && node.children[0].text? && node.content.match(oldval) != nil)
274
+ node.content = node.content.gsub(oldval, newval)
275
+ end
276
+ node.attributes.each{ |key, attr|
277
+ attr.value = attr.value.gsub(oldval, newval)
278
+ }
279
+ }
280
+ end
281
+
282
+ def search(virtualSystem, value)
283
+ virtualSystem.xpath(".//*").each{ |node|
284
+ if(node.children.length == 1 && node.children[0].text? && node.content.match(value) != nil)
285
+ return true
286
+ end
287
+ node.attributes.each{ |key, attr|
288
+ if(attr.value.match(value) != nil)
289
+ return true
290
+ end
291
+ }
292
+ }
293
+ return false
294
+ end
295
+
296
+ # @todo any need to make this a general purpose "writer" ?
297
+ def self.constructSkeleton
298
+ builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
299
+ xml.Envelope('xmlns' => 'http://schemas.dmtf.org/ovf/envelope/1', 'xmlns:cim' => "http://schemas.dmtf.org/wbem/wscim/1/common", 'xmlns:ovf' => "http://schemas.dmtf.org/ovf/envelope/1", 'xmlns:rasd' => "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData", 'xmlns:vmw' => "http://www.vmware.com/schema/ovf", 'xmlns:vssd' => "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData", 'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance", 'xmlns:cops' => 'http://cops.mitre.org/1.2', 'xmlns:cpe' => 'http://cpe.mitre.org/dictionary/2.0') {
300
+ xml.References{}
301
+ xml.DiskSection{
302
+ xml.Info "Virtual disk information"
303
+ }
304
+ xml.NetworkSection{
305
+ xml.Info "List of logical networks"
306
+ }
307
+ xml.VirtualSystemCollection('ovf:id' => "vm_collection"){
308
+ xml.Info "A collection of virtual machines"
309
+ }
310
+ }
311
+
312
+ node = Nokogiri::XML::Comment.new(xml.doc, ' skeleton framework constructed by OVFparse ')
313
+ xml.doc.children[0].add_previous_sibling(node)
314
+ end
315
+
316
+ newPackage = NewVmCollection.new
317
+ newPackage.xml = builder.doc
318
+ newPackage.loadElementRefs
319
+ return newPackage
320
+ end
321
+
322
+ def writeXML(filename)
323
+ file = File.new(filename, "w")
324
+ file.puts(xml.to_s)
325
+ file.close
326
+ end
327
+
328
+ def splitIntoPackages
329
+ packages = Array.new
330
+
331
+ i = 0
332
+ virtualSystems = getChildrenByName(virtualSystemCollection, "VirtualSystem")
333
+ virtualSystems.each{ |virtualSystem|
334
+ details = package_details[i]
335
+ new_package = VmPackage.construct_skeleton
336
+ new_package.loadElementRefs
337
+
338
+ details.files.each{ |file|
339
+ node = getChildrenByName(references, "File").detect{ |node| node['id'] == file }
340
+ new_package.references.add_child(node.clone)
341
+ }
342
+
343
+ details.disks.each{ |disk|
344
+ node = getChildrenByName(diskSection, "Disk").detect{ |node| node['diskId'] == disk }
345
+ new_package.diskSection.add_child(node.clone)
346
+ }
347
+
348
+ details.networks.each{ |network|
349
+ node = getChildrenByName(networkSection, "Network").detect{ |node| node['name'] == network }
350
+ new_package.networkSection.add_child(node.clone)
351
+ }
352
+
353
+ new_package.virtualSystem.children.unlink
354
+ new_package.virtualSystem.add_child(virtualSystem.clone.children)
355
+ new_package.virtualSystem.children.each{ |child|
356
+ child.namespace = new_package.virtualSystem.namespace
357
+ }
358
+ packages.push(new_package)
359
+
360
+ i += 1
361
+ }
362
+
363
+ return packages
364
+ end
365
+
366
+ def parseXML
367
+ fileRefs = Hash.new
368
+ fileNames = Array.new
369
+ getChildrenByName(references, "File").each{ |fileRef|
370
+ fileNames.push(fileRef['id'])
371
+ }
372
+
373
+ diskNames = Array.new
374
+ getChildrenByName(diskSection, "Disk").each{ |disk|
375
+ diskNames.push(disk['diskId'])
376
+ fileRefs[disk['diskId']] = disk['fileRef']
377
+ }
378
+
379
+ networkNames = Array.new
380
+ getChildrenByName(networkSection, "Network").each{ |network|
381
+ networkNames.push(network['name'])
382
+ }
383
+
384
+ getChildrenByName(virtualSystemCollection, "VirtualSystem").each{ |virtualSystem|
385
+ details = PackageDetails.new()
386
+ details.id = virtualSystem['id']
387
+
388
+ fileNames.each{ |fileName|
389
+ if(search(virtualSystem, fileName))
390
+ details.files.push(fileName)
391
+ end
392
+ }
393
+
394
+ diskNames.each{ |diskName|
395
+ if(search(virtualSystem, diskName))
396
+ details.disks.push(diskName)
397
+ if(!details.files.include?(fileRefs[diskName]))
398
+ details.files.push(fileRefs[diskName])
399
+ end
400
+ end
401
+ }
402
+
403
+ networkNames.each{ |networkName|
404
+ if(search(virtualSystem, networkName))
405
+ details.networks.push(networkName)
406
+ end
407
+ }
408
+
409
+ package_details.push(details)
410
+ }
411
+ end
412
+
413
+ end
414
+
415
+ class NewVmCollection < VmCollection
416
+ def initialize
417
+ @package_details = Array.new
418
+ end
419
+ end
420
+
421
+ class HttpVmCollection < VmCollection
422
+ def fetch
423
+ url = URI.parse(URI.escape(self.uri))
424
+ @xml = Nokogiri::XML(open(url)) do |config|
425
+ config.noblanks.strict.noent
426
+ end
427
+
428
+ loadElementRefs
429
+ parseXML
430
+ end
431
+ end
432
+
433
+ class FileVmCollection < VmCollection
434
+ def fetch
435
+ @xml = Nokogiri::XML(File.open(self.url)) do |config|
436
+ config.noblanks.strict.noent
437
+ end
438
+
439
+ loadElementRefs
440
+ parseXML
441
+ end
442
+ end
443
+
444
+ class PackageDetails
445
+
446
+ attr_accessor :id, :files, :disks, :networks
447
+
448
+ def initialize
449
+ @id = ""
450
+ @files = Array.new
451
+ @disks = Array.new
452
+ @networks = Array.new
453
+ end
454
+
455
+ end
@@ -1,3 +1,4 @@
1
+ require 'nokogiri'
1
2
  require 'open-uri'
2
3
 
3
4
  class VmPackage
@@ -237,7 +238,9 @@ class VmPackage
237
238
  getChildrenByName(diskSection, 'Disk').each { |node|
238
239
  capacity = node['capacity']
239
240
  units = node['capacityAllocationUnits']
240
- if(units == "byte * 2^30")
241
+ if(units == "byte * 2^40")
242
+ capacity = (capacity.to_i * 1099511627776).to_s
243
+ elsif(units == "byte * 2^30")
241
244
  capacity = (capacity.to_i * 1073741824).to_s
242
245
  elsif(units == "byte * 2^20")
243
246
  capacity = (capacity.to_i * 1048576).to_s
@@ -261,6 +264,13 @@ class VmPackage
261
264
  return networks
262
265
  end
263
266
 
267
+ def getVmReferences
268
+ refs = Array.new
269
+ getChildrenByName(references, 'File').each { |node|
270
+ refs.push({'href' => node['href'], 'id' => node['id'], 'size' => node['size']})
271
+ }
272
+ return refs
273
+ end
264
274
 
265
275
  def getVmCPUs
266
276
  return getVirtualQuantity(3)
@@ -565,6 +575,17 @@ class VmPackage
565
575
  end
566
576
  end
567
577
 
578
+ def setPropertyDefault(key, newVal)
579
+ getChildrenByName(virtualSystem, "ProductSection").each{ |product|
580
+ getChildrenByName(product, "Property").each{ |property|
581
+ if(property['key'] == key)
582
+ property['ovf:value'] = newVal
583
+ return
584
+ end
585
+ }
586
+ }
587
+ end
588
+
568
589
  def setElements(updated_element, parent_node, element_list)
569
590
  element_list.each { |element_details|
570
591
  updated_value = updated_element[element_details['element_ref']]
data/lib/ovfparse.rb CHANGED
@@ -8,6 +8,7 @@ path = File.expand_path(File.dirname(__FILE__))
8
8
 
9
9
  require path + '/ovfparse/vmrepository'
10
10
  require path + '/ovfparse/vmpackage'
11
+ require path + '/ovfparse/vmcollection'
11
12
  require path + '/ovfparse/os_id_table'
12
13
  require path + '/ovfparse/esx4_vmrepository'
13
14
  require path + '/ovfparse/file_vmrepository'
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ovfparse
3
3
  version: !ruby/object:Gem::Version
4
- hash: 59
4
+ hash: 57
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 9
9
- - 0
10
- version: 0.9.0
9
+ - 1
10
+ version: 0.9.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jim Barkley
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-09-01 00:00:00 -04:00
18
+ date: 2011-10-10 00:00:00 -04:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -48,6 +48,7 @@ files:
48
48
  - lib/ovfparse/marketplace_repository.rb
49
49
  - lib/ovfparse/vc_repository.rb
50
50
  - lib/ovfparse/https_vmrepository.rb
51
+ - lib/ovfparse/vmcollection.rb
51
52
  - lib/ovfparse/vmrepository.rb
52
53
  - lib/ovfparse/esx_repository.rb
53
54
  - lib/ovfparse/vmpackage.rb