opennebula 3.9.80.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/LICENSE +202 -0
  2. data/NOTICE +47 -0
  3. data/lib/opennebula.rb +58 -0
  4. data/lib/opennebula/acl.rb +266 -0
  5. data/lib/opennebula/acl_pool.rb +55 -0
  6. data/lib/opennebula/client.rb +119 -0
  7. data/lib/opennebula/cluster.rb +249 -0
  8. data/lib/opennebula/cluster_pool.rb +58 -0
  9. data/lib/opennebula/datastore.rb +171 -0
  10. data/lib/opennebula/datastore_pool.rb +55 -0
  11. data/lib/opennebula/document.rb +261 -0
  12. data/lib/opennebula/document_json.rb +131 -0
  13. data/lib/opennebula/document_pool.rb +102 -0
  14. data/lib/opennebula/document_pool_json.rb +58 -0
  15. data/lib/opennebula/error.rb +52 -0
  16. data/lib/opennebula/group.rb +163 -0
  17. data/lib/opennebula/group_pool.rb +56 -0
  18. data/lib/opennebula/host.rb +201 -0
  19. data/lib/opennebula/host_pool.rb +93 -0
  20. data/lib/opennebula/image.rb +297 -0
  21. data/lib/opennebula/image_pool.rb +79 -0
  22. data/lib/opennebula/ldap_auth.rb +99 -0
  23. data/lib/opennebula/ldap_auth_spec.rb +70 -0
  24. data/lib/opennebula/pool.rb +160 -0
  25. data/lib/opennebula/pool_element.rb +269 -0
  26. data/lib/opennebula/server_cipher_auth.rb +148 -0
  27. data/lib/opennebula/server_x509_auth.rb +104 -0
  28. data/lib/opennebula/ssh_auth.rb +139 -0
  29. data/lib/opennebula/system.rb +141 -0
  30. data/lib/opennebula/template.rb +213 -0
  31. data/lib/opennebula/template_pool.rb +79 -0
  32. data/lib/opennebula/user.rb +174 -0
  33. data/lib/opennebula/user_pool.rb +55 -0
  34. data/lib/opennebula/virtual_machine.rb +560 -0
  35. data/lib/opennebula/virtual_machine_pool.rb +323 -0
  36. data/lib/opennebula/virtual_network.rb +249 -0
  37. data/lib/opennebula/virtual_network_pool.rb +79 -0
  38. data/lib/opennebula/x509_auth.rb +288 -0
  39. data/lib/opennebula/xml_element.rb +427 -0
  40. data/lib/opennebula/xml_pool.rb +45 -0
  41. data/lib/opennebula/xml_utils.rb +34 -0
  42. metadata +118 -0
@@ -0,0 +1,79 @@
1
+ # -------------------------------------------------------------------------- #
2
+ # Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs #
3
+ # #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
+ # not use this file except in compliance with the License. You may obtain #
6
+ # a copy of the License at #
7
+ # #
8
+ # http://www.apache.org/licenses/LICENSE-2.0 #
9
+ # #
10
+ # Unless required by applicable law or agreed to in writing, software #
11
+ # distributed under the License is distributed on an "AS IS" BASIS, #
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
13
+ # See the License for the specific language governing permissions and #
14
+ # limitations under the License. #
15
+ #--------------------------------------------------------------------------- #
16
+
17
+
18
+ require 'opennebula/pool'
19
+
20
+ module OpenNebula
21
+ class VirtualNetworkPool < Pool
22
+ #######################################################################
23
+ # Constants and Class attribute accessors
24
+ #######################################################################
25
+
26
+
27
+ VN_POOL_METHODS = {
28
+ :info => "vnpool.info"
29
+ }
30
+
31
+ #######################################################################
32
+ # Class constructor & Pool Methods
33
+ #######################################################################
34
+
35
+ # +client+ a Client object that represents a XML-RPC connection
36
+ # +user_id+ is to refer to a Pool with VirtualNetworks from that user
37
+ def initialize(client, user_id=0)
38
+ super('VNET_POOL','VNET',client)
39
+
40
+ @user_id = user_id
41
+ end
42
+
43
+ # Default Factory Method for the Pools
44
+ def factory(element_xml)
45
+ OpenNebula::VirtualNetwork.new(element_xml,@client)
46
+ end
47
+
48
+ #######################################################################
49
+ # XML-RPC Methods for the Virtual Network Object
50
+ #######################################################################
51
+
52
+ # Retrieves all or part of the VirtualMachines in the pool.
53
+ def info(*args)
54
+ case args.size
55
+ when 0
56
+ info_filter(VN_POOL_METHODS[:info],@user_id,-1,-1)
57
+ when 3
58
+ info_filter(VN_POOL_METHODS[:info],args[0],args[1],args[2])
59
+ end
60
+ end
61
+
62
+ def info_all()
63
+ return super(VN_POOL_METHODS[:info])
64
+ end
65
+
66
+ def info_mine()
67
+ return super(VN_POOL_METHODS[:info])
68
+ end
69
+
70
+ def info_group()
71
+ return super(VN_POOL_METHODS[:info])
72
+ end
73
+
74
+ alias_method :info!, :info
75
+ alias_method :info_all!, :info_all
76
+ alias_method :info_mine!, :info_mine
77
+ alias_method :info_group!, :info_group
78
+ end
79
+ end
@@ -0,0 +1,288 @@
1
+ # -------------------------------------------------------------------------- #
2
+ # Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs #
3
+ # #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
+ # not use this file except in compliance with the License. You may obtain #
6
+ # a copy of the License at #
7
+ # #
8
+ # http://www.apache.org/licenses/LICENSE-2.0 #
9
+ # #
10
+ # Unless required by applicable law or agreed to in writing, software #
11
+ # distributed under the License is distributed on an "AS IS" BASIS, #
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
13
+ # See the License for the specific language governing permissions and #
14
+ # limitations under the License. #
15
+ #--------------------------------------------------------------------------- #
16
+
17
+ require 'openssl'
18
+ require 'base64'
19
+ require 'fileutils'
20
+ require 'yaml'
21
+
22
+ module OpenNebula; end
23
+
24
+ # X509 authentication class. It can be used as a driver for auth_mad
25
+ # as auth method is defined. It also holds some helper methods to be used
26
+ # by oneauth command
27
+ class OpenNebula::X509Auth
28
+ ###########################################################################
29
+ #Constants with paths to relevant files and defaults
30
+ ###########################################################################
31
+ if !ENV["ONE_LOCATION"]
32
+ ETC_LOCATION = "/etc/one"
33
+ else
34
+ ETC_LOCATION = ENV["ONE_LOCATION"] + "/etc"
35
+ end
36
+
37
+ LOGIN_PATH = ENV['HOME']+'/.one/one_x509'
38
+
39
+ X509_AUTH_CONF_PATH = ETC_LOCATION + "/auth/x509_auth.conf"
40
+
41
+ X509_DEFAULTS = {
42
+ :ca_dir => ETC_LOCATION + "/auth/certificates"
43
+ }
44
+
45
+ def self.escape_dn(dn)
46
+ dn.gsub(/\s/) { |s| "\\"+s[0].ord.to_s(16) }
47
+ end
48
+
49
+ def self.unescape_dn(dn)
50
+ dn.gsub(/\\[0-9a-f]{2}/) { |s| s[1,2].to_i(16).chr }
51
+ end
52
+
53
+ ###########################################################################
54
+ # Initialize x509Auth object
55
+ #
56
+ # @param [Hash] default options for path
57
+ # @option options [String] :certs_pem
58
+ # cert chain array in colon-separated pem format
59
+ # @option options [String] :key_pem
60
+ # key in pem format
61
+ # @option options [String] :ca_dir
62
+ # directory of trusted CA's. Needed for auth method, not for login.
63
+ def initialize(options={})
64
+ @options ||= X509_DEFAULTS
65
+ @options.merge!(options)
66
+
67
+ load_options(X509_AUTH_CONF_PATH)
68
+
69
+ @cert_chain = @options[:certs_pem].collect do |cert_pem|
70
+ OpenSSL::X509::Certificate.new(cert_pem)
71
+ end
72
+
73
+ if @options[:key_pem]
74
+ @key = OpenSSL::PKey::RSA.new(@options[:key_pem])
75
+ end
76
+ end
77
+
78
+ ###########################################################################
79
+ # Client side
80
+ ###########################################################################
81
+
82
+ # Creates the login file for x509 authentication at ~/.one/one_x509.
83
+ # By default it is valid as long as the certificate is valid. It can
84
+ # be changed to any number of seconds with expire parameter (sec.)
85
+ def login(user, expire=0)
86
+ write_login(login_token(user,expire))
87
+ end
88
+
89
+ # Returns a valid password string to create a user using this auth driver.
90
+ # In this case the dn of the user certificate.
91
+ def password
92
+ self.class.escape_dn(@cert_chain[0].subject.to_s)
93
+ end
94
+
95
+ # Generates a login token in the form:
96
+ # user_name:x509:user_name:time_expires:cert_chain
97
+ # - user_name:time_expires is encrypted with the user certificate
98
+ # - user_name:time_expires:cert_chain is base64 encoded
99
+ def login_token(user, expire)
100
+ if expire != 0
101
+ expires = Time.now.to_i + expire.to_i
102
+ else
103
+ expires = @cert_chain[0].not_after.to_i
104
+ end
105
+
106
+ text_to_sign = "#{user}:#{expires}"
107
+ signed_text = encrypt(text_to_sign)
108
+
109
+ certs_pem = @cert_chain.collect{|cert| cert.to_pem}.join(":")
110
+
111
+ token = "#{signed_text}:#{certs_pem}"
112
+ token64 = Base64::encode64(token).strip.delete("\n")
113
+
114
+ login_out = "#{user}:#{token64}"
115
+
116
+ login_out
117
+ end
118
+
119
+ ###########################################################################
120
+ # Server side
121
+ ###########################################################################
122
+ # auth method for auth_mad
123
+ def authenticate(user, pass, signed_text)
124
+ begin
125
+ # Decryption demonstrates that the user posessed the private key.
126
+ _user, expires = decrypt(signed_text).split(':')
127
+
128
+ return "User name missmatch" if user != _user
129
+
130
+ return "x509 proxy expired" if Time.now.to_i >= expires.to_i
131
+
132
+ # Some DN in the chain must match a DN in the password
133
+ dn_ok = @cert_chain.each do |cert|
134
+ if pass.split('|').include?(
135
+ self.class.escape_dn(cert.subject.to_s))
136
+ break true
137
+ end
138
+ end
139
+
140
+ unless dn_ok == true
141
+ return "Certificate subject missmatch"
142
+ end
143
+
144
+ validate
145
+
146
+ return true
147
+ rescue => e
148
+ return e.message
149
+ end
150
+ end
151
+
152
+ private
153
+ # Writes a login_txt to the login file as defined in LOGIN_PATH
154
+ # constant
155
+ def write_login(login_txt)
156
+ # Inits login file path and creates ~/.one directory if needed
157
+ # Set instance variables
158
+ login_dir = File.dirname(LOGIN_PATH)
159
+
160
+ begin
161
+ FileUtils.mkdir_p(login_dir)
162
+ rescue Errno::EEXIST
163
+ end
164
+
165
+ file = File.open(LOGIN_PATH, "w")
166
+ file.write(login_txt)
167
+ file.close
168
+
169
+ File.chmod(0600,LOGIN_PATH)
170
+ end
171
+
172
+ # Load class options form a configuration file (yaml syntax)
173
+ def load_options(conf_file)
174
+ if File.readable?(conf_file)
175
+ conf_txt = File.read(conf_file)
176
+ conf_opt = YAML::load(conf_txt)
177
+
178
+ @options.merge!(conf_opt) if conf_opt != false
179
+ end
180
+ end
181
+
182
+ ###########################################################################
183
+ # Methods to encrpyt/decrypt keys
184
+ ###########################################################################
185
+ # Encrypts data with the private key of the user and returns
186
+ # base 64 encoded output in a single line
187
+ def encrypt(data)
188
+ return nil if !@key
189
+ Base64::encode64(@key.private_encrypt(data)).delete("\n").strip
190
+ end
191
+
192
+ # Decrypts base 64 encoded data with pub_key (public key)
193
+ def decrypt(data)
194
+ @cert_chain[0].public_key.public_decrypt(Base64::decode64(data))
195
+ end
196
+
197
+ ###########################################################################
198
+ # Validate the user certificate
199
+ ###########################################################################
200
+ def validate
201
+ now = Time.now
202
+
203
+ # Check start time and end time of certificates
204
+ @cert_chain.each do |cert|
205
+ if cert.not_before > now || cert.not_after < now
206
+ raise failed + "Certificate not valid. Current time is " +
207
+ now.localtime.to_s + "."
208
+ end
209
+ end
210
+
211
+ begin
212
+ # Validate the proxy certifcates
213
+ signee = @cert_chain[0]
214
+
215
+ check_crl(signee)
216
+
217
+ @cert_chain[1..-1].each do |cert|
218
+ if !((signee.issuer.to_s == cert.subject.to_s) &&
219
+ (signee.verify(cert.public_key)))
220
+ raise failed + signee.subject.to_s + " with issuer " +
221
+ signee.issuer.to_s + " was not verified by " +
222
+ cert.subject.to_s + "."
223
+ end
224
+ signee = cert
225
+ end
226
+
227
+ # Validate the End Entity certificate
228
+ if !@options[:ca_dir]
229
+ raise failed + "No certifcate authority directory was specified."
230
+ end
231
+
232
+ begin
233
+ ca_hash = signee.issuer.hash.to_s(16)
234
+ ca_path = @options[:ca_dir] + '/' + ca_hash + '.0'
235
+
236
+ ca_cert = OpenSSL::X509::Certificate.new(File.read(ca_path))
237
+
238
+ if !((signee.issuer.to_s == ca_cert.subject.to_s) &&
239
+ (signee.verify(ca_cert.public_key)))
240
+ raise failed + signee.subject.to_s + " with issuer " +
241
+ signee.issuer.to_s + " was not verified by " +
242
+ ca_cert.subject.to_s + "."
243
+ end
244
+
245
+ signee = ca_cert
246
+ end while ca_cert.subject.to_s != ca_cert.issuer.to_s
247
+ rescue
248
+ raise
249
+ end
250
+ end
251
+
252
+ def check_crl(signee)
253
+ failed = "Could not validate user credentials: "
254
+
255
+ ca_hash = signee.issuer.hash.to_s(16)
256
+ ca_path = @options[:ca_dir] + '/' + ca_hash + '.0'
257
+
258
+ crl_path = @options[:ca_dir] + '/' + ca_hash + '.r0'
259
+
260
+ if !File.exist?(crl_path)
261
+ if @options[:check_crl]
262
+ raise failed + "CRL file #{crl_path} does not exist"
263
+ else
264
+ return
265
+ end
266
+ end
267
+
268
+ ca_cert = OpenSSL::X509::Certificate.new( File.read(ca_path) )
269
+ crl_cert = OpenSSL::X509::CRL.new( File.read(crl_path) )
270
+
271
+ # First verify the CRL itself with its signer
272
+ unless crl_cert.verify( ca_cert.public_key ) then
273
+ raise failed + "CRL is not verified by its Signer"
274
+ end
275
+
276
+ # Extract the list of revoked certificates from the CRL
277
+ rc_array = crl_cert.revoked
278
+
279
+ # Loop over the list and compare with the target personal
280
+ # certificate
281
+ rc_array.each do |e|
282
+ if e.serial.eql?(signee.serial) then
283
+ raise failed + "#{signee.subject.to_s} is found in the "<<
284
+ "CRL, i.e. it is revoked"
285
+ end
286
+ end
287
+ end
288
+ end
@@ -0,0 +1,427 @@
1
+ # -------------------------------------------------------------------------- #
2
+ # Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs #
3
+ # #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
+ # not use this file except in compliance with the License. You may obtain #
6
+ # a copy of the License at #
7
+ # #
8
+ # http://www.apache.org/licenses/LICENSE-2.0 #
9
+ # #
10
+ # Unless required by applicable law or agreed to in writing, software #
11
+ # distributed under the License is distributed on an "AS IS" BASIS, #
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
13
+ # See the License for the specific language governing permissions and #
14
+ # limitations under the License. #
15
+ #--------------------------------------------------------------------------- #
16
+
17
+
18
+ module OpenNebula
19
+ # The XMLElement class provides an abstraction of the underlying
20
+ # XML parser engine. It provides XML-related methods for the Pool and
21
+ # PoolElement classes
22
+ class XMLElement
23
+
24
+ # xml:: _opaque xml object_ an xml object as returned by build_xml
25
+ def initialize(xml=nil)
26
+ @xml = xml
27
+ end
28
+
29
+ # Initialize a XML document for the element
30
+ # xml:: _String_ the XML document of the object
31
+ # root_element:: _String_ Base xml element
32
+ def initialize_xml(xml, root_element)
33
+ @xml = XMLElement.build_xml(xml, root_element)
34
+
35
+ if OpenNebula.is_error?(@xml)
36
+ @xml = nil
37
+ else
38
+ if NOKOGIRI
39
+ if @xml.size == 0
40
+ @xml = nil
41
+ end
42
+ else
43
+ if @xml.name != root_element
44
+ @xml = nil
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ # Builds a XML document
51
+ # xml:: _String_ the XML document of the object
52
+ # root_element:: _String_ Base xml element
53
+ # [return] _XML_ object for the underlying XML engine
54
+ def self.build_xml(xml, root_element)
55
+ begin
56
+ if NOKOGIRI
57
+ doc = Nokogiri::XML(xml).xpath("/#{root_element}")
58
+ else
59
+ doc = REXML::Document.new(xml).root
60
+ end
61
+ rescue Exception => e
62
+ return OpenNebula::Error.new(e.message)
63
+ end
64
+
65
+ return doc
66
+ end
67
+
68
+ # Extract a text element from the XML description of the PoolElement.
69
+ #
70
+ # @param [String] key Xpath expression
71
+ #
72
+ # @return [String, nil] If a text element is found, the element's
73
+ # text value. Otherwise, an empty string or nil, depending
74
+ # on the backend
75
+ #
76
+ # @example
77
+ # vm['VID'] # gets VM id
78
+ # vm['HISTORY/HOSTNAME'] # get the hostname from the history
79
+ def [](key)
80
+ if NOKOGIRI
81
+ element=@xml.xpath(key.to_s)
82
+
83
+ return nil if element.size == 0
84
+ else
85
+ element=@xml.elements[key.to_s]
86
+
87
+ return "" if element && !element.has_text?
88
+ end
89
+
90
+ element.text if element
91
+ end
92
+
93
+ # Delete an element from the xml
94
+ # xpath::_String_ xpath expression that selects the elemnts to be deleted
95
+ def delete_element(xpath)
96
+ if NOKOGIRI
97
+ @xml.xpath(xpath.to_s).remove
98
+ else
99
+ @xml.delete_element(xpath.to_s)
100
+ end
101
+ end
102
+
103
+ # Add a new element to the xml
104
+ # xpath::_String_ xpath xpression where the elemente will be added
105
+ # elems::_Hash_ Hash containing the pairs key-value to be included
106
+ # Examples:
107
+ # add_element('VM', 'NEW_ITEM' => 'NEW_VALUE')
108
+ # <VM><NEW_ITEM>NEW_VALUE</NEW_ITEM>...</VM>
109
+ #
110
+ # add_element('VM/TEMPLATE', 'V1' => {'X1' => 'A1', 'Y2' => 'A2'})
111
+ # <VM><TEMPLATE><V1><X1>A1</X1><Y2>A2</Y2>...</TEMPLATE></VM>
112
+ def add_element(xpath, elems)
113
+ elems.each { |key, value|
114
+ if value.instance_of?(Hash)
115
+ if NOKOGIRI
116
+ elem = Nokogiri::XML::Node.new key, @xml.document
117
+ value.each { |k2, v2|
118
+ child = Nokogiri::XML::Node.new k2, elem
119
+ child.content = v2
120
+ elem.add_child(child)
121
+ }
122
+ @xml.xpath(xpath.to_s).first.add_child(elem)
123
+ else
124
+ elem = REXML::Element.new(key)
125
+ value.each { |k2, v2|
126
+ elem.add_element(k2).text = v2
127
+ }
128
+ @xml.elements[xpath].add_element(elem)
129
+ end
130
+ else
131
+ if NOKOGIRI
132
+ elem = Nokogiri::XML::Node.new key, @xml.document
133
+ elem.content = value
134
+ @xml.xpath(xpath.to_s).first.add_child(elem)
135
+ else
136
+ @xml.elements[xpath].add_element(key).text = value
137
+ end
138
+ end
139
+ }
140
+ end
141
+
142
+ # Gets an array of text from elemenets extracted
143
+ # using the XPATH expression passed as filter
144
+ def retrieve_elements(filter)
145
+ elements_array = Array.new
146
+
147
+ if NOKOGIRI
148
+ @xml.xpath(filter.to_s).each { |pelem|
149
+ elements_array << pelem.text if pelem.text
150
+ }
151
+ else
152
+ @xml.elements.each(filter.to_s) { |pelem|
153
+ elements_array << pelem.text if pelem.text
154
+ }
155
+ end
156
+
157
+ if elements_array.size == 0
158
+ return nil
159
+ else
160
+ return elements_array
161
+ end
162
+
163
+ end
164
+
165
+ # Gets an attribute from an elemenT
166
+ # key:: _String_ xpath for the element
167
+ # name:: _String_ name of the attribute
168
+ def attr(key,name)
169
+ value = nil
170
+
171
+ if NOKOGIRI
172
+ element=@xml.xpath(key.to_s.upcase)
173
+ if element.size == 0
174
+ return nil
175
+ end
176
+
177
+ attribute = element.attr(name)
178
+
179
+ value = attribute.text if attribute != nil
180
+ else
181
+ element=@xml.elements[key.to_s.upcase]
182
+
183
+ value = element.attributes[name] if element != nil
184
+ end
185
+
186
+ return value
187
+ end
188
+
189
+ # Iterates over every Element in the XPath and calls the block with a
190
+ # a XMLElement
191
+ # block:: _Block_
192
+ def each(xpath_str,&block)
193
+ if NOKOGIRI
194
+ @xml.xpath(xpath_str).each { |pelem|
195
+ block.call XMLElement.new(pelem)
196
+ }
197
+ else
198
+ @xml.elements.each(xpath_str) { |pelem|
199
+ block.call XMLElement.new(pelem)
200
+ }
201
+ end
202
+ end
203
+
204
+ def each_xpath(xpath_str,&block)
205
+ if NOKOGIRI
206
+ @xml.xpath(xpath_str).each { |pelem|
207
+ block.call pelem.text
208
+ }
209
+ else
210
+ @xml.elements.each(xpath_str) { |pelem|
211
+ block.call pelem.text
212
+ }
213
+ end
214
+ end
215
+
216
+ def name
217
+ @xml.name
218
+ end
219
+
220
+ def text
221
+ if NOKOGIRI
222
+ @xml.content
223
+ else
224
+ @xml.text
225
+ end
226
+ end
227
+
228
+ # Returns wheter there are elements for a given XPath
229
+ # xpath_str:: _String_ XPath expression to locate the element
230
+ def has_elements?(xpath_str)
231
+ if NOKOGIRI
232
+ element = @xml.xpath(xpath_str.to_s.upcase)
233
+ return element != nil && element.children.size > 0
234
+ else
235
+ element = @xml.elements[xpath_str.to_s]
236
+ return element != nil && element.has_elements?
237
+ end
238
+ end
239
+
240
+ # Returns the <TEMPLATE> element in text form
241
+ # indent:: _Boolean_ indents the resulting string, default true
242
+ def template_str(indent=true)
243
+ template_like_str('TEMPLATE', indent)
244
+ end
245
+
246
+ # Returns the <TEMPLATE> element in XML form
247
+ def template_xml
248
+ if NOKOGIRI
249
+ @xml.xpath('TEMPLATE').to_s
250
+ else
251
+ @xml.elements['TEMPLATE'].to_s
252
+ end
253
+ end
254
+
255
+ # Returns the xml of an element
256
+ def element_xml(xpath)
257
+ if NOKOGIRI
258
+ @xml.xpath(xpath).to_s
259
+ else
260
+ @xml.elements[xpath].to_s
261
+ end
262
+ end
263
+
264
+ # Returns elements in text form
265
+ # root_element:: _String_ base element
266
+ # indent:: _Boolean_ indents the resulting string, default true
267
+ # xpath_exp:: _String_ filter elements with a XPath
268
+ def template_like_str(root_element, indent=true, xpath_exp=nil)
269
+ if NOKOGIRI
270
+ xml_template = @xml.xpath(root_element).to_s
271
+ rexml = REXML::Document.new(xml_template).root
272
+ else
273
+ rexml = @xml.elements[root_element]
274
+ end
275
+
276
+ if indent
277
+ ind_enter = "\n"
278
+ ind_tab = ' '
279
+ else
280
+ ind_enter = ''
281
+ ind_tab = ' '
282
+ end
283
+
284
+ str = rexml.elements.collect(xpath_exp) {|n|
285
+ next if n.class != REXML::Element
286
+
287
+ str_line = ""
288
+
289
+ if n.has_elements?
290
+ str_line << "#{n.name}=[#{ind_enter}" << n.collect { |n2|
291
+
292
+ next if n2.class != REXML::Element or !n2.has_text?
293
+
294
+ str = "#{ind_tab}#{n2.name}=#{attr_to_str(n2.text)}"
295
+
296
+ }.compact.join(",#{ind_enter}") << " ]"
297
+ else
298
+ next if !n.has_text?
299
+
300
+ str_line << "#{n.name}=#{attr_to_str(n.text)}"
301
+ end
302
+
303
+ str_line
304
+ }.compact.join("\n")
305
+
306
+ return str
307
+ end
308
+
309
+ #
310
+ #
311
+ #
312
+ def to_xml(pretty=false)
313
+ if NOKOGIRI && pretty
314
+ str = @xml.to_xml
315
+ elsif REXML_FORMATTERS && pretty
316
+ str = String.new
317
+
318
+ formatter = REXML::Formatters::Pretty.new
319
+ formatter.compact = true
320
+
321
+ formatter.write(@xml,str)
322
+ else
323
+ str = @xml.to_s
324
+ end
325
+
326
+ return str
327
+ end
328
+
329
+ # @return [Hash] a hash representing the resource
330
+ def to_hash
331
+ hash = {}
332
+
333
+ if NOKOGIRI
334
+ if @xml.instance_of?(Nokogiri::XML::NodeSet)
335
+ @xml.each { |c|
336
+ if c.element?
337
+ build_hash(hash, c)
338
+ end
339
+ }
340
+ else
341
+ build_hash(hash, @xml)
342
+ end
343
+ else
344
+ build_hash(hash, @xml)
345
+ end
346
+
347
+ hash
348
+ end
349
+
350
+ private
351
+
352
+ #
353
+ #
354
+ #
355
+ def build_hash(hash, element)
356
+ if NOKOGIRI
357
+ array = element.children
358
+ if array.length==1 and (array.first.text? or array.first.cdata?)
359
+ r = array.first.text
360
+ else
361
+ r = {}
362
+ array.each { |c|
363
+ if c.element?
364
+ build_hash(r, c)
365
+ end
366
+ }
367
+ end
368
+ else
369
+ r = {}
370
+ if element.has_elements?
371
+ element.each_element { |c| build_hash(r, c) }
372
+ elsif element.has_text?
373
+ r = element.text
374
+ end
375
+ end
376
+
377
+ key = element.name
378
+ if hash.has_key?(key)
379
+ if hash[key].instance_of?(Array)
380
+ hash[key] << r
381
+ else
382
+ hash[key] = [hash[key], r]
383
+ end
384
+ else
385
+ hash[key] = r
386
+ end
387
+
388
+ hash
389
+ end
390
+
391
+ #
392
+ #
393
+ #
394
+ def attr_to_str(attr)
395
+ attr.gsub!('"',"\\\"")
396
+ attr = "\"#{attr}\""
397
+
398
+ return attr
399
+ end
400
+ end
401
+
402
+ # The XMLUtilsPool module provides an abstraction of the underlying
403
+ # XML parser engine. It provides XML-related methods for the Pools
404
+ class XMLPool < XMLElement
405
+
406
+ def initialize(xml=nil)
407
+ super(xml)
408
+ end
409
+
410
+ #Executes the given block for each element of the Pool
411
+ #block:: _Block_
412
+ def each_element(block)
413
+ if NOKOGIRI
414
+ @xml.xpath(
415
+ "#{@element_name}").each {|pelem|
416
+ block.call self.factory(pelem)
417
+ }
418
+ else
419
+ @xml.elements.each(
420
+ "#{@element_name}") {|pelem|
421
+ block.call self.factory(pelem)
422
+ }
423
+ end
424
+ end
425
+ end
426
+
427
+ end