ovfparse 0.9.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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