From: František Dvořák Date: Wed, 12 Mar 2014 17:31:58 +0000 (+0100) Subject: ruby-opennebula-4.4 - initial gem2deb package. X-Git-Url: http://scientific.zcu.cz/git/?a=commitdiff_plain;h=5110154551476dc74b5bb4e769f64c2797a050ab;p=rOCCI-packaging.git ruby-opennebula-4.4 - initial gem2deb package. --- diff --git a/ruby-opennebula-4.4/LICENSE b/ruby-opennebula-4.4/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/ruby-opennebula-4.4/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ruby-opennebula-4.4/NOTICE b/ruby-opennebula-4.4/NOTICE new file mode 100644 index 0000000..0e12ebb --- /dev/null +++ b/ruby-opennebula-4.4/NOTICE @@ -0,0 +1,51 @@ +OpenNebula Open Source Project +-------------------------------------------------------------------------------- +Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs +-------------------------------------------------------------------------------- + +You can find more information about the project, release notes and +documentation at www.OpenNebula.org + +AUTHORS + +- Ruben Santiago Montero (rsmontero@opennebula.org) +- Ignacio Martin Llorente (imllorente@opennebula.org) + +ACKNOWLEDGEMENTS + +The following people have contributed to the development of the technology +- Javier Fontan Muiños (jfontan@opennebula.org) +- Constantino Vazquez Blanco (tinova@opennebula.org) +- Jaime Melis Bayo (jmelis@opennebula.org) +- Carlos Martin Sanchez (cmartin@opennebula.org) +- Daniel Molina Aranda (dmolina@opennebula.org) +- Hector Sanjuan Redondo (hsanjuan@opennebula.org) + +The new features for service elasticity (oneFlow) introduced in OpenNebula 4.2 +were funded by BlackBerry in the context of the Fund a Feature Program. + +OpenNebula Project also acknowledges the contributions of C12G Labs developers. + +LICENSE + +OpenNebula is licensed under the Apache License, Version 2.0 (the +"License"); you may not use this software except in compliance with the +License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied +See the License for the specific language governing permissions and +limitations under the Apache License. + +THIRD-PARTY SOFTWARE + +OpenNebula distribution includes third-party software under fully compatible +open-source licenses. See the following directories and the NOTICE files +they contain for more information: + +- share/vendor +- src/sunstone/public/vendor +- src/oca/java/lib diff --git a/ruby-opennebula-4.4/checksums.yaml.gz b/ruby-opennebula-4.4/checksums.yaml.gz new file mode 100644 index 0000000..18c22a4 Binary files /dev/null and b/ruby-opennebula-4.4/checksums.yaml.gz differ diff --git a/ruby-opennebula-4.4/debian/changelog b/ruby-opennebula-4.4/debian/changelog new file mode 100644 index 0000000..38966ec --- /dev/null +++ b/ruby-opennebula-4.4/debian/changelog @@ -0,0 +1,5 @@ +ruby-opennebula (4.4.0-1) UNRELEASED; urgency=medium + + * Initial release (Closes: #nnnn) + + -- FrantiÅ¡ek Dvořák Wed, 12 Mar 2014 18:30:49 +0100 diff --git a/ruby-opennebula-4.4/debian/compat b/ruby-opennebula-4.4/debian/compat new file mode 100644 index 0000000..7f8f011 --- /dev/null +++ b/ruby-opennebula-4.4/debian/compat @@ -0,0 +1 @@ +7 diff --git a/ruby-opennebula-4.4/debian/control b/ruby-opennebula-4.4/debian/control new file mode 100644 index 0000000..8b93418 --- /dev/null +++ b/ruby-opennebula-4.4/debian/control @@ -0,0 +1,19 @@ +Source: ruby-opennebula +Section: ruby +Priority: optional +Maintainer: Debian Ruby Extras Maintainers +Uploaders: FrantiÅ¡ek Dvořák +Build-Depends: debhelper (>= 7.0.50~), gem2deb (>= 0.6.1~) +Standards-Version: 3.9.4 +#Vcs-Git: git://anonscm.debian.org/pkg-ruby-extras/ruby-opennebula.git +#Vcs-Browser: http://anonscm.debian.org/gitweb/?p=pkg-ruby-extras/ruby-opennebula.git;a=summary +Homepage: http://opennebula.org +XS-Ruby-Versions: all + +Package: ruby-opennebula +Architecture: all +XB-Ruby-Versions: ${ruby:Versions} +Depends: ${shlibs:Depends}, ${misc:Depends}, ruby | ruby-interpreter +# nokogiri (>= 0), json (>= 0) +Description: OpenNebula Client API + Libraries needed to talk to OpenNebula diff --git a/ruby-opennebula-4.4/debian/copyright b/ruby-opennebula-4.4/debian/copyright new file mode 100644 index 0000000..1c1e1f6 --- /dev/null +++ b/ruby-opennebula-4.4/debian/copyright @@ -0,0 +1,35 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: opennebula +Source: FIXME + +Files: * +Copyright: + +License: GPL-2+ (FIXME) + +Files: debian/* +Copyright: 2014 FrantiÅ¡ek Dvořák +License: GPL-2+ (FIXME) +Comment: the Debian packaging is licensed under the same terms as the original package. + +License: GPL-2+ (FIXME) + This program is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later + version. + . + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the GNU General Public License for more + details. + . + You should have received a copy of the GNU General Public + License along with this package; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, + Boston, MA 02110-1301 USA + . + On Debian systems, the full text of the GNU General Public + License version 2 can be found in the file + `/usr/share/common-licenses/GPL-2'. diff --git a/ruby-opennebula-4.4/debian/ruby-opennebula.docs b/ruby-opennebula-4.4/debian/ruby-opennebula.docs new file mode 100644 index 0000000..d0ab95f --- /dev/null +++ b/ruby-opennebula-4.4/debian/ruby-opennebula.docs @@ -0,0 +1 @@ +# FIXME: READMEs found diff --git a/ruby-opennebula-4.4/debian/rules b/ruby-opennebula-4.4/debian/rules new file mode 100755 index 0000000..82ddc0c --- /dev/null +++ b/ruby-opennebula-4.4/debian/rules @@ -0,0 +1,15 @@ +#!/usr/bin/make -f +#export DH_VERBOSE=1 +# +# Uncomment to ignore all test failures (but the tests will run anyway) +#export DH_RUBY_IGNORE_TESTS=all +# +# Uncomment to ignore some test failures (but the tests will run anyway). +# Valid values: +#export DH_RUBY_IGNORE_TESTS=ruby1.9.1 ruby2.0 require-rubygems +# +# If you need to specify the .gemspec (eg there is more than one) +#export DH_RUBY_GEMSPEC=gem.gemspec + +%: + dh $@ --buildsystem=ruby --with ruby diff --git a/ruby-opennebula-4.4/debian/source/format b/ruby-opennebula-4.4/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/ruby-opennebula-4.4/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/ruby-opennebula-4.4/debian/watch b/ruby-opennebula-4.4/debian/watch new file mode 100644 index 0000000..5d5095f --- /dev/null +++ b/ruby-opennebula-4.4/debian/watch @@ -0,0 +1,2 @@ +version=3 +http://pkg-ruby-extras.alioth.debian.org/cgi-bin/gemwatch/opennebula .*/opennebula-(.*).tar.gz diff --git a/ruby-opennebula-4.4/lib/opennebula.rb b/ruby-opennebula-4.4/lib/opennebula.rb new file mode 100644 index 0000000..13e5b5b --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula.rb @@ -0,0 +1,58 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +begin # require 'rubygems' + require 'rubygems' +rescue Exception +end + +require 'digest/sha1' +require 'rexml/document' +require 'pp' + +require 'opennebula/xml_utils' +require 'opennebula/client' +require 'opennebula/error' +require 'opennebula/virtual_machine' +require 'opennebula/virtual_machine_pool' +require 'opennebula/virtual_network' +require 'opennebula/virtual_network_pool' +require 'opennebula/image' +require 'opennebula/image_pool' +require 'opennebula/user' +require 'opennebula/user_pool' +require 'opennebula/host' +require 'opennebula/host_pool' +require 'opennebula/template' +require 'opennebula/template_pool' +require 'opennebula/group' +require 'opennebula/group_pool' +require 'opennebula/acl' +require 'opennebula/acl_pool' +require 'opennebula/datastore' +require 'opennebula/datastore_pool' +require 'opennebula/cluster' +require 'opennebula/cluster_pool' +require 'opennebula/document' +require 'opennebula/document_pool' +require 'opennebula/system' + +module OpenNebula + + # OpenNebula version + VERSION = '4.4.0' +end diff --git a/ruby-opennebula-4.4/lib/opennebula/acl.rb b/ruby-opennebula-4.4/lib/opennebula/acl.rb new file mode 100644 index 0000000..1051d0e --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/acl.rb @@ -0,0 +1,265 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +module OpenNebula + + # Abstract rules of the type USER RESOURCE RIGHTS + # which are: + # USER -> # + # @ + # ALL + # RESOURCE -> + separated list and "/{#,@,%}|ALL" + # VM, + # HOST + # NET + # IMAGE + # USER + # TEMPLATE + # GROUP + # ACL + # RIGHTS -> + separated list + # USE + # MANAGE + # ADMIN + # CREATE + class Acl < PoolElement + + USERS = { + "UID" => 0x100000000, + "GID" => 0x200000000, + "ALL" => 0x400000000, + "CLUSTER" => 0x800000000 + } + + RESOURCES = + { + "VM" => 0x1000000000, + "HOST" => 0x2000000000, + "NET" => 0x4000000000, + "IMAGE" => 0x8000000000, + "USER" => 0x10000000000, + "TEMPLATE" => 0x20000000000, + "GROUP" => 0x40000000000, + "DATASTORE" => 0x100000000000, + "CLUSTER" => 0x200000000000, + "DOCUMENT" => 0x400000000000 + } + + RIGHTS = + { + "USE" => 0x1, # Auth. to use an object + "MANAGE" => 0x2, # Auth. to perform management actions + "ADMIN" => 0x4, # Auth. to perform administrative actions + "CREATE" => 0x8 # Auth. to create an object + } + + # Constructor + # + # @param xml [String] must be an xml built with {.build_xml} + # @param client [Client] represents an XML-RPC connection + def initialize(xml, client) + super(xml,client) + end + + # Creates an empty XML representation. It contains the id, if it is + # specified. + # + # @param pe_id [Integer] rule ID + # + # @return [String] an empty XML representation + def self.build_xml(pe_id=nil) + if pe_id + acl_xml = "#{pe_id}" + else + acl_xml = "" + end + + XMLElement.build_xml(acl_xml,'ACL') + end + + # Creates a new ACL rule. + # + # @param user [String] + # A string containing a hex number, e.g. 0x100000001 + # @param resource [String] + # A string containing a hex number, e.g. 0x2100000001 + # @param rights [String] + # A string containing a hex number, e.g. 0x10 + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def allocate(user, resource, rights) + return super( AclPool::ACL_POOL_METHODS[:addrule], + user, + resource, + rights ) + end + + # Deletes the Acl rule + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def delete() + super(AclPool::ACL_POOL_METHODS[:delrule]) + end + + # Does nothing, individual ACL rules info can't be retrieved from + # OpenNebula + # + # @return [nil] nil + def info() + return nil + end + + alias_method :info!, :info + + # Parses a rule string, e.g. "#5 HOST+VM/@12 INFO+CREATE+DELETE" + # + # @param rule_str [String] an ACL rule in string format + # + # @return [Array] an Array containing 3 strings (hex 64b numbers), + # or OpenNebula::Error objects + def self.parse_rule(rule_str) + ret = Array.new + + rule_str = rule_str.split(" ") + + if rule_str.length != 3 + return OpenNebula::Error.new( + "String needs three components: User, Resource, Rights") + end + + ret << parse_users(rule_str[0]) + ret << parse_resources(rule_str[1]) + ret << parse_rights(rule_str[2]) + + errors=ret.map do |arg| + if OpenNebula.is_error?(arg) + arg.message + else + nil + end + end + + errors.compact! + + if errors.length>0 + return OpenNebula::Error.new(errors.join(', ')) + end + + return ret + end + +private + + # Converts a string in the form [#, @, *] to a hex. number + # + # @param users [String] Users component string + # + # @return [String] A string containing a hex number + def self.parse_users(users) + begin + return calculate_ids(users).to_i.to_s(16) + rescue Exception => e + return OpenNebula::Error.new(e.message) + end + end + + # Converts a resources string to a hex. number + # + # @param resources [String] Resources component string + # + # @return [String] A string containing a hex number + def self.parse_resources(resources) + begin + ret = 0 + resources = resources.split("/") + + if resources.size != 2 + raise "Resource '#{resources}' malformed" + end + + resources[0].split("+").each{ |resource| + if !RESOURCES[resource.upcase] + raise "Resource '#{resource}' does not exist" + end + ret += RESOURCES[resource.upcase] + } + + ret += calculate_ids(resources[1]) + + return ret.to_i.to_s(16) + rescue Exception => e + return OpenNebula::Error.new(e.message) + end + end + + # Converts a rights string to a hex. number + # + # @param rights [String] Rights component string + # + # @return [String] A string containing a hex number + def self.parse_rights(rights) + begin + ret = 0 + rights = rights.split("+") + + rights.each{ |right| + raise "Right '#{right}' does not exist" if !RIGHTS[right.upcase] + + ret += RIGHTS[right.upcase] + } + + return ret.to_i.to_s(16) + rescue Exception => e + return OpenNebula::Error.new(e.message) + end + end + + # Calculates the numeric value for a String containing an individual + # (#), group (@) or all (*) ID component + # + # @param id_str [String] Rule Id string + # + # @return [Integer] the numeric value for the given id_str + def self.calculate_ids(id_str) + raise "ID string '#{id_str}' malformed" if + !id_str.match(/^([\#@\%]\d+|\*)$/) + + value = 0 + + case id_str[0..0] + when "#" + value = USERS["UID"] + users_value = id_str[1..-1].to_i + value + + when "@" + value = USERS["GID"] + users_value = id_str[1..-1].to_i + value + + when "*" + users_value = USERS["ALL"] + + when "%" + value = USERS["CLUSTER"] + users_value = id_str[1..-1].to_i + value + end + + return users_value + end + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/acl_pool.rb b/ruby-opennebula-4.4/lib/opennebula/acl_pool.rb new file mode 100644 index 0000000..02e4263 --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/acl_pool.rb @@ -0,0 +1,55 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +require 'opennebula/pool' + +module OpenNebula + class AclPool < Pool + + ####################################################################### + # Constants and Class Methods + ####################################################################### + + + ACL_POOL_METHODS = { + :info => "acl.info", + :addrule => "acl.addrule", + :delrule => "acl.delrule" + } + + # Class constructor + def initialize(client) + super('ACL_POOL','ACL',client) + end + + def factory(element_xml) + OpenNebula::Acl.new(element_xml, @client) + end + + ####################################################################### + # XML-RPC Methods + ####################################################################### + + # Retrieves the ACL Pool + def info() + # Retrieves all the Acls in the pool. + super(ACL_POOL_METHODS[:info]) + end + + alias_method :info!, :info + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/client.rb b/ruby-opennebula-4.4/lib/opennebula/client.rb new file mode 100644 index 0000000..2e7d658 --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/client.rb @@ -0,0 +1,119 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +require 'xmlrpc/client' + +module OpenNebula + if OpenNebula::NOKOGIRI + class NokogiriStreamParser < XMLRPC::XMLParser::AbstractStreamParser + def initialize + @parser_class = NokogiriParser + end + + class NokogiriParser < Nokogiri::XML::SAX::Document + include XMLRPC::XMLParser::StreamParserMixin + + alias :cdata_block :character + alias :characters :character + alias :end_element :endElement + alias :start_element :startElement + + def parse(str) + parser = Nokogiri::XML::SAX::Parser.new(self) + parser.parse(str) + end + end + end + end + + # The client class, represents the connection with the core and handles the + # xml-rpc calls. + class Client + attr_accessor :one_auth + + begin + require 'xmlparser' + XMLPARSER=true + rescue LoadError + XMLPARSER=false + end + + # Creates a new client object that will be used to call OpenNebula + # functions. + # + # @param [String, nil] secret user credentials ("user:password") or + # nil to get the credentials from user auth file + # @param [String, nil] endpoint OpenNebula server endpoint + # (http://host:2633/RPC2) or nil to get it form the environment + # variable ONE_XMLRPC or use the default endpoint + # @param [Hash] options + # @option params [Integer] :timeout connection timeout in seconds, + # defaults to 30 + # + # @return [OpenNebula::Client] + def initialize(secret=nil, endpoint=nil, options={}) + if secret + @one_auth = secret + elsif ENV["ONE_AUTH"] and !ENV["ONE_AUTH"].empty? and + File.file?(ENV["ONE_AUTH"]) + @one_auth = File.read(ENV["ONE_AUTH"]) + elsif File.file?(ENV["HOME"]+"/.one/one_auth") + @one_auth = File.read(ENV["HOME"]+"/.one/one_auth") + else + raise "ONE_AUTH file not present" + end + + @one_auth.rstrip! + + if endpoint + @one_endpoint = endpoint + elsif ENV["ONE_XMLRPC"] + @one_endpoint = ENV["ONE_XMLRPC"] + else + @one_endpoint = "http://localhost:2633/RPC2" + end + + timeout=nil + timeout=options[:timeout] if options[:timeout] + + @server = XMLRPC::Client.new2(@one_endpoint, nil, timeout) + + if OpenNebula::NOKOGIRI + @server.set_parser(NokogiriStreamParser.new) + elsif XMLPARSER + @server.set_parser(XMLRPC::XMLParser::XMLStreamParser.new) + end + end + + def call(action, *args) + begin + response = @server.call_async("one."+action, @one_auth, *args) + + if response[0] == false + Error.new(response[1], response[2]) + else + response[1] #response[1..-1] + end + rescue Exception => e + Error.new(e.message) + end + end + + def get_version() + call("system.version") + end + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/cluster.rb b/ruby-opennebula-4.4/lib/opennebula/cluster.rb new file mode 100644 index 0000000..25c9d75 --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/cluster.rb @@ -0,0 +1,262 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +require 'opennebula/pool_element' + +module OpenNebula + class Cluster < PoolElement + ####################################################################### + # Constants and Class Methods + ####################################################################### + + CLUSTER_METHODS = { + :info => "cluster.info", + :allocate => "cluster.allocate", + :delete => "cluster.delete", + :addhost => "cluster.addhost", + :delhost => "cluster.delhost", + :adddatastore => "cluster.adddatastore", + :deldatastore => "cluster.deldatastore", + :addvnet => "cluster.addvnet", + :delvnet => "cluster.delvnet", + :update => "cluster.update", + :rename => "cluster.rename" + } + + # Creates a Cluster description with just its identifier + # this method should be used to create plain Cluster objects. + # +id+ the id of the host + # + # Example: + # cluster = Cluster.new(Cluster.build_xml(3),rpc_client) + # + def Cluster.build_xml(pe_id=nil) + if pe_id + cluster_xml = "#{pe_id}" + else + cluster_xml = "" + end + + XMLElement.build_xml(cluster_xml,'CLUSTER') + end + + # Class constructor + def initialize(xml, client) + super(xml,client) + end + + ####################################################################### + # XML-RPC Methods for the Cluster Object + ####################################################################### + + # Retrieves the information of the given Cluster. + def info() + super(CLUSTER_METHODS[:info], 'CLUSTER') + end + + alias_method :info!, :info + + # Allocates a new Cluster in OpenNebula + # + # +clustername+ A string containing the name of the Cluster. + def allocate(clustername) + super(CLUSTER_METHODS[:allocate], clustername) + end + + # Deletes the Cluster + def delete() + super(CLUSTER_METHODS[:delete]) + end + + # Adds a Host to this Cluster + # @param hid [Integer] Host ID + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def addhost(hid) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(CLUSTER_METHODS[:addhost], @pe_id, hid) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + + # Deletes a Host from this Cluster + # @param hid [Integer] Host ID + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def delhost(hid) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(CLUSTER_METHODS[:delhost], @pe_id, hid) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + + # Adds a Datastore to this Cluster + # @param ds_id [Integer] Datastore ID + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def adddatastore(ds_id) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(CLUSTER_METHODS[:adddatastore], @pe_id, ds_id) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + + # Deletes a Datastore from this Cluster + # @param ds_id [Integer] Datastore ID + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def deldatastore(ds_id) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(CLUSTER_METHODS[:deldatastore], @pe_id, ds_id) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + + # Adds a VNet to this Cluster + # @param vnet_id [Integer] VNet ID + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def addvnet(vnet_id) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(CLUSTER_METHODS[:addvnet], @pe_id, vnet_id) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + + # Deletes a VNet from this Cluster + # @param vnet_id [Integer] VNet ID + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def delvnet(vnet_id) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(CLUSTER_METHODS[:delvnet], @pe_id, vnet_id) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + + # Replaces the template contents + # + # @param new_template [String] New template contents + # @param append [true, false] True to append new attributes instead of + # replace the whole template + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def update(new_template, append=false) + super(CLUSTER_METHODS[:update], new_template, append ? 1 : 0) + end + + # Renames this Cluster + # + # @param name [String] New name for the Cluster. + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def rename(name) + return call(CLUSTER_METHODS[:rename], @pe_id, name) + end + + # --------------------------------------------------------------------- + # Helpers to get information + # --------------------------------------------------------------------- + + # Returns whether or not the host with 'id' is part of this cluster + # @param id [Integer|Array] host ID + # @return [Boolean] true if found + def contains_host?(id) + contains_resource?('HOSTS/ID', id) + end + + # Returns an array with the numeric host ids + # @return [Array] + def host_ids + array = Array.new + + self.each("HOSTS/ID") do |id| + array << id.text.to_i + end + + return array + end + + # Returns whether or not the datastore with 'id' is part of this cluster + # @param id [Integer|Array] datastore ID + # @return [Boolean] true if found + def contains_datastore?(id) + contains_resource?('DATASTORES/ID', id) + end + + # Returns an array with the numeric datastore ids + # @return [Array] + def datastore_ids + array = Array.new + + self.each("DATASTORES/ID") do |id| + array << id.text.to_i + end + + return array + end + + # Returns whether or not the vnet with 'id' is part of this cluster + # @param id [Integer|Arrray] vnet ID + # @return [Boolean] true if found + def contains_vnet?(id) + contains_resource?('VNETS/ID', id) + end + + # Returns an array with the numeric vnet ids + # @return [Array] + def vnet_ids + array = Array.new + + self.each("VNETS/ID") do |id| + array << id.text.to_i + end + + return array + end + + private + + def contains_resource?(xpath, id) + id_array = retrieve_elements(xpath) + + return false if id_array.nil? + + id = [id] if id.class != Array + + id.each { |i| + return false if !id_array.include?(i.to_s) + } + + return true + end + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/cluster_pool.rb b/ruby-opennebula-4.4/lib/opennebula/cluster_pool.rb new file mode 100644 index 0000000..6cf024d --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/cluster_pool.rb @@ -0,0 +1,58 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +require 'opennebula/pool' + +module OpenNebula + class ClusterPool < Pool + ####################################################################### + # Constants and Class attribute accessors + ####################################################################### + + NONE_CLUSTER_ID = -1 + DEFAULT_CLUSTER_ID = 0 + + CLUSTER_POOL_METHODS = { + :info => "clusterpool.info" + } + + ####################################################################### + # Class constructor & Pool Methods + ####################################################################### + + # +client+ a Client object that represents a XML-RPC connection + def initialize(client) + super('CLUSTER_POOL','CLUSTER',client) + end + + # Factory method to create Cluster objects + def factory(element_xml) + OpenNebula::Cluster.new(element_xml,@client) + end + + ####################################################################### + # XML-RPC Methods for the Cluster Object + ####################################################################### + + # Retrieves all the Clusters in the pool. + def info() + super(CLUSTER_POOL_METHODS[:info]) + end + + alias_method :info!, :info + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/datastore.rb b/ruby-opennebula-4.4/lib/opennebula/datastore.rb new file mode 100644 index 0000000..fe702ab --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/datastore.rb @@ -0,0 +1,184 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +require 'opennebula/pool_element' + +module OpenNebula + class Datastore < PoolElement + ####################################################################### + # Constants and Class Methods + ####################################################################### + + DATASTORE_METHODS = { + :info => "datastore.info", + :allocate => "datastore.allocate", + :delete => "datastore.delete", + :update => "datastore.update", + :chown => "datastore.chown", + :chmod => "datastore.chmod", + :rename => "datastore.rename" + } + + DATASTORE_TYPES=%w{IMAGE SYSTEM FILE} + + SHORT_DATASTORE_TYPES = { + "IMAGE" => "img", + "SYSTEM"=> "sys", + "FILE" => "fil" + } + + # Creates a Datastore description with just its identifier + # this method should be used to create plain Datastore objects. + # +id+ the id of the user + # + # Example: + # datastore = Datastore.new(Datastore.build_xml(3),rpc_client) + # + def Datastore.build_xml(pe_id=nil) + if pe_id + datastore_xml = "#{pe_id}" + else + datastore_xml = "" + end + + XMLElement.build_xml(datastore_xml,'DATASTORE') + end + + # Class constructor + def initialize(xml, client) + super(xml,client) + end + + ####################################################################### + # XML-RPC Methods for the Datastore Object + ####################################################################### + # Returns the datastore type + def type + self['TYPE'].to_i + end + + # Returns the datastore type (string value) + def type_str + DATASTORE_TYPES[type] + end + + # Returns the datastore type (string value) + def short_type_str + SHORT_DATASTORE_TYPES[type_str] + end + + # Retrieves the information of the given Datastore. + def info() + super(DATASTORE_METHODS[:info], 'DATASTORE') + end + + alias_method :info!, :info + + # Allocates a new Datastore in OpenNebula + # + # @param description [String] The template of the Datastore. + # @param cluster_id [Integer] Id of the cluster + # + # @return [Integer, OpenNebula::Error] the new ID in case of + # success, error otherwise + def allocate(description, cluster_id=ClusterPool::NONE_CLUSTER_ID) + super(DATASTORE_METHODS[:allocate], description, cluster_id) + end + + # Deletes the Datastore + def delete() + super(DATASTORE_METHODS[:delete]) + end + + # Replaces the template contents + # + # @param new_template [String] New template contents + # @param append [true, false] True to append new attributes instead of + # replace the whole template + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def update(new_template, append=false) + super(DATASTORE_METHODS[:update], new_template, append ? 1 : 0) + end + + # Changes the owner/group + # + # @param uid [Integer] the new owner id. Set to -1 to leave the current one + # @param gid [Integer] the new group id. Set to -1 to leave the current one + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chown(uid, gid) + super(DATASTORE_METHODS[:chown], uid, gid) + end + + # Changes the datastore permissions. + # + # @param octet [String] Permissions octed , e.g. 640 + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chmod_octet(octet) + super(DATASTORE_METHODS[:chmod], octet) + end + + # Changes the datastore permissions. + # Each [Integer] argument must be 1 to allow, 0 deny, -1 do not change + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chmod(owner_u, owner_m, owner_a, group_u, group_m, group_a, other_u, + other_m, other_a) + super(DATASTORE_METHODS[:chmod], owner_u, owner_m, owner_a, group_u, + group_m, group_a, other_u, other_m, other_a) + end + + # Renames this datastore + # + # @param name [String] New name for the datastore + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def rename(name) + return call(DATASTORE_METHODS[:rename], @pe_id, name) + end + + # --------------------------------------------------------------------- + # Helpers to get information + # --------------------------------------------------------------------- + + # Returns whether or not the image with id 'id' is part of this datastore + def contains(id) + #This doesn't work in ruby 1.8.5 + #return self["DATASTORE/ID[.=#{uid}]"] != nil + + id_array = retrieve_elements('IMAGES/ID') + return id_array != nil && id_array.include?(uid.to_s) + end + + # Returns an array with the numeric image ids + def img_ids + array = Array.new + + self.each("IMAGES/ID") do |id| + array << id.text.to_i + end + + return array + end + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/datastore_pool.rb b/ruby-opennebula-4.4/lib/opennebula/datastore_pool.rb new file mode 100644 index 0000000..25bfef8 --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/datastore_pool.rb @@ -0,0 +1,55 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +require 'opennebula/pool' + +module OpenNebula + class DatastorePool < Pool + ####################################################################### + # Constants and Class attribute accessors + ####################################################################### + + DATASTORE_POOL_METHODS = { + :info => "datastorepool.info" + } + + ####################################################################### + # Class constructor & Pool Methods + ####################################################################### + + # +client+ a Client object that represents a XML-RPC connection + def initialize(client) + super('DATASTORE_POOL','DATASTORE',client) + end + + # Factory method to create User objects + def factory(element_xml) + OpenNebula::Group.new(element_xml,@client) + end + + ####################################################################### + # XML-RPC Methods for the User Object + ####################################################################### + + # Retrieves all the Groups in the pool. + def info() + super(DATASTORE_POOL_METHODS[:info]) + end + + alias_method :info!, :info + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/document.rb b/ruby-opennebula-4.4/lib/opennebula/document.rb new file mode 100644 index 0000000..2a519ef --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/document.rb @@ -0,0 +1,263 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +require 'opennebula/pool_element' + +module OpenNebula + + # All subclasses must define the DOCUMENT_TYPE constant. + # + # @example + # require 'opennebula/document' + # + # module OpenNebula + # class CustomObject < Document + # + # DOCUMENT_TYPE = 400 + # + # end + # end + class Document < PoolElement + + ####################################################################### + # Constants and Class Methods + ####################################################################### + + DOCUMENT_METHODS = { + :allocate => "document.allocate", + :delete => "document.delete", + :info => "document.info", + :update => "document.update", + :chown => "document.chown", + :chmod => "document.chmod", + :clone => "document.clone", + :rename => "document.rename" + } + + # Creates a Document Object description with just its identifier + # this method should be used to create plain Document objects. + # @param [Integer] pe_id the id of the object + # + # @return [Nokogiri::XML::Node, REXML::Element] the empty xml + def Document.build_xml(pe_id=nil) + if pe_id + obj_xml = "#{pe_id}" + else + obj_xml = "" + end + + XMLElement.build_xml(obj_xml,'DOCUMENT') + end + + # Class constructor + # + # @param [Nokogiri::XML::Node, REXML::Element] xml string + # created by the build_xml() method + # @param [OpenNebula::Client] client the xml-rpc client + # + # @return [Document] the new object + # + # @example + # doc = Document.new(Document.build_xml(3),rpc_client) + def initialize(xml, client) + super(xml,client) + end + + ####################################################################### + # XML-RPC Methods for the Document Object + ####################################################################### + + # Retrieves the information of the given Document. + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def info() + rc = super(DOCUMENT_METHODS[:info], 'DOCUMENT') + + if !OpenNebula.is_error?(rc) && self['TYPE'].to_i != document_type + return OpenNebula::Error.new("[DocumentInfo] Error getting document [#{@pe_id}].") + end + + return rc + end + + alias_method :info!, :info + + # Allocates a new Document in OpenNebula + # + # @param description [String] The contents of the Document. + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def allocate(description) + super(DOCUMENT_METHODS[:allocate], description, document_type) + end + + # Deletes the Document + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def delete() + rc = check_type() + return rc if OpenNebula.is_error?(rc) + + return call(DOCUMENT_METHODS[:delete], @pe_id) + end + + # Replaces the template contents + # + # @param [String] new_template new template contents + # @param append [true, false] True to append new attributes instead of + # replace the whole template + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def update(new_template, append=false) + rc = check_type() + return rc if OpenNebula.is_error?(rc) + + super(DOCUMENT_METHODS[:update], new_template, append ? 1 : 0) + end + + # Changes the owner/group + # + # @param [Integer] uid the new owner id. Set to -1 to leave the current one + # @param [Integer] gid the new group id. Set to -1 to leave the current one + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chown(uid, gid) + rc = check_type() + return rc if OpenNebula.is_error?(rc) + + super(DOCUMENT_METHODS[:chown], uid, gid) + end + + # Changes the Document permissions. + # + # @param octet [String] Permissions octed , e.g. 640 + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chmod_octet(octet) + rc = check_type() + return rc if OpenNebula.is_error?(rc) + + super(DOCUMENT_METHODS[:chmod], octet) + end + + # Changes the Document permissions. + # Each [Integer] argument must be 1 to allow, 0 deny, -1 do not change + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chmod(owner_u, owner_m, owner_a, group_u, group_m, group_a, other_u, + other_m, other_a) + rc = check_type() + return rc if OpenNebula.is_error?(rc) + + super(DOCUMENT_METHODS[:chmod], owner_u, owner_m, owner_a, group_u, + group_m, group_a, other_u, other_m, other_a) + end + + # Clones this Document into a new one + # + # @param name [String] Name for the new Document. + # + # @return [Integer, OpenNebula::Error] The new Document ID in case + # of success, Error otherwise + def clone(name) + rc = check_type() + return rc if OpenNebula.is_error?(rc) + + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(DOCUMENT_METHODS[:clone], @pe_id, name) + + return rc + end + + # Renames this Document + # + # @param name [String] New name for the Document. + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def rename(name) + return call(DOCUMENT_METHODS[:rename], @pe_id, name) + end + + ####################################################################### + # Helpers to get Document information + ####################################################################### + + # Returns the group identifier + # @return [Integer] the element's group ID + def gid + self['GID'].to_i + end + + # Returns the owner user ID + # @return [Integer] the element's owner user ID + def owner_id + self['UID'].to_i + end + + # Returns true if the GROUP_U permission bit is set + # @return [true, false] true if the GROUP_U permission bit is set + def public? + if self['PERMISSIONS/GROUP_U'] == "1" || self['PERMISSIONS/OTHER_U'] == "1" + true + else + false + end + end + + def document_type + self.class::DOCUMENT_TYPE + end + + private + + def set_publish(published) + group_u = published ? 1 : 0 + + chmod(-1, -1, -1, group_u, -1, -1, -1, -1, -1) + end + + def check_type() + type = self['TYPE'] + + if type.nil? && @pe_id + rc = @client.call(DOCUMENT_METHODS[:info], @pe_id) + + return rc if OpenNebula.is_error?(rc) + + xmldoc = XMLElement.new + xmldoc.initialize_xml(rc, 'DOCUMENT') + + type = xmldoc['TYPE'] + end + + if !type.nil? && type.to_i != document_type + return OpenNebula::Error.new( + "[DocumentInfo] Error getting document [#{@pe_id}].") + end + + return nil + end + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/document_json.rb b/ruby-opennebula-4.4/lib/opennebula/document_json.rb new file mode 100644 index 0000000..fdbb680 --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/document_json.rb @@ -0,0 +1,134 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +require 'json' + +module OpenNebula + class DocumentJSON < Document + + TEMPLATE_TAG = "BODY" + + # Allocate a new Document containing the json inside the TEMPLATE + # + # @param [String] template_json json to be inserted in the TEMPLATE + # of the new resource + # @param [String, nil] name name of the object, this value will be + # processed by the OpenNebula core + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + # + def allocate(template_json, name=nil) + text = build_template_xml(template_json, name) + + super(text) + end + + # Retrieves the information of the Service and all its Nodes. + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + # + def info + rc = super + if OpenNebula.is_error?(rc) + return rc + end + + load_body + end + + alias_method :info!, :info + + # Updates the current state of this Service in the OpenNebula DB + # + # @params [String, nil] template_json string to be inserted in the + # template. If nil @body will be used instead + # @param append [true, false] True to append new attributes instead of + # replace the whole template + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + # + def update(template_json=nil, append=false) + template_json ||= @body.to_json + + text = build_template_xml(template_json) + + super(text, append) + end + + # Generates a json representing the object + # + # @param [true, false] pretty_generate + # @return [String] json representing the object + # + def to_json(pretty_generate=true) + hash = self.to_hash + + body = hash['DOCUMENT']['TEMPLATE']["#{TEMPLATE_TAG}"] + if body + body_hash = JSON.parse(body) + hash['DOCUMENT']['TEMPLATE']["#{TEMPLATE_TAG}"] = body_hash + end + + if pretty_generate + JSON.pretty_generate hash + else + hash.to_json + end + end + + + # Fill the @body hash with the values of the template + def load_body + body_str = self["TEMPLATE/#{TEMPLATE_TAG}"] + + if body_str + begin + @body = JSON.parse(body_str) + rescue JSON::JSONError + return OpenNebula::Error.new($!) + end + end + + return nil + end + + private + + # Build an xml string incluiding the provided json + # + # @param [String] template_json The template to be inserted + # @param [String, nil] name The string to be inserted as name + # @return [String] The xml containing the json + # + def build_template_xml(template_json, name=nil) + template_json ||= "" + + text = "" + + text + end + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/document_pool.rb b/ruby-opennebula-4.4/lib/opennebula/document_pool.rb new file mode 100644 index 0000000..dfd1c96 --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/document_pool.rb @@ -0,0 +1,102 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +require 'opennebula/pool' + +module OpenNebula + + # All subclasses must define the DOCUMENT_TYPE constant + # and the factory method. + # + # @example + # require 'opennebuña/document_pool' + # + # module OpenNebula + # class CustomObjectPool < DocumentPool + # + # DOCUMENT_TYPE = 400 + # + # def factory(element_xml) + # OpenNebula::CustomObject.new(element_xml, @client) + # end + # end + # end + class DocumentPool < Pool + + ####################################################################### + # Constants and Class attribute accessors + ####################################################################### + + DOCUMENT_POOL_METHODS = { + :info => "documentpool.info" + } + + ####################################################################### + # Class constructor & Pool Methods + ####################################################################### + + # Class constructor + # + # @param [OpenNebula::Client] client the xml-rpc client + # @param [Integer] user_id the filter flag, see + # http://opennebula.org/documentation:rel3.6:api + # + # @return [DocumentPool] the new object + def initialize(client, user_id=-1) + super('DOCUMENT_POOL','DOCUMENT',client) + + @user_id = user_id + end + + ####################################################################### + # XML-RPC Methods for the Document Object + ####################################################################### + + # Retrieves all or part of the Documents in the pool. + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def info(*args) + case args.size + when 0 + info_filter(DOCUMENT_POOL_METHODS[:info],@user_id,-1,-1, document_type) + when 3 + info_filter(DOCUMENT_POOL_METHODS[:info],args[0],args[1],args[2], document_type) + end + end + + def info_all() + return super(DOCUMENT_POOL_METHODS[:info], document_type) + end + + def info_mine() + return super(DOCUMENT_POOL_METHODS[:info], document_type) + end + + def info_group() + return super(DOCUMENT_POOL_METHODS[:info], document_type) + end + + alias_method :info!, :info + alias_method :info_all!, :info_all + alias_method :info_mine!, :info_mine + alias_method :info_group!, :info_group + + def document_type + self.class::DOCUMENT_TYPE + end + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/document_pool_json.rb b/ruby-opennebula-4.4/lib/opennebula/document_pool_json.rb new file mode 100644 index 0000000..8097dca --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/document_pool_json.rb @@ -0,0 +1,58 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +module OpenNebula + class DocumentPoolJSON < DocumentPool + + TEMPLATE_TAG = "BODY" + + def factory(element_xml) + doc = OpenNebula::DocumentJSON.new(element_xml, @client) + doc.load_body + doc + end + + # Generates a json representing the object + # + # @param [true, false] pretty_generate + # @return [String] json representing the object + # + def to_json(pretty_generate=true) + hash = self.to_hash + + if hash['DOCUMENT_POOL'] && hash['DOCUMENT_POOL']['DOCUMENT'] + if !hash['DOCUMENT_POOL']['DOCUMENT'].instance_of?(Array) + array = [hash['DOCUMENT_POOL']['DOCUMENT']] + hash['DOCUMENT_POOL']['DOCUMENT'] = array.compact + end + + hash['DOCUMENT_POOL']['DOCUMENT'].each { |doc| + body = doc['TEMPLATE']["#{TEMPLATE_TAG}"] + if body + b_hash = JSON.parse(body) + doc['TEMPLATE']["#{TEMPLATE_TAG}"] = b_hash + end + } + end + + if pretty_generate + JSON.pretty_generate hash + else + hash.to_json + end + end + end +end \ No newline at end of file diff --git a/ruby-opennebula-4.4/lib/opennebula/error.rb b/ruby-opennebula-4.4/lib/opennebula/error.rb new file mode 100644 index 0000000..e3081b0 --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/error.rb @@ -0,0 +1,52 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +module OpenNebula + # The Error Class represents a generic error in the OpenNebula + # library. It contains a readable representation of the error. + # Any function in the OpenNebula module will return an Error + # object in case of error. + class Error + ESUCCESS = 0x0000 + EAUTHENTICATION = 0x0100 + EAUTHORIZATION = 0x0200 + ENO_EXISTS = 0x0400 + EACTION = 0x0800 + EXML_RPC_API = 0x1000 + EINTERNAL = 0x2000 + ENOTDEFINED = 0x1111 + + attr_reader :message, :errno + + # +message+ Description of the error + # +errno+ OpenNebula code error + def initialize(message=nil, errno=0x1111) + @message = message + @errno = errno + end + + def to_str() + @message + end + end + + # Returns true if the object returned by a method of the OpenNebula + # library is an Error + def self.is_error?(value) + value.class==OpenNebula::Error + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/group.rb b/ruby-opennebula-4.4/lib/opennebula/group.rb new file mode 100644 index 0000000..6a8fc15 --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/group.rb @@ -0,0 +1,163 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +require 'opennebula/pool_element' + +module OpenNebula + class Group < PoolElement + ####################################################################### + # Constants and Class Methods + ####################################################################### + + GROUP_METHODS = { + :info => "group.info", + :allocate => "group.allocate", + :delete => "group.delete", + :quota => "group.quota" + } + + # Flag for requesting connected user's group info + SELF = -1 + + #Default location for group ACL's + if ENV['ONE_LOCATION'] + GROUP_DEFAULT = ENV['ONE_LOCATION'] + "/etc/group.default" + else + GROUP_DEFAULT = "/etc/one/group.default" + end + + # Creates a Group description with just its identifier + # this method should be used to create plain Group objects. + # +id+ the id of the user + # + # Example: + # group = Group.new(Group.build_xml(3),rpc_client) + # + def Group.build_xml(pe_id=nil) + if pe_id + group_xml = "#{pe_id}" + else + group_xml = "" + end + + XMLElement.build_xml(group_xml,'GROUP') + end + + # Class constructor + def initialize(xml, client) + super(xml,client) + end + + ####################################################################### + # Group utils + ####################################################################### + + # Creates ACLs for the group. The ACL rules are described in a file + def create_acls(filename = GROUP_DEFAULT) + if !File.readable?(filename) + return -1, "Cannot read deafult ACL file for group" + end + + msg = String.new + + File.open(filename).each_line{ |l| + next if l.match(/^#/) + + rule = "@#{@pe_id} #{l}" + parse = OpenNebula::Acl.parse_rule(rule) + + if OpenNebula.is_error?(parse) + return -1, "Error parsing rule #{rule}: #{parse.message}" + end + + xml = OpenNebula::Acl.build_xml + acl = OpenNebula::Acl.new(xml, @client) + + rc = acl.allocate(*parse) + + if OpenNebula.is_error?(rc) + return -1, "Error creating rule #{rule}: #{rc.message}" + else + msg << "ACL_ID: #{acl.id}\n" + end + } + + return 0, msg + end + + ####################################################################### + # XML-RPC Methods for the Group Object + ####################################################################### + + # Retrieves the information of the given Group. + def info() + super(GROUP_METHODS[:info], 'GROUP') + end + + alias_method :info!, :info + + # Allocates a new Group in OpenNebula + # + # +groupname+ A string containing the name of the Group. + def allocate(groupname) + super(GROUP_METHODS[:allocate], groupname) + end + + # Deletes the Group + def delete() + super(GROUP_METHODS[:delete]) + end + + # Sets the group quota limits + # @param quota [String] a template (XML or txt) with the new quota limits + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def set_quota(quota) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(GROUP_METHODS[:quota],@pe_id, quota) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + + # --------------------------------------------------------------------- + # Helpers to get information + # --------------------------------------------------------------------- + + # Returns whether or not the user with id 'uid' is part of this group + def contains(uid) + #This doesn't work in ruby 1.8.5 + #return self["USERS/ID[.=#{uid}]"] != nil + + id_array = retrieve_elements('USERS/ID') + return id_array != nil && id_array.include?(uid.to_s) + end + + # Returns an array with the numeric user ids + def user_ids + array = Array.new + + self.each("USERS/ID") do |id| + array << id.text.to_i + end + + return array + end + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/group_pool.rb b/ruby-opennebula-4.4/lib/opennebula/group_pool.rb new file mode 100644 index 0000000..bd5deee --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/group_pool.rb @@ -0,0 +1,56 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +require 'opennebula/pool' + +module OpenNebula + class GroupPool < Pool + ####################################################################### + # Constants and Class attribute accessors + ####################################################################### + + + GROUP_POOL_METHODS = { + :info => "grouppool.info" + } + + ####################################################################### + # Class constructor & Pool Methods + ####################################################################### + + # +client+ a Client object that represents a XML-RPC connection + def initialize(client) + super('GROUP_POOL','GROUP',client) + end + + # Factory method to create User objects + def factory(element_xml) + OpenNebula::Group.new(element_xml,@client) + end + + ####################################################################### + # XML-RPC Methods for the User Object + ####################################################################### + + # Retrieves all the Groups in the pool. + def info() + super(GROUP_POOL_METHODS[:info]) + end + + alias_method :info!, :info + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/host.rb b/ruby-opennebula-4.4/lib/opennebula/host.rb new file mode 100644 index 0000000..7476e01 --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/host.rb @@ -0,0 +1,219 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +require 'opennebula/pool_element' + +module OpenNebula + class Host < PoolElement + ####################################################################### + # Constants and Class Methods + ####################################################################### + + + HOST_METHODS = { + :info => "host.info", + :allocate => "host.allocate", + :delete => "host.delete", + :enable => "host.enable", + :update => "host.update", + :monitoring => "host.monitoring", + :rename => "host.rename" + } + + HOST_STATES=%w{INIT MONITORING_MONITORED MONITORED ERROR DISABLED MONITORING_ERROR MONITORING_INIT MONITORING_DISABLED} + + SHORT_HOST_STATES={ + "INIT" => "init", + "MONITORING_MONITORED" => "update", + "MONITORED" => "on", + "ERROR" => "err", + "DISABLED" => "off", + "MONITORING_ERROR" => "retry", + "MONITORING_INIT" => "init", + "MONITORING_DISABLED" => "off" + } + + # Creates a Host description with just its identifier + # this method should be used to create plain Host objects. + # +id+ the id of the host + # + # Example: + # host = Host.new(Host.build_xml(3),rpc_client) + # + def Host.build_xml(pe_id=nil) + if pe_id + host_xml = "#{pe_id}" + else + host_xml = "" + end + + XMLElement.build_xml(host_xml, 'HOST') + end + + # Class constructor + def initialize(xml, client) + super(xml,client) + + @client = client + @pe_id = self['ID'].to_i if self['ID'] + end + + ####################################################################### + # XML-RPC Methods for the Host + ####################################################################### + + # Retrieves the information of the given Host. + def info() + super(HOST_METHODS[:info], 'HOST') + end + + alias_method :info!, :info + + # Allocates a new Host in OpenNebula + # + # @param hostname [String] Name of the new Host. + # @param im [String] Name of the im_driver (information/monitoring) + # @param vmm [String] Name of the vmm_driver (hypervisor) + # @param vnm [String] Name of the vnm_driver (networking) + # @param cluster_id [String] Id of the cluster + # + # @return [Integer, OpenNebula::Error] the new ID in case of + # success, error otherwise + def allocate(hostname,im,vmm,vnm,cluster_id=ClusterPool::NONE_CLUSTER_ID) + super(HOST_METHODS[:allocate],hostname,im,vmm,vnm,cluster_id) + end + + # Deletes the Host + def delete() + super(HOST_METHODS[:delete]) + end + + # Enables the Host + def enable() + set_enabled(true) + end + + # Disables the Host + def disable() + set_enabled(false) + end + + def flush() + self.disable + + vm_pool = OpenNebula::VirtualMachinePool.new(@client, + VirtualMachinePool::INFO_ALL_VM) + + rc = vm_pool.info + if OpenNebula.is_error?(rc) + puts rc.message + exit -1 + end + + vm_pool.each do |vm| + hid = vm['HISTORY_RECORDS/HISTORY[last()]/HID'] + if hid == self['ID'] + vm.resched + end + end + end + + # Replaces the template contents + # + # @param new_template [String] New template contents + # @param append [true, false] True to append new attributes instead of + # replace the whole template + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def update(new_template, append=false) + super(HOST_METHODS[:update], new_template, append ? 1 : 0) + end + + # Retrieves this Host's monitoring data from OpenNebula + # + # @param [Array] xpath_expressions Elements to retrieve. + # + # @return [Hash>>, OpenNebula::Error] Hash with + # the requested xpath expressions, and an Array of 'timestamp, value'. + # + # @example + # host.monitoring( ['HOST_SHARE/FREE_CPU', 'HOST_SHARE/RUNNING_VMS'] ) + # + # { "HOST_SHARE/RUNNING_VMS" => + # [["1337266000", "1"], + # ["1337266044", "1"], + # ["1337266088", "3"]], + # "HOST_SHARE/FREE_CPU" => + # [["1337266000", "800"], + # ["1337266044", "800"], + # ["1337266088", "800"]] + # } + def monitoring(xpath_expressions) + return super(HOST_METHODS[:monitoring], 'HOST', + 'LAST_MON_TIME', xpath_expressions) + end + + # Retrieves this Host's monitoring data from OpenNebula, in XML + # + # @return [String] Monitoring data, in XML + def monitoring_xml() + return Error.new('ID not defined') if !@pe_id + + return @client.call(HOST_METHODS[:monitoring], @pe_id) + end + + # Renames this Host + # + # @param name [String] New name for the Host. + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def rename(name) + return call(HOST_METHODS[:rename], @pe_id, name) + end + + ####################################################################### + # Helpers to get Host information + ####################################################################### + + # Returns the state of the Host (numeric value) + def state + self['STATE'].to_i + end + + # Returns the state of the Host (string value) + def state_str + HOST_STATES[state] + end + + # Returns the state of the Host (string value) + def short_state_str + SHORT_HOST_STATES[state_str] + end + + private + def set_enabled(enabled) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(HOST_METHODS[:enable], @pe_id, enabled) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/host_pool.rb b/ruby-opennebula-4.4/lib/opennebula/host_pool.rb new file mode 100644 index 0000000..c36ff0b --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/host_pool.rb @@ -0,0 +1,93 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +require 'opennebula/pool' + +module OpenNebula + class HostPool < Pool + ####################################################################### + # Constants and Class attribute accessors + ####################################################################### + + + HOST_POOL_METHODS = { + :info => "hostpool.info", + :monitoring => "hostpool.monitoring" + } + + ####################################################################### + # Class constructor & Pool Methods + ####################################################################### + + + # +client+ a Client object that represents a XML-RPC connection + def initialize(client) + super('HOST_POOL','HOST',client) + end + + # Factory Method for the Host Pool + def factory(element_xml) + OpenNebula::Host.new(element_xml,@client) + end + + ####################################################################### + # XML-RPC Methods for the Host Pool + ####################################################################### + + # Retrieves all the Hosts in the pool. + def info() + super(HOST_POOL_METHODS[:info]) + end + + alias_method :info!, :info + + # Retrieves the monitoring data for all the Hosts in the pool + # + # @param [Array] xpath_expressions Elements to retrieve. + # + # @return [Hash>>>>, + # OpenNebula::Error] The first level hash uses the Host ID as keys, + # and as value a Hash with the requested xpath expressions, + # and an Array of 'timestamp, value'. + # + # @example + # host_pool.monitoring( + # ['HOST_SHARE/FREE_CPU', + # 'HOST_SHARE/RUNNING_VMS', + # 'TEMPLATE/CUSTOM_PROBE'] ) + # + # {"1"=> + # {"TEMPLATE/CUSTOM_PROBE"=>[], + # "HOST_SHARE/FREE_CPU"=>[["1337609673", "800"]], + # "HOST_SHARE/RUNNING_VMS"=>[["1337609673", "3"]]}, + # "0"=> + # {"TEMPLATE/CUSTOM_PROBE"=>[], + # "HOST_SHARE/FREE_CPU"=>[["1337609673", "800"]], + # "HOST_SHARE/RUNNING_VMS"=>[["1337609673", "3"]]}} + def monitoring(xpath_expressions) + return super(HOST_POOL_METHODS[:monitoring], + 'HOST', 'LAST_MON_TIME', xpath_expressions) + end + + # Retrieves the monitoring data for all the Hosts in the pool, in XML + # + # @return [String] VM monitoring data, in XML + def monitoring_xml() + return @client.call(HOST_POOL_METHODS[:monitoring]) + end + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/image.rb b/ruby-opennebula-4.4/lib/opennebula/image.rb new file mode 100644 index 0000000..05330ed --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/image.rb @@ -0,0 +1,301 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +require 'opennebula/pool_element' +require 'fileutils' + +module OpenNebula + class Image < PoolElement + ####################################################################### + # Constants and Class Methods + ####################################################################### + + + IMAGE_METHODS = { + :info => "image.info", + :allocate => "image.allocate", + :update => "image.update", + :enable => "image.enable", + :persistent => "image.persistent", + :delete => "image.delete", + :chown => "image.chown", + :chmod => "image.chmod", + :chtype => "image.chtype", + :clone => "image.clone", + :rename => "image.rename" + } + + IMAGE_STATES=%w{INIT READY USED DISABLED LOCKED ERROR CLONE DELETE USED_PERS} + + SHORT_IMAGE_STATES={ + "INIT" => "init", + "READY" => "rdy", + "USED" => "used", + "DISABLED" => "disa", + "LOCKED" => "lock", + "ERROR" => "err", + "CLONE" => "clon", + "DELETE" => "dele", + "USED_PERS" => "used" + } + + IMAGE_TYPES=%w{OS CDROM DATABLOCK KERNEL RAMDISK CONTEXT} + + SHORT_IMAGE_TYPES={ + "OS" => "OS", + "CDROM" => "CD", + "DATABLOCK" => "DB", + "KERNEL" => "KL", + "RAMDISK" => "RD", + "CONTEXT" => "CX" + } + + DISK_TYPES=%w{FILE CD_ROM BLOCK RBD} + + # Creates an Image description with just its identifier + # this method should be used to create plain Image objects. + # +id+ the id of the image + # + # Example: + # image = Image.new(Image.build_xml(3),rpc_client) + # + def Image.build_xml(pe_id=nil) + if pe_id + image_xml = "#{pe_id}" + else + image_xml = "" + end + + XMLElement.build_xml(image_xml,'IMAGE') + end + + # Class constructor + def initialize(xml, client) + super(xml,client) + + @client = client + end + + ####################################################################### + # XML-RPC Methods for the Image Object + ####################################################################### + + # Retrieves the information of the given Image. + def info() + super(IMAGE_METHODS[:info], 'IMAGE') + end + + alias_method :info!, :info + + # Allocates a new Image in OpenNebula + # + # @param description [String] A string containing the template of the Image. + # @param ds_id [Integer] the target datastore ID + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def allocate(description, ds_id) + super(IMAGE_METHODS[:allocate],description, ds_id) + end + + # Replaces the template contents + # + # @param new_template [String] New template contents + # @param append [true, false] True to append new attributes instead of + # replace the whole template + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def update(new_template=nil, append=false) + super(IMAGE_METHODS[:update], new_template, append ? 1 : 0) + end + + # Enables an Image + def enable + set_enabled(true) + end + + # Disables an Image + def disable + set_enabled(false) + end + + # Publishes the Image, to be used by other users + def publish + set_publish(true) + end + + # Unplubishes the Image + def unpublish + set_publish(false) + end + + # Makes the Image persistent + def persistent + set_persistent(true) + end + + # Makes the Image non persistent + def nonpersistent + set_persistent(false) + end + + # Deletes the Image + def delete() + super(IMAGE_METHODS[:delete]) + end + + # Changes the owner/group + # uid:: _Integer_ the new owner id. Set to -1 to leave the current one + # gid:: _Integer_ the new group id. Set to -1 to leave the current one + # [return] nil in case of success or an Error object + def chown(uid, gid) + super(IMAGE_METHODS[:chown], uid, gid) + end + + # Changes the Image permissions. + # + # @param octet [String] Permissions octed , e.g. 640 + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chmod_octet(octet) + super(IMAGE_METHODS[:chmod], octet) + end + + # Changes the Image permissions. + # Each [Integer] argument must be 1 to allow, 0 deny, -1 do not change + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chmod(owner_u, owner_m, owner_a, group_u, group_m, group_a, other_u, + other_m, other_a) + super(IMAGE_METHODS[:chmod], owner_u, owner_m, owner_a, group_u, + group_m, group_a, other_u, other_m, other_a) + end + + # Changes the Image type + # @param type [String] new Image type + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chtype(type) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(IMAGE_METHODS[:chtype], @pe_id, type) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + + # Clones this Image into a new one + # + # @param [String] name for the new Image. + # + # @return [Integer, OpenNebula::Error] The new Image ID in case + # of success, Error otherwise + def clone(name) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(IMAGE_METHODS[:clone], @pe_id, name) + + return rc + end + + # Renames this Image + # + # @param name [String] New name for the Image. + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def rename(name) + return call(IMAGE_METHODS[:rename], @pe_id, name) + end + + ####################################################################### + # Helpers to get Image information + ####################################################################### + + # Returns the state of the Image (numeric value) + def state + self['STATE'].to_i + end + + # Returns the state of the Image (string value) + def state_str + IMAGE_STATES[state] + end + + # Returns the state of the Image (string value) + def short_state_str + SHORT_IMAGE_STATES[state_str] + end + + # Returns the type of the Image (numeric value) + def type + self['TYPE'].to_i + end + + # Returns the type of the Image (string value) + def type_str + IMAGE_TYPES[type] + end + + # Returns the state of the Image (string value) + def short_type_str + SHORT_IMAGE_TYPES[type_str] + end + + # Returns the group identifier + # [return] _Integer_ the element's group ID + def gid + self['GID'].to_i + end + + def public? + if self['PERMISSIONS/GROUP_U'] == "1" || self['PERMISSIONS/OTHER_U'] == "1" + true + else + false + end + end + + private + + def set_enabled(enabled) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(IMAGE_METHODS[:enable], @pe_id, enabled) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + + def set_publish(published) + group_u = published ? 1 : 0 + + chmod(-1, -1, -1, group_u, -1, -1, -1, -1, -1) + end + + def set_persistent(persistence) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(IMAGE_METHODS[:persistent], @pe_id, persistence) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/image_pool.rb b/ruby-opennebula-4.4/lib/opennebula/image_pool.rb new file mode 100644 index 0000000..545742d --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/image_pool.rb @@ -0,0 +1,79 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +require 'opennebula/pool' + +module OpenNebula + class ImagePool < Pool + ####################################################################### + # Constants and Class attribute accessors + ####################################################################### + + + IMAGE_POOL_METHODS = { + :info => "imagepool.info" + } + + ####################################################################### + # Class constructor & Pool Methods + ####################################################################### + + # +client+ a Client object that represents a XML-RPC connection + # +user_id+ is to refer to a Pool with Images from that user + def initialize(client, user_id=-1) + super('IMAGE_POOL','IMAGE',client) + + @user_id = user_id + end + + # Default Factory Method for the Pools + def factory(element_xml) + OpenNebula::Image.new(element_xml,@client) + end + + ####################################################################### + # XML-RPC Methods for the Image Object + ####################################################################### + + # Retrieves all or part of the VirtualMachines in the pool. + def info(*args) + case args.size + when 0 + info_filter(IMAGE_POOL_METHODS[:info],@user_id,-1,-1) + when 3 + info_filter(IMAGE_POOL_METHODS[:info],args[0],args[1],args[2]) + end + end + + def info_all() + return super(IMAGE_POOL_METHODS[:info]) + end + + def info_mine() + return super(IMAGE_POOL_METHODS[:info]) + end + + def info_group() + return super(IMAGE_POOL_METHODS[:info]) + end + + alias_method :info!, :info + alias_method :info_all!, :info_all + alias_method :info_mine!, :info_mine + alias_method :info_group!, :info_group + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/ldap_auth.rb b/ruby-opennebula-4.4/lib/opennebula/ldap_auth.rb new file mode 100644 index 0000000..b6d965f --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/ldap_auth.rb @@ -0,0 +1,103 @@ +# ---------------------------------------------------------------------------- # +# Copyright 2010-2013, C12G Labs S.L # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +# ---------------------------------------------------------------------------- # + +require 'rubygems' +require 'net/ldap' + +module OpenNebula; end + +class OpenNebula::LdapAuth + def initialize(options) + @options={ + :host => 'localhost', + :port => 389, + :user => nil, + :password => nil, + :base => nil, + :auth_method => :simple, + :user_field => 'cn', + :user_group_field => 'dn', + :group_field => 'member' + }.merge(options) + + ops={} + + if @options[:user] + ops[:auth] = { + :method => @options[:auth_method], + :username => @options[:user], + :password => @options[:password] + } + end + + ops[:host]=@options[:host] if @options[:host] + ops[:port]=@options[:port].to_i if @options[:port] + ops[:encryption]=@options[:encryption] if @options[:encryption] + + @ldap=Net::LDAP.new(ops) + end + + def find_user(name) + begin + result=@ldap.search( + :base => @options[:base], + :filter => "#{@options[:user_field]}=#{name}") + + if result && result.first + [result.first.dn, result.first[@options[:user_group_field]]] + else + result=@ldap.search(:base => name) + + if result && result.first + [name, result.first[@options[:user_group_field]]] + else + [nil, nil] + end + end + rescue + [nil, nil] + end + end + + def is_in_group?(user, group) + result=@ldap.search( + :base => group, + :filter => "(#{@options[:group_field]}=#{user.first})") + + if result && result.first + true + else + false + end + end + + def authenticate(user, password) + ldap=@ldap.clone + + auth={ + :method => @options[:auth_method], + :username => user, + :password => password + } + + if ldap.bind(auth) + true + else + false + end + end +end + diff --git a/ruby-opennebula-4.4/lib/opennebula/ldap_auth_spec.rb b/ruby-opennebula-4.4/lib/opennebula/ldap_auth_spec.rb new file mode 100644 index 0000000..5fb8b5c --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/ldap_auth_spec.rb @@ -0,0 +1,74 @@ +# ---------------------------------------------------------------------------- # +# Copyright 2010-2013, C12G Labs S.L # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +# ---------------------------------------------------------------------------- # + +$: << ".." + +require 'ldap_auth' + +options={ + :host => 'ubuntu-test', + :base => 'dc=localdomain' +} + +describe LdapAuth do + before(:all) do + @ldap=LdapAuth.new(options) + end + + it 'should find user dn' do + name,group_name=@ldap.find_user('user01') + name.should=='cn=user01,dc=localdomain' + group_name.should=='cn=user01,dc=localdomain' + + name,group_name=@ldap.find_user('user02') + name.should=='cn=user02,dc=localdomain' + group_name.should=='cn=user02,dc=localdomain' + + name,group_name=@ldap.find_user('user03') + name.should==nil + group_name.should==nil + + name=@ldap.find_user('cn=user01,dc=localdomain') + name.should=='cn=user01,dc=localdomain' + group_name.should=='cn=user01,dc=localdomain' + end + + it 'should tell if a user is in a group' do + group='cn=cloud,ou=groups,dc=localdomain' + + result=@ldap.is_in_group?('cn=user01,dc=localdomain', group) + result.should==true + + result=@ldap.is_in_group?('cn=user02,dc=localdomain', group) + result.should==false + end + + it 'should authenticate user' do + result=@ldap.authenticate('cn=user01,dc=localdomain', 'password01') + result.should==true + + result=@ldap.authenticate('cn=user02,dc=localdomain', 'password02') + result.should==true + + result=@ldap.authenticate('cn=user01,dc=localdomain', 'password02') + result.should==false + + result=@ldap.authenticate('user01,dc=localdomain', 'password01') + result.should==false + end + +end + diff --git a/ruby-opennebula-4.4/lib/opennebula/oneflow_client.rb b/ruby-opennebula-4.4/lib/opennebula/oneflow_client.rb new file mode 100644 index 0000000..d0a8d7f --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/oneflow_client.rb @@ -0,0 +1,387 @@ +# -------------------------------------------------------------------------- # +# Copyright 2010-2013, C12G Labs S.L. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +require 'uri' +require 'cloud/CloudClient' + +include CloudCLI + +module Role + # Actions that can be performed on the VMs of a given Role + SCHEDULE_ACTIONS = [ + 'shutdown', + 'shutdown-hard', + 'undeploy', + 'undeploy-hard', + 'hold', + 'release', + 'stop', + 'suspend', + 'resume', + 'boot', + 'delete', + 'delete-recreate', + 'reboot', + 'reboot-hard', + 'poweroff', + 'poweroff-hard', + 'snapshot-create' + ] + + STATE = { + 'PENDING' => 0, + 'DEPLOYING' => 1, + 'RUNNING' => 2, + 'UNDEPLOYING' => 3, + 'WARNING' => 4, + 'DONE' => 5, + 'FAILED_UNDEPLOYING' => 6, + 'FAILED_DEPLOYING' => 7, + 'SCALING' => 8, + 'FAILED_SCALING' => 9, + 'COOLDOWN' => 10 + } + + STATE_STR = [ + 'PENDING', + 'DEPLOYING', + 'RUNNING', + 'UNDEPLOYING', + 'WARNING', + 'DONE', + 'FAILED_UNDEPLOYING', + 'FAILED_DEPLOYING', + 'SCALING', + 'FAILED_SCALING', + 'COOLDOWN' + ] + + # Returns the string representation of the role state + # @param [String] state String number representing the state + # @return the state string + def self.state_str(state_number) + return STATE_STR[state_number.to_i] + end +end + +module Service + + STATE = { + 'PENDING' => 0, + 'DEPLOYING' => 1, + 'RUNNING' => 2, + 'UNDEPLOYING' => 3, + 'WARNING' => 4, + 'DONE' => 5, + 'FAILED_UNDEPLOYING' => 6, + 'FAILED_DEPLOYING' => 7, + 'SCALING' => 8, + 'FAILED_SCALING' => 9, + 'COOLDOWN' => 10 + } + + STATE_STR = [ + 'PENDING', + 'DEPLOYING', + 'RUNNING', + 'UNDEPLOYING', + 'WARNING', + 'DONE', + 'FAILED_UNDEPLOYING', + 'FAILED_DEPLOYING', + 'SCALING', + 'FAILED_SCALING', + 'COOLDOWN' + ] + + # Returns the string representation of the service state + # @param [String] state String number representing the state + # @return the state string + def self.state_str(state_number) + return STATE_STR[state_number.to_i] + end + + # Build a json specifying an action + # @param [String] perform action to be performed (i.e: shutdowm) + # @param [Hash, nil] params contains the params for the action + # @return [String] json representing the action + def self.build_json_action(perform, params=nil) + body = Hash.new + body['perform'] = perform + body['params'] = params if params + + action = Hash.new + action['action'] = body + + JSON.pretty_generate action + end + + # CLI options + + DEFAULT_OPTIONS = [ + ENDPOINT = { + :name => "server", + :short => "-s url", + :large => "--server url", + :format => String, + :description => "Service endpoint" + }, + USERNAME={ + :name => "username", + :short => "-u name", + :large => "--username name", + :format => String, + :description => "User name" + }, + PASSWORD={ + :name => "password", + :short => "-p pass", + :large => "--password pass", + :format => String, + :description => "User password" + } + ] + + JSON_FORMAT = { + :name => "json", + :short => "-j", + :large => "--json", + :description => "Print the resource in JSON" + } + + TOP = { + :name => "top", + :short => "-t", + :large => "--top", + :description => "Top for the command" + } + + PERIOD = { + :name => "period", + :short => "-p x", + :large => "--period x", + :format => Integer, + :description => "Seconds between each group of actions" + } + + NUMBER = { + :name => "number", + :short => "-n x", + :large => "--number x", + :format => Integer, + :description => "Number of VMs to apply the action to each period" + } + + FORCE = { + :name => "force", + :short => "-f", + :large => "--force", + :description => "Force the new cardinality even if it is outside the limits" + } + + # Format helpers + +# def self.rname_to_id(name, poolname, options) + def self.rname_to_id(name, poolname) + return 0, name.to_i if name.match(/^[0123456789]+$/) + + client = Service::Client.new() + + resource_path = case poolname + when "SERVICE" then "/service" + when "SERVICE TEMPLATE" then "/service_template" + end + + response = client.get(resource_path) + + if CloudClient::is_error?(response) + return -1, "OpenNebula #{poolname} name not found," << + " use the ID instead" + end + + pool = JSON.parse(response.body) + name_to_id(name, pool, poolname) + end + + def self.rname_to_id_desc(poolname) + "OpenNebula #{poolname} name or id" + end + + def self.name_to_id(name, pool, ename) + if pool['DOCUMENT_POOL']['DOCUMENT'].nil? + return -1, "#{ename} named #{name} not found." + end + + objects = pool['DOCUMENT_POOL']['DOCUMENT'].select {|object| object['NAME'] == name } + + if objects.length>0 + if objects.length>1 + return -1, "There are multiple #{ename}s with name #{name}." + else + result = objects.first['ID'] + end + else + return -1, "#{ename} named #{name} not found." + end + + return 0, result + end + + def self.list_to_id(names, poolname) + + client = Service::Client.new() + + resource_path = case poolname + when "SERVICE" then "/service" + when "SERVICE TEMPLATE" then "/service_template" + end + + response = client.get(resource_path) + + if CloudClient::is_error?(response) + return -1, "OpenNebula #{poolname} name not found," << + " use the ID instead" + end + + pool = JSON.parse(response.body) + + result = names.split(',').collect { |name| + if name.match(/^[0123456789]+$/) + name.to_i + else + rc = name_to_id(name, pool, poolname) + + if rc.first == -1 + return rc[0], rc[1] + end + + rc[1] + end + } + + return 0, result + end + + def self.list_to_id_desc(poolname) + "Comma-separated list of OpenNebula #{poolname} names or ids" + end + + # Perform an action on several resources + # @param [Array] ids resources ids + # @param [Block] block action to be performed + # @return [Integer] exit_code + def self.perform_actions(ids, &block) + exit_code = 0 + + ids.each do |id| + response = block.call(id) + + if CloudClient::is_error?(response) + puts response.to_s + exit_code = response.code.to_i + end + end + + exit_code + end + + class Client + def initialize(opts={}) + @username = opts[:username] || ENV['ONEFLOW_USER'] + @password = opts[:password] || ENV['ONEFLOW_PASSWORD'] + + url = opts[:url] || ENV['ONEFLOW_URL'] || 'http://localhost:2474' + + if @username.nil? && @password.nil? + if ENV["ONE_AUTH"] and !ENV["ONE_AUTH"].empty? and File.file?(ENV["ONE_AUTH"]) + one_auth = File.read(ENV["ONE_AUTH"]) + elsif File.file?(ENV["HOME"]+"/.one/one_auth") + one_auth = File.read(ENV["HOME"]+"/.one/one_auth") + end + + one_auth.rstrip! + + @username, @password = one_auth.split(':') + end + + @uri = URI.parse(url) + + @user_agent = "OpenNebula #{CloudClient::VERSION} " << + "(#{opts[:user_agent]||"Ruby"})" + + @host = nil + @port = nil + + if ENV['http_proxy'] + uri_proxy = URI.parse(ENV['http_proxy']) + @host = uri_proxy.host + @port = uri_proxy.port + end + end + + def get(path) + req = Net::HTTP::Proxy(@host, @port)::Get.new(path) + + do_request(req) + end + + def delete(path) + req =Net::HTTP::Proxy(@host, @port)::Delete.new(path) + + do_request(req) + end + + def post(path, body) + req = Net::HTTP::Proxy(@host, @port)::Post.new(path) + req.body = body + + do_request(req) + end + + def put(path, body) + req = Net::HTTP::Proxy(@host, @port)::Put.new(path) + req.body = body + + do_request(req) + end + + def login + req = Net::HTTP::Proxy(@host, @port)::Post.new('/login') + + do_request(req) + end + + def logout + req = Net::HTTP::Proxy(@host, @port)::Post.new('/logout') + + do_request(req) + end + + private + + def do_request(req) + req.basic_auth @username, @password + + req['User-Agent'] = @user_agent + + res = CloudClient::http_start(@uri, @timeout) do |http| + http.request(req) + end + + res + end + end +end \ No newline at end of file diff --git a/ruby-opennebula-4.4/lib/opennebula/pool.rb b/ruby-opennebula-4.4/lib/opennebula/pool.rb new file mode 100644 index 0000000..59b832e --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/pool.rb @@ -0,0 +1,160 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +require 'opennebula/xml_utils' + +module OpenNebula + + # The Pool class represents a generic OpenNebula Pool in XML format + # and provides the basic functionality to handle the Pool elements + class Pool < XMLPool + include Enumerable + alias_method :each_with_xpath, :each + + protected + #pool:: _String_ XML name of the root element + #element:: _String_ XML name of the Pool elements + #client:: _Client_ represents a XML-RPC connection + def initialize(pool,element,client) + super(nil) + + @pool_name = pool.upcase + @element_name = element.upcase + + @client = client + end + + # Default Factory Method for the Pools. The factory method returns an + # suitable PoolElement object. Each Pool MUST implement the + # corresponding factory method + # element_xml:: _XML_ XML element describing the pool element + # [return] a PoolElement object + def factory(element_xml) + OpenNebula::PoolElement.new(element_xml,client) + end + + ####################################################################### + # Common XML-RPC Methods for all the Pool Types + ####################################################################### + + #Gets the pool without any filter. Host, Group and User Pools + # xml_method:: _String_ the name of the XML-RPC method + def info(xml_method) + return xmlrpc_info(xml_method) + end + + alias_method :info!, :info + + def info_all(xml_method, *args) + return xmlrpc_info(xml_method,INFO_ALL,-1,-1, *args) + end + + def info_mine(xml_method, *args) + return xmlrpc_info(xml_method,INFO_MINE,-1,-1, *args) + end + + def info_group(xml_method, *args) + return xmlrpc_info(xml_method,INFO_GROUP,-1,-1, *args) + end + + def info_filter(xml_method, who, start_id, end_id, *args) + return xmlrpc_info(xml_method,who, start_id, end_id, *args) + end + + # Retrieves the monitoring data for all the Objects in the pool + # + # @param [String] xml_method xml-rcp method + # @param [String] root_elem Root for each individual PoolElement + # @param [String] timestamp_elem Name of the XML element with the last + # monitorization timestamp + # @param [Array] xpath_expressions Elements to retrieve. + # @param args arguemnts for the xml_method call + # + # @return [Hash>>>>, + # OpenNebula::Error] The first level hash uses the Object ID as keys, + # and as value a Hash with the requested xpath expressions, + # and an Array of 'timestamp, value'. + def monitoring(xml_method, root_elem, timestamp_elem, xpath_expressions, + *args) + + rc = @client.call(xml_method, *args) + + if ( OpenNebula.is_error?(rc) ) + return rc + end + + xmldoc = XMLElement.new + xmldoc.initialize_xml(rc, 'MONITORING_DATA') + + hash = {} + + # Get all existing Object IDs + ids = xmldoc.retrieve_elements("#{root_elem}/ID") + + if ids.nil? + return hash + else + ids.uniq! + end + + ids.each { |id| + hash[id] = OpenNebula.process_monitoring( + xmldoc, root_elem, timestamp_elem, id, xpath_expressions) + + } + + return hash + end + + private + # Calls to the corresponding info method to retreive the pool + # representation in XML format + # xml_method:: _String_ the name of the XML-RPC method + # args:: _Array_ with additional arguments for the info call + # [return] nil in case of success or an Error object + def xmlrpc_info(xml_method,*args) + rc = @client.call(xml_method,*args) + + if !OpenNebula.is_error?(rc) + initialize_xml(rc,@pool_name) + rc = nil + end + + return rc + end + + public + # Constants for info queries (include/RequestManagerPoolInfoFilter.h) + INFO_GROUP = -1 + INFO_ALL = -2 + INFO_MINE = -3 + + # Iterates over every PoolElement in the Pool and calls the block with a + # a PoolElement obtained calling the factory method + # block:: _Block_ + def each(&block) + each_element(block) if @xml + end + + # DO NOT USE - ONLY REXML BACKEND + def to_str + str = "" + REXML::Formatters::Pretty.new(1).write(@xml,str) + + return str + end + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/pool_element.rb b/ruby-opennebula-4.4/lib/opennebula/pool_element.rb new file mode 100644 index 0000000..2af930d --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/pool_element.rb @@ -0,0 +1,292 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +require 'opennebula/pool' + +module OpenNebula + # The PoolElement Class represents a generic element of a Pool in + # XML format + class PoolElement < XMLElement + + protected + # node:: _XML_is a XML element that represents the Pool element + # client:: _Client_ represents a XML-RPC connection + def initialize(node, client) + @xml = node + @client = client + + if self['ID'] + @pe_id = self['ID'].to_i + else + @pe_id = nil + end + @name = self['NAME'] if self['NAME'] + end + + ####################################################################### + # Common XML-RPC Methods for all the Pool Element Types + ####################################################################### + + # Common client call wrapper. Checks that @pe_id is defined, and + # returns nil instead of the response if it is successful + # + # @param [String] xml_method xml-rpc method + # @param [Array] args any arguments for the xml-rpc method + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def call(xml_method, *args) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(xml_method, *args) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + + # Calls to the corresponding info method to retreive the element + # detailed information in XML format + # + # @param [String] xml_method the name of the XML-RPC method + # @param [String] root_element Base XML element name + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def info(xml_method, root_element) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(xml_method, @pe_id) + + if !OpenNebula.is_error?(rc) + initialize_xml(rc, root_element) + rc = nil + + @pe_id = self['ID'].to_i if self['ID'] + @name = self['NAME'] if self['NAME'] + end + + return rc + end + + # Calls to the corresponding allocate method to create a new element + # in the OpenNebula core + # + # @param [String] xml_method the name of the XML-RPC method + # @param [Array] args any extra arguments for the xml-rpc method + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def allocate(xml_method, *args) + rc = @client.call(xml_method, *args) + + if !OpenNebula.is_error?(rc) + @pe_id = rc + rc = nil + end + + return rc + end + + # Calls to the corresponding update method to modify + # the object's template + # + # @param [String] xml_method the name of the XML-RPC method + # @param [String] new_template the new template contents + # @param [Array] args any extra arguments for the xml-rpc method + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def update(xml_method, new_template, *args) + new_template ||= template_xml + + return call(xml_method, @pe_id, new_template, *args) + end + + # Calls to the corresponding delete method to remove this element + # from the OpenNebula core + # + # @param [String] xml_method the name of the XML-RPC method + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def delete(xml_method) + return call(xml_method,@pe_id) + end + + # Calls to the corresponding chown method to modify + # the object's owner and group + # + # @param [String] xml_method the name of the XML-RPC method + # @param [Integer] uid the new owner id. Set to -1 to leave the current one + # @param [Integer] gid the new goup id. Set to -1 to leave the current one + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chown(xml_method, uid, gid) + return call(xml_method, @pe_id, uid, gid) + end + + # Calls to the corresponding chmod method to modify + # the object's permission bits + # + # @param xml_method [String] the name of the XML-RPC method + # @param octet [String] Permissions octed , e.g. 640 + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chmod_octet(xml_method, octet) + owner_u = octet[0..0].to_i & 4 != 0 ? 1 : 0 + owner_m = octet[0..0].to_i & 2 != 0 ? 1 : 0 + owner_a = octet[0..0].to_i & 1 != 0 ? 1 : 0 + group_u = octet[1..1].to_i & 4 != 0 ? 1 : 0 + group_m = octet[1..1].to_i & 2 != 0 ? 1 : 0 + group_a = octet[1..1].to_i & 1 != 0 ? 1 : 0 + other_u = octet[2..2].to_i & 4 != 0 ? 1 : 0 + other_m = octet[2..2].to_i & 2 != 0 ? 1 : 0 + other_a = octet[2..2].to_i & 1 != 0 ? 1 : 0 + + chmod(owner_u, owner_m, owner_a, group_u, group_m, group_a, other_u, + other_m, other_a) + end + + # Calls to the corresponding chmod method to modify + # the object's permission bits + # Each [Integer] parameter must be 1 to allow, 0 deny, -1 do not change + # + # @param xml_method [String] the name of the XML-RPC method + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chmod(xml_method, owner_u, owner_m, owner_a, group_u, group_m, group_a, other_u, + other_m, other_a) + return call(xml_method, @pe_id, owner_u, owner_m, + owner_a, group_u, group_m, group_a, other_u, + other_m, other_a) + end + + + # Retrieves this Element's monitoring data from OpenNebula + # + # @param [String] xml_method the name of the XML-RPC method + # @param [String] root_elem Root for each individual PoolElement + # @param [String] timestamp_elem Name of the XML element with the last + # monitorization timestamp + # @param xpath_expressions [Array] Xpath expressions for the + # elements to retrieve. + # + # @return [Hash>, OpenNebula::Error] Hash with + # the requested xpath expressions, and an Array of [timestamp, value]. + def monitoring(xml_method, root_elem, timestamp_elem, xpath_expressions) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(xml_method, @pe_id) + + if ( OpenNebula.is_error?(rc) ) + return rc + end + + xmldoc = XMLElement.new + xmldoc.initialize_xml(rc, 'MONITORING_DATA') + + + return OpenNebula.process_monitoring( + xmldoc, root_elem, timestamp_elem, @pe_id, xpath_expressions) + end + + public + + # Creates new element specifying its id + # id:: identifyier of the element + # client:: initialized OpenNebula::Client object + def self.new_with_id(id, client=nil) + self.new(self.build_xml(id), client) + end + + # Returns element identifier + # [return] _Integer_ the PoolElement ID + def id + @pe_id + end + + # Gets element name + # [return] _String_ the PoolElement name + def name + @name + end + + # DO NOT USE - ONLY REXML BACKEND + def to_str + str = "" + REXML::Formatters::Pretty.new(1).write(@xml,str) + + return str + end + end + + # Processes the monitoring data in XML returned by OpenNebula + # + # @param [XMLElement] xmldoc monitoring data returned by OpenNebula + # @param [String] root_elem Root for each individual PoolElement + # @param [String] timestamp_elem Name of the XML element with the last + # monitorization timestamp + # @param [Integer] oid Id of the object to process + # @param [Array] xpath_expressions Elements to retrieve. + # + # @return [Hash>, OpenNebula::Error] Hash with + # the requested xpath expressions, and an Array of [timestamp, value]. + def self.process_monitoring(xmldoc, root_elem, timestamp_elem, oid, xpath_expressions) + hash = {} + timestamps = xmldoc.retrieve_elements( + "#{root_elem}[ID=#{oid}]/#{timestamp_elem}") + + xpath_expressions.each { |xpath| + xpath_values = xmldoc.retrieve_elements("#{root_elem}[ID=#{oid}]/#{xpath}") + + if ( xpath_values.nil? ) + hash[xpath] = [] + else + hash[xpath] = timestamps.zip(xpath_values) + end + } + + return hash + end + + + # Alternative method with better performance for huge number of timestamps. + # For reasonable amounts of data, the current method is quicker +=begin + def self.process_monitoring(xmldoc, root_elem, timestamp_elem, oid, xpath_expressions) + hash = {} + + xpath_expressions.each { |xpath| + hash[xpath] = [] + } + + xmldoc.each("#{root_elem}[ID=#{oid}]") do |elem| + timestamp = elem[timestamp_elem] + + xpath_expressions.each { |xpath| + xpath_value = elem[xpath] + + hash[xpath] << [timestamp, xpath_value] if !xpath_value.nil? + } + end + + return hash + end +=end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/server_cipher_auth.rb b/ruby-opennebula-4.4/lib/opennebula/server_cipher_auth.rb new file mode 100644 index 0000000..78476ef --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/server_cipher_auth.rb @@ -0,0 +1,148 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +require 'openssl' +require 'digest/sha1' + +require 'base64' +require 'fileutils' + +module OpenNebula; end + +# Server authentication class. This method can be used by OpenNebula services +# to let access authenticated users by other means. It is based on OpenSSL +# symmetric ciphers +class OpenNebula::ServerCipherAuth + ########################################################################### + #Constants with paths to relevant files and defaults + ########################################################################### + + CIPHER = "aes-256-cbc" + + ########################################################################### + + def initialize(srv_user, srv_passwd) + @srv_user = srv_user + @srv_passwd = srv_passwd + + if !srv_passwd.empty? + @key = Digest::SHA1.hexdigest(@srv_passwd) + else + @key = "" + end + + @cipher = OpenSSL::Cipher::Cipher.new(CIPHER) + end + + ########################################################################### + # Client side + ########################################################################### + + # Creates a ServerCipher for client usage + def self.new_client(srv_user=nil, srv_passwd=nil) + if ( srv_user == nil || srv_passwd == nil ) + begin + if ENV["ONE_CIPHER_AUTH"] and !ENV["ONE_CIPHER_AUTH"].empty? + one_auth = File.read(ENV["ONE_CIPHER_AUTH"]) + else + raise "ONE_CIPHER_AUTH environment variable not set" + end + + one_auth.rstrip! + + rc = one_auth.match(/(.*?):(.*)/) + + if rc.nil? + raise "Bad format for one_auth token (:)" + else + srv_user = rc[1] + srv_passwd = rc[2] + end + rescue => e + raise e.message + end + end + + self.new(srv_user, srv_passwd) + end + + # Generates a login token in the form: + # - server_user:target_user:time_expires + # The token is then encrypted with the contents of one_auth + def login_token(expire, target_user=nil) + target_user ||= @srv_user + token_txt = "#{@srv_user}:#{target_user}:#{expire}" + + token = encrypt(token_txt) + token64 = Base64::encode64(token).strip.delete("\n") + + return "#{@srv_user}:#{target_user}:#{token64}" + end + + # Returns a valid password string to create a user using this auth driver + def password + return @srv_passwd + end + + ########################################################################### + # Driver side + ########################################################################### + + # Creates a ServerCipher for driver usage + def self.new_driver() + self.new("","") + end + + # auth method for auth_mad + def authenticate(srv_user,srv_pass, signed_text) + begin + @key = srv_pass + + s_user, t_user, expires = decrypt(signed_text).split(':') + + return "User name missmatch" if s_user != srv_user + + return "login token expired" if Time.now.to_i >= expires.to_i + + return true + rescue => e + return e.message + end + end + + private + + def encrypt(data) + @cipher.encrypt + @cipher.key = @key + + rc = @cipher.update(data) + rc << @cipher.final + + return rc + end + + def decrypt(data) + @cipher.decrypt + @cipher.key = @key + + rc = @cipher.update(Base64::decode64(data)) + rc << @cipher.final + + return rc + end +end + diff --git a/ruby-opennebula-4.4/lib/opennebula/server_x509_auth.rb b/ruby-opennebula-4.4/lib/opennebula/server_x509_auth.rb new file mode 100644 index 0000000..6799e43 --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/server_x509_auth.rb @@ -0,0 +1,104 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +require 'openssl' +require 'base64' +require 'fileutils' + +require 'opennebula/x509_auth' + +module OpenNebula; end + +# Server authentication class. This authmethod can be used by opennebula services +# to let access authenticated users by other means. It is based on x509 server +# certificates +class OpenNebula::ServerX509Auth < OpenNebula::X509Auth + ########################################################################### + #Constants with paths to relevant files and defaults + ########################################################################### + + SERVER_AUTH_CONF_PATH = ETC_LOCATION + "/auth/server_x509_auth.conf" + + SERVER_DEFAULTS = { + :one_cert => ETC_LOCATION + "/auth/cert.pem", + :one_key => ETC_LOCATION + "/auth/key.pem" + } + + ########################################################################### + + def initialize() + @options = SERVER_DEFAULTS + + load_options(SERVER_AUTH_CONF_PATH) + + begin + certs = [ File.read(@options[:one_cert]) ] + key = File.read(@options[:one_key]) + + super(:certs_pem => certs, :key_pem => key) + rescue + raise + end + + if @options[:srv_user] == nil || @options[:srv_user].empty? + raise "User for x509 server not defined" + end + end + + ########################################################################### + # Client side + ########################################################################### + + # Creates a ServerCipher for client and driver sage + class << OpenNebula::ServerX509Auth + alias :new_client :new + alias :new_driver :new + end + + # Generates a login token in the form: + # - server_user:target_user:time_expires + def login_token(expire, target_user=nil) + target_user ||= @options[:srv_user] + token_txt = "#{@options[:srv_user]}:#{target_user}:#{expire}" + + token = encrypt(token_txt) + token64 = Base64::encode64(token).strip.delete("\n") + + return "#{@options[:srv_user]}:#{target_user}:#{token64}" + end + + ########################################################################### + # Server side + ########################################################################### + + # auth method for auth_mad + def authenticate(server_user, server_pass, signed_text) + begin + s_user, t_user, expires = decrypt(signed_text).split(':') + + return "Server password missmatch" if server_pass != password + + return "User name missmatch" if ( s_user != server_user || + s_user != @options[:srv_user] ) + + return "login token expired" if Time.now.to_i >= expires.to_i + + return true + rescue => e + return e.message + end + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/ssh_auth.rb b/ruby-opennebula-4.4/lib/opennebula/ssh_auth.rb new file mode 100644 index 0000000..db06978 --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/ssh_auth.rb @@ -0,0 +1,141 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +require 'pp' +require 'openssl' +require 'base64' +require 'fileutils' + +module OpenNebula; end + +# SSH key authentication class. It can be used as a driver for auth_mad +# as auth method is defined. It also holds some helper methods to be used +# by oneauth command +class OpenNebula::SshAuth + LOGIN_PATH = ENV['HOME']+'/.one/one_ssh' + + # Initialize SshAuth object + # + # @param [Hash] default options for path + # @option options [String] :public_key public key for the user + # @option options [String] :private_key key private key for the user. + def initialize(options={}) + @private_key = nil + @public_key = nil + + # Initialize the private key + if options[:private_key] + begin + @private_key = File.read(options[:private_key]) + rescue Exception => e + raise "Cannot read #{options[:private_key]}" + end + + @private_key_rsa = OpenSSL::PKey::RSA.new(@private_key) + end + + # Initialize the public key + if options[:public_key] + @public_key = options[:public_key] + elsif @private_key != nil + # Init ssh keys using private key. public key is extracted in a + # format compatible with openssl. The public key does not contain + # "---- BEGIN/END PUBLIC KEY ----" and is in a single line + @public_key = @private_key_rsa.public_key.to_pem.split("\n") + @public_key = @public_key.reject {|l| l.match(/PUBLIC KEY/) }.join('') + end + + if @private_key.nil? && @public_key.nil? + raise "You have to define at least one of the keys" + end + + @public_key_rsa = OpenSSL::PKey::RSA.new(Base64::decode64(@public_key)) + end + + # Creates the login file for ssh authentication at ~/.one/one_ssh. + # By default it is valid for 1 hour but it can be changed to any number + # of seconds with expire parameter (in seconds) + def login(user, expire=3600) + expire ||= 3600 + + # Init proxy file path and creates ~/.one directory if needed + proxy_dir = File.dirname(LOGIN_PATH) + + begin + FileUtils.mkdir_p(proxy_dir) + rescue Errno::EEXIST + end + + # Generate security token + time = Time.now.to_i + expire.to_i + + secret_plain = "#{user}:#{time}" + secret_crypted = encrypt(secret_plain) + + proxy = "#{user}:#{secret_crypted}" + + file = File.open(LOGIN_PATH, "w") + file.write(proxy) + file.close + + File.chmod(0600,LOGIN_PATH) + + secret_crypted + end + + # Returns a valid password string to create a user using this auth driver. + # In this case the ssh public key. + def password + @public_key + end + + # Checks the proxy created with the login method + def authenticate(user, token) + begin + token_plain = decrypt(token) + _user, time = token_plain.split(':') + + if user == _user + if Time.now.to_i >= time.to_i + return "ssh proxy expired, login again to renew it" + else + return true + end + else + return "invalid credentials" + end + rescue + return "error" + end + end + + private + + ########################################################################### + # Methods to handle ssh keys + ########################################################################### + # Encrypts data with the private key of the user and returns + # base 64 encoded output in a single line + def encrypt(data) + Base64::encode64(@private_key_rsa.private_encrypt(data)).gsub!(/\n/, '').strip + end + + # Decrypts base 64 encoded data with pub_key (public key) + def decrypt(data) + @public_key_rsa.public_decrypt(Base64::decode64(data)) + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/system.rb b/ruby-opennebula-4.4/lib/opennebula/system.rb new file mode 100644 index 0000000..ab3eae4 --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/system.rb @@ -0,0 +1,141 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +require 'opennebula/pool' + +module OpenNebula + class System + ####################################################################### + # Constants and Class attribute accessors + ####################################################################### + + SYSTEM_METHODS = { + :userquotainfo => "userquota.info", + :userquotaupdate => "userquota.update", + :groupquotainfo => "groupquota.info", + :groupquotaupdate => "groupquota.update", + :version => "system.version", + :config => "system.config" + } + + ####################################################################### + # Class constructor + ####################################################################### + + # Constructor + # @param [Client] client that represents a XML-RPC connection + def initialize(client) + @client = client + end + + ####################################################################### + # XML-RPC Methods + ####################################################################### + + # Gets the oned version + # + # @return [String, OpenNebula::Error] the oned version in case + # of success, Error otherwise + def get_oned_version() + return @client.call("system.version") + end + + # Returns whether of not the oned version is the same as the OCA version + # + # @return [true, false, OpenNebula::Error] true if oned is the same + # version + def compatible_version() + no_revision = VERSION[/^\d+\.\d+\./] + oned_v = get_oned_version + + if OpenNebula.is_error?(oned_v) + return oned_v + end + + return (oned_v =~ /#{no_revision}/) != nil + end + + # Gets the oned configuration + # + # @return [XMLElement, OpenNebula::Error] the oned configuration in case + # of success, Error otherwise + def get_configuration() + rc = @client.call(SYSTEM_METHODS[:config]) + + if OpenNebula.is_error?(rc) + return rc + end + + config = XMLElement.new + config.initialize_xml(rc, 'TEMPLATE') + + return config + end + + # Gets the default user quota limits + # + # @return [XMLElement, OpenNebula::Error] the default user quota in case + # of success, Error otherwise + def get_user_quotas() + rc = @client.call(SYSTEM_METHODS[:userquotainfo]) + + if OpenNebula.is_error?(rc) + return rc + end + + default_quotas = XMLElement.new + default_quotas.initialize_xml(rc, 'DEFAULT_USER_QUOTAS') + + return default_quotas + end + + # Sets the default user quota limits + # @param quota [String] a template (XML or txt) with the new quota limits + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def set_user_quotas(quota) + return @client.call(SYSTEM_METHODS[:userquotaupdate], quota) + end + + # Gets the default group quota limits + # + # @return [XMLElement, OpenNebula::Error] the default group quota in case + # of success, Error otherwise + def get_group_quotas() + rc = @client.call(SYSTEM_METHODS[:groupquotainfo]) + + if OpenNebula.is_error?(rc) + return rc + end + + default_quotas = XMLElement.new + default_quotas.initialize_xml(rc, 'DEFAULT_GROUP_QUOTAS') + + return default_quotas + end + + # Sets the default group quota limits + # @param quota [String] a template (XML or txt) with the new quota limits + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def set_group_quotas(quota) + return @client.call(SYSTEM_METHODS[:groupquotaupdate], quota) + end + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/template.rb b/ruby-opennebula-4.4/lib/opennebula/template.rb new file mode 100644 index 0000000..91d92c0 --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/template.rb @@ -0,0 +1,216 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +require 'opennebula/pool_element' + +module OpenNebula + class Template < PoolElement + ####################################################################### + # Constants and Class Methods + ####################################################################### + + + TEMPLATE_METHODS = { + :allocate => "template.allocate", + :instantiate => "template.instantiate", + :info => "template.info", + :update => "template.update", + :delete => "template.delete", + :chown => "template.chown", + :chmod => "template.chmod", + :clone => "template.clone", + :rename => "template.rename" + } + + # Creates a Template description with just its identifier + # this method should be used to create plain Template objects. + # +id+ the id of the user + # + # Example: + # template = Template.new(Template.build_xml(3),rpc_client) + # + def Template.build_xml(pe_id=nil) + if pe_id + obj_xml = "#{pe_id}" + else + obj_xml = "" + end + + XMLElement.build_xml(obj_xml,'VMTEMPLATE') + end + + # Class constructor + def initialize(xml, client) + super(xml,client) + + @client = client + end + + ####################################################################### + # XML-RPC Methods for the Template Object + ####################################################################### + + # Retrieves the information of the given Template. + def info() + super(TEMPLATE_METHODS[:info], 'VMTEMPLATE') + end + + alias_method :info!, :info + + # Allocates a new Template in OpenNebula + # + # @param description [String] The contents of the Template. + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def allocate(description) + super(TEMPLATE_METHODS[:allocate], description) + end + + # Deletes the Template + def delete() + super(TEMPLATE_METHODS[:delete]) + end + + # Creates a VM instance from a Template + # + # @param name [String] Name for the VM instance. If it is an empty + # string OpenNebula will set a default name + # @param hold [true,false] false to create the VM in pending state, + # true to create it on hold + # @param template [String] User provided Template to merge with the + # one being instantiated + # + # @return [Integer, OpenNebula::Error] The new VM id, Error + # otherwise + def instantiate(name="", hold=false, template="") + return Error.new('ID not defined') if !@pe_id + + name ||= "" + template ||= "" + + rc = @client.call( + TEMPLATE_METHODS[:instantiate], @pe_id, name, hold, template) + + return rc + end + + # Replaces the template contents + # + # @param new_template [String] New template contents + # @param append [true, false] True to append new attributes instead of + # replace the whole template + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def update(new_template, append=false) + super(TEMPLATE_METHODS[:update], new_template, append ? 1 : 0) + end + + # Publishes the Template, to be used by other users + def publish + set_publish(true) + end + + # Unplubishes the Image + def unpublish + set_publish(false) + end + + # Changes the owner/group + # uid:: _Integer_ the new owner id. Set to -1 to leave the current one + # gid:: _Integer_ the new group id. Set to -1 to leave the current one + # [return] nil in case of success or an Error object + def chown(uid, gid) + super(TEMPLATE_METHODS[:chown], uid, gid) + end + + # Changes the Template permissions. + # + # @param octet [String] Permissions octed , e.g. 640 + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chmod_octet(octet) + super(TEMPLATE_METHODS[:chmod], octet) + end + + # Changes the Template permissions. + # Each [Integer] argument must be 1 to allow, 0 deny, -1 do not change + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chmod(owner_u, owner_m, owner_a, group_u, group_m, group_a, other_u, + other_m, other_a) + super(TEMPLATE_METHODS[:chmod], owner_u, owner_m, owner_a, group_u, + group_m, group_a, other_u, other_m, other_a) + end + + # Clones this Template into a new one + # + # @param [String] name for the new Template. + # + # @return [Integer, OpenNebula::Error] The new Template ID in case + # of success, Error otherwise + def clone(name) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(TEMPLATE_METHODS[:clone], @pe_id, name) + + return rc + end + + # Renames this Template + # + # @param name [String] New name for the Template. + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def rename(name) + return call(TEMPLATE_METHODS[:rename], @pe_id, name) + end + + ####################################################################### + # Helpers to get Template information + ####################################################################### + + # Returns the group identifier + # [return] _Integer_ the element's group ID + def gid + self['GID'].to_i + end + + def owner_id + self['UID'].to_i + end + + def public? + if self['PERMISSIONS/GROUP_U'] == "1" || self['PERMISSIONS/OTHER_U'] == "1" + true + else + false + end + end + + private + + def set_publish(published) + group_u = published ? 1 : 0 + + chmod(-1, -1, -1, group_u, -1, -1, -1, -1, -1) + end + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/template_pool.rb b/ruby-opennebula-4.4/lib/opennebula/template_pool.rb new file mode 100644 index 0000000..10bfd93 --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/template_pool.rb @@ -0,0 +1,79 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +require 'opennebula/pool' + +module OpenNebula + class TemplatePool < Pool + ####################################################################### + # Constants and Class attribute accessors + ####################################################################### + + + TEMPLATE_POOL_METHODS = { + :info => "templatepool.info" + } + + ####################################################################### + # Class constructor & Pool Methods + ####################################################################### + + # +client+ a Client object that represents an XML-RPC connection + # +user_id+ used to refer to a Pool with Templates from that user + def initialize(client, user_id=-1) + super('VMTEMPLATE_POOL','VMTEMPLATE',client) + + @user_id = user_id + end + + # Factory method to create Template objects + def factory(element_xml) + OpenNebula::Template.new(element_xml,@client) + end + + ####################################################################### + # XML-RPC Methods for the Template Object + ####################################################################### + + # Retrieves all or part of the VirtualMachines in the pool. + def info(*args) + case args.size + when 0 + info_filter(TEMPLATE_POOL_METHODS[:info],@user_id,-1,-1) + when 3 + info_filter(TEMPLATE_POOL_METHODS[:info],args[0],args[1],args[2]) + end + end + + def info_all() + return super(TEMPLATE_POOL_METHODS[:info]) + end + + def info_mine() + return super(TEMPLATE_POOL_METHODS[:info]) + end + + def info_group() + return super(TEMPLATE_POOL_METHODS[:info]) + end + + alias_method :info!, :info + alias_method :info_all!, :info_all + alias_method :info_mine!, :info_mine + alias_method :info_group!, :info_group + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/user.rb b/ruby-opennebula-4.4/lib/opennebula/user.rb new file mode 100644 index 0000000..4548ae5 --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/user.rb @@ -0,0 +1,198 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +require 'opennebula/pool_element' + +module OpenNebula + class User < PoolElement + ####################################################################### + # Constants and Class Methods + ####################################################################### + + USER_METHODS = { + :info => "user.info", + :allocate => "user.allocate", + :delete => "user.delete", + :passwd => "user.passwd", + :chgrp => "user.chgrp", + :addgroup => "user.addgroup", + :delgroup => "user.delgroup", + :update => "user.update", + :chauth => "user.chauth", + :quota => "user.quota" + } + + SELF = -1 + + # Driver name for default core authentication + CORE_AUTH = "core" + + # Driver name for default core authentication + CIPHER_AUTH = "server_cipher" + + # Driver name for ssh authentication + SSH_AUTH = "ssh" + + # Driver name for x509 authentication + X509_AUTH = "x509" + + # Driver name for x509 proxy authentication + X509_PROXY_AUTH = "x509_proxy" + + # Creates a User description with just its identifier + # this method should be used to create plain User objects. + # +id+ the id of the user + # + # Example: + # user = User.new(User.build_xml(3),rpc_client) + # + def User.build_xml(pe_id=nil) + if pe_id + user_xml = "#{pe_id}" + else + user_xml = "" + end + + XMLElement.build_xml(user_xml, 'USER') + end + + # Class constructor + def initialize(xml, client) + super(xml,client) + + @client = client + end + + ####################################################################### + # XML-RPC Methods for the User Object + ####################################################################### + + # Retrieves the information of the given User. + def info() + super(USER_METHODS[:info], 'USER') + end + + alias_method :info!, :info + + # Allocates a new User in OpenNebula + # + # +username+ Name of the new user. + # + # +password+ Password for the new user + def allocate(username, password, driver=CORE_AUTH) + super(USER_METHODS[:allocate], username, password, driver) + end + + # Replaces the template contents + # + # @param new_template [String] New template contents + # @param append [true, false] True to append new attributes instead of + # replace the whole template + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def update(new_template, append=false) + super(USER_METHODS[:update], new_template, append ? 1 : 0) + end + + # Deletes the User + def delete() + super(USER_METHODS[:delete]) + end + + # Changes the password of the given User + # + # +password+ String containing the new password + def passwd(password) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(USER_METHODS[:passwd], @pe_id, password) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + + # Changes the primary group + # gid:: _Integer_ the new group id. Set to -1 to leave the current one + # [return] nil in case of success or an Error object + def chgrp(gid) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(USER_METHODS[:chgrp],@pe_id, gid) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + + # Adds the User to a secondary group + # @param gid [Integer] the new group id. + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def addgroup(gid) + return call(USER_METHODS[:addgroup], @pe_id, gid) + end + + # Removes the User from a secondary group. Fails if the + # group is the main one + # @param gid [Integer] the group id. + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def delgroup(gid) + return call(USER_METHODS[:delgroup], @pe_id, gid) + end + + # Changes the auth driver and the password of the given User + # + # @param auth [String] the new auth driver + # @param password [String] the new password. If it is an empty string, + # the user password is not changed + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chauth(auth, password="") + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(USER_METHODS[:chauth],@pe_id, auth, password) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + + # Sets the user quota limits + # @param quota [String] a template (XML or txt) with the new quota limits + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def set_quota(quota) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(USER_METHODS[:quota],@pe_id, quota) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + + ####################################################################### + # Helpers to get User information + ####################################################################### + + # Returns the group identifier + # [return] _Integer_ the element's group ID + def gid + self['GID'].to_i + end + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/user_pool.rb b/ruby-opennebula-4.4/lib/opennebula/user_pool.rb new file mode 100644 index 0000000..6e87507 --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/user_pool.rb @@ -0,0 +1,55 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +require 'opennebula/pool' + +module OpenNebula + class UserPool < Pool + ####################################################################### + # Constants and Class attribute accessors + ####################################################################### + + USER_POOL_METHODS = { + :info => "userpool.info" + } + + ####################################################################### + # Class constructor & Pool Methods + ####################################################################### + + # +client+ a Client object that represents a XML-RPC connection + def initialize(client) + super('USER_POOL','USER',client) + end + + # Factory method to create User objects + def factory(element_xml) + OpenNebula::User.new(element_xml,@client) + end + + ####################################################################### + # XML-RPC Methods for the User Object + ####################################################################### + + # Retrieves all the Users in the pool. + def info() + super(USER_POOL_METHODS[:info]) + end + + alias_method :info!, :info + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/virtual_machine.rb b/ruby-opennebula-4.4/lib/opennebula/virtual_machine.rb new file mode 100644 index 0000000..5a60c20 --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/virtual_machine.rb @@ -0,0 +1,597 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +require 'opennebula/pool_element' + +module OpenNebula + class VirtualMachine < PoolElement + ####################################################################### + # Constants and Class Methods + ####################################################################### + + VM_METHODS = { + :info => "vm.info", + :allocate => "vm.allocate", + :action => "vm.action", + :migrate => "vm.migrate", + :deploy => "vm.deploy", + :savedisk => "vm.savedisk", + :chown => "vm.chown", + :chmod => "vm.chmod", + :monitoring => "vm.monitoring", + :attach => "vm.attach", + :detach => "vm.detach", + :rename => "vm.rename", + :update => "vm.update", + :resize => "vm.resize", + :snapshotcreate => "vm.snapshotcreate", + :snapshotrevert => "vm.snapshotrevert", + :snapshotdelete => "vm.snapshotdelete", + :attachnic => "vm.attachnic", + :detachnic => "vm.detachnic", + :resize => "vm.resize", + :recover => "vm.recover" + } + + VM_STATE=%w{INIT PENDING HOLD ACTIVE STOPPED SUSPENDED DONE FAILED + POWEROFF UNDEPLOYED} + + LCM_STATE=%w{LCM_INIT PROLOG BOOT RUNNING MIGRATE SAVE_STOP SAVE_SUSPEND + SAVE_MIGRATE PROLOG_MIGRATE PROLOG_RESUME EPILOG_STOP EPILOG + SHUTDOWN CANCEL FAILURE CLEANUP_RESUBMIT UNKNOWN HOTPLUG SHUTDOWN_POWEROFF + BOOT_UNKNOWN BOOT_POWEROFF BOOT_SUSPENDED BOOT_STOPPED CLEANUP_DELETE + HOTPLUG_SNAPSHOT HOTPLUG_NIC HOTPLUG_SAVEAS HOTPLUG_SAVEAS_POWEROFF + HOTPLUG_SAVEAS_SUSPENDED SHUTDOWN_UNDEPLOY EPILOG_UNDEPLOY + PROLOG_UNDEPLOY BOOT_UNDEPLOY} + + SHORT_VM_STATES={ + "INIT" => "init", + "PENDING" => "pend", + "HOLD" => "hold", + "ACTIVE" => "actv", + "STOPPED" => "stop", + "SUSPENDED" => "susp", + "DONE" => "done", + "FAILED" => "fail", + "POWEROFF" => "poff", + "UNDEPLOYED"=> "unde" + } + + SHORT_LCM_STATES={ + "PROLOG" => "prol", + "BOOT" => "boot", + "RUNNING" => "runn", + "MIGRATE" => "migr", + "SAVE_STOP" => "save", + "SAVE_SUSPEND" => "save", + "SAVE_MIGRATE" => "save", + "PROLOG_MIGRATE" => "migr", + "PROLOG_RESUME" => "prol", + "EPILOG_STOP" => "epil", + "EPILOG" => "epil", + "SHUTDOWN" => "shut", + "CANCEL" => "shut", + "FAILURE" => "fail", + "CLEANUP_RESUBMIT" => "clea", + "UNKNOWN" => "unkn", + "HOTPLUG" => "hotp", + "SHUTDOWN_POWEROFF" => "shut", + "BOOT_UNKNOWN" => "boot", + "BOOT_POWEROFF" => "boot", + "BOOT_SUSPENDED" => "boot", + "BOOT_STOPPED" => "boot", + "CLEANUP_DELETE" => "clea", + "HOTPLUG_SNAPSHOT" => "snap", + "HOTPLUG_NIC" => "hotp", + "HOTPLUG_SAVEAS" => "hotp", + "HOTPLUG_SAVEAS_POWEROFF" => "hotp", + "HOTPLUG_SAVEAS_SUSPENDED" => "hotp", + "SHUTDOWN_UNDEPLOY" => "shut", + "EPILOG_UNDEPLOY" => "epil", + "PROLOG_UNDEPLOY" => "prol", + "BOOT_UNDEPLOY" => "boot" + } + + MIGRATE_REASON=%w{NONE ERROR USER} + + SHORT_MIGRATE_REASON={ + "NONE" => "none", + "ERROR" => "erro", + "USER" => "user" + } + + HISTORY_ACTION=%w{none migrate live-migrate shutdown shutdown-hard + undeploy undeploy-hard hold release stop suspend resume boot delete + delete-recreate reboot reboot-hard resched unresched poweroff + poweroff-hard} + + # Creates a VirtualMachine description with just its identifier + # this method should be used to create plain VirtualMachine objects. + # +id+ the id of the vm + # + # Example: + # vnet = VirtualMachine.new(VirtualMachine.build_xml(3),rpc_client) + # + def VirtualMachine.build_xml(pe_id=nil) + if pe_id + vm_xml = "#{pe_id}" + else + vm_xml = "" + end + + XMLElement.build_xml(vm_xml, 'VM') + end + + def VirtualMachine.get_reason(reason) + reason=MIGRATE_REASON[reason.to_i] + reason_str=SHORT_MIGRATE_REASON[reason] + + reason_str + end + + def VirtualMachine.get_history_action(action) + return HISTORY_ACTION[action.to_i] + end + + # Class constructor + def initialize(xml, client) + super(xml,client) + end + + ####################################################################### + # XML-RPC Methods for the Virtual Machine Object + ####################################################################### + + # Retrieves the information of the given VirtualMachine. + def info() + super(VM_METHODS[:info], 'VM') + end + + alias_method :info!, :info + + # Allocates a new VirtualMachine in OpenNebula + # + # @param description [String] A string containing the template of + # the VirtualMachine. + # @param hold [true,false] false to create the VM in pending state, + # true to create it on hold + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def allocate(description, hold=false) + super(VM_METHODS[:allocate], description, hold) + end + + # Replaces the template contents + # + # @param new_template [String] New template contents + # @param append [true, false] True to append new attributes instead of + # replace the whole template + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def update(new_template=nil, append=false) + super(VM_METHODS[:update], new_template, append ? 1 : 0) + end + + # Returns the element in text form + # + # @param indent [true,false] indents the resulting string, defaults to true + # + # @return [String] The USER_TEMPLATE + def user_template_str(indent=true) + template_like_str('USER_TEMPLATE', indent) + end + + # Returns the element in XML form + # + # @return [String] The USER_TEMPLATE + def user_template_xml + if NOKOGIRI + @xml.xpath('TEMPLATE').to_s + else + @xml.elements['TEMPLATE'].to_s + end + end + + + # Initiates the instance of the VM on the target host. + # + # @param host_id [Interger] The host id (hid) of the target host where + # the VM will be instantiated. + # @param enforce [true|false] If it is set to true, the host capacity + # will be checked, and the deployment will fail if the host is + # overcommited. Defaults to false + # @param ds_id [Integer] The System Datastore where to deploy the VM. To + # use the default, set it to -1 + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def deploy(host_id, enforce=false, ds_id=-1) + enforce ||= false + ds_id ||= -1 + return call(VM_METHODS[:deploy], @pe_id, host_id.to_i, enforce, ds_id.to_i) + end + + # Shutdowns an already deployed VM + def shutdown(hard=false) + action(hard ? 'shutdown-hard' : 'shutdown') + end + + # Shuts down an already deployed VM, saving its state in the system DS + def undeploy(hard=false) + action(hard ? 'undeploy-hard' : 'undeploy') + end + + # Powers off a running VM + def poweroff(hard=false) + action(hard ? 'poweroff-hard' : 'poweroff') + end + + # Reboots an already deployed VM + def reboot(hard=false) + action(hard ? 'reboot-hard' : 'reboot') + end + + # @deprecated use {#reboot} + def reset + reboot(true) + end + + # @deprecated use {#shutdown} + def cancel + shutdown(true) + end + + # Sets a VM to hold state, scheduler will not deploy it + def hold + action('hold') + end + + # Releases a VM from hold state + def release + action('release') + end + + # Stops a running VM + def stop + action('stop') + end + + # Saves a running VM + def suspend + action('suspend') + end + + # Resumes the execution of a saved VM + def resume + action('resume') + end + + # Attaches a disk to a running VM + # + # @param disk_template [String] Template containing a DISK element + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def disk_attach(disk_template) + return call(VM_METHODS[:attach], @pe_id, disk_template) + end + + alias_method :attachdisk, :disk_attach + + # Detaches a disk from a running VM + # + # @param disk_id [Integer] Id of the disk to be detached + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def disk_detach(disk_id) + return call(VM_METHODS[:detach], @pe_id, disk_id) + end + + alias_method :detachdisk, :disk_detach + + # Attaches a NIC to a running VM + # + # @param nic_template [String] Template containing a NIC element + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def nic_attach(nic_template) + return call(VM_METHODS[:attachnic], @pe_id, nic_template) + end + + # Detaches a NIC from a running VM + # + # @param nic_id [Integer] Id of the NIC to be detached + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def nic_detach(nic_id) + return call(VM_METHODS[:detachnic], @pe_id, nic_id) + end + + # Deletes a VM from the pool + def delete(recreate=false) + if recreate + action('delete-recreate') + else + action('delete') + end + end + + # @deprecated use {#delete} instead + def finalize(recreate=false) + delete(recreate) + end + + # Forces a re-deployment of a VM in UNKNOWN or BOOT state + def boot + action('boot') + end + + alias_method :restart, :boot + + # @deprecated use {#delete} instead + def resubmit + action('delete-recreate') + end + + # Sets the re-scheduling flag for the VM + def resched + action('resched') + end + + # Unsets the re-scheduling flag for the VM + def unresched + action('unresched') + end + + # Moves a running VM to the specified host. With live=true the + # migration is done withdout downtime. + # + # @param host_id [Interger] The host id (hid) of the target host where + # the VM will be migrated. + # @param live [true|false] If true the migration is done without + # downtime. Defaults to false + # @param enforce [true|false] If it is set to true, the host capacity + # will be checked, and the deployment will fail if the host is + # overcommited. Defaults to false + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def migrate(host_id, live=false, enforce=false) + call(VM_METHODS[:migrate], @pe_id, host_id.to_i, live==true, + enforce) + end + + # @deprecated use {#migrate} instead + def live_migrate(host_id, enforce=false) + migrate(host_id, true, enforce) + end + + # Set the specified vm's disk to be saved in a new image + # when the VirtualMachine shutdowns + # + # @param disk_id [Integer] ID of the disk to be saved + # @param image_name [String] Name for the new image where the + # disk will be saved + # @param image_type [String] Type of the new image. Set to empty string + # to use the default type + # @param hot [true|false] True to save the disk immediately, false will + # perform the operation when the VM shuts down + # + # @return [Integer, OpenNebula::Error] the new Image ID in case of + # success, error otherwise + def disk_snapshot(disk_id, image_name, image_type="", hot=false) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(VM_METHODS[:savedisk], + @pe_id, + disk_id, + image_name, + image_type, + hot) + + return rc + end + + # @deprecated use {#disk_snapshot} + def save_as(disk_id, image_name, image_type="", hot=false) + return disk_snapshot(disk_id, image_name, image_type, hot) + end + + # Resize the VM + # + # @param capacity_template [String] Template containing the new capacity + # elements CPU, VCPU, MEMORY. If one of them is not present, or its + # value is 0, it will not be resized + # @param enforce [true|false] If it is set to true, the host capacity + # will be checked. This will only affect oneadmin requests, regular users + # resize requests will always be enforced + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def resize(capacity_template, enforce) + return call(VM_METHODS[:resize], @pe_id, capacity_template, enforce) + end + + # Changes the owner/group + # uid:: _Integer_ the new owner id. Set to -1 to leave the current one + # gid:: _Integer_ the new group id. Set to -1 to leave the current one + # [return] nil in case of success or an Error object + def chown(uid, gid) + super(VM_METHODS[:chown], uid, gid) + end + + # Changes the permissions. + # + # @param octet [String] Permissions octed , e.g. 640 + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chmod_octet(octet) + super(VM_METHODS[:chmod], octet) + end + + # Changes the permissions. + # Each [Integer] argument must be 1 to allow, 0 deny, -1 do not change + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chmod(owner_u, owner_m, owner_a, group_u, group_m, group_a, other_u, + other_m, other_a) + super(VM_METHODS[:chmod], owner_u, owner_m, owner_a, group_u, + group_m, group_a, other_u, other_m, other_a) + end + + # Retrieves this VM's monitoring data from OpenNebula + # + # @param [Array] xpath_expressions Elements to retrieve. + # + # @return [Hash>>, OpenNebula::Error] Hash with + # the requested xpath expressions, and an Array of 'timestamp, value'. + # + # @example + # vm.monitoring( ['CPU', 'NET_TX', 'TEMPLATE/CUSTOM_PROBE'] ) + # + # { "NET_TX" => + # [["1337264510", "210"], + # ["1337264553", "220"], + # ["1337264584", "230"]], + # "TEMPLATE/CUSTOM_PROBE" => + # [], + # "CPU" => + # [["1337264510", "0"], + # ["1337264553", "0"], + # ["1337264584", "0"]] + # } + def monitoring(xpath_expressions) + return super(VM_METHODS[:monitoring], 'VM', + 'LAST_POLL', xpath_expressions) + end + + # Retrieves this VM's monitoring data from OpenNebula, in XML + # + # @return [String] VM monitoring data, in XML + def monitoring_xml() + return Error.new('ID not defined') if !@pe_id + + return @client.call(VM_METHODS[:monitoring], @pe_id) + end + + # Renames this VM + # + # @param name [String] New name for the VM. + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def rename(name) + return call(VM_METHODS[:rename], @pe_id, name) + end + + # Creates a new VM snapshot + # + # @param name [String] Name for the snapshot. + # + # @return [Integer, OpenNebula::Error] The new snaphost ID in case + # of success, Error otherwise + def snapshot_create(name="") + return Error.new('ID not defined') if !@pe_id + + name ||= "" + return @client.call(VM_METHODS[:snapshotcreate], @pe_id, name) + end + + # Reverts to a snapshot + # + # @param snap_id [Integer] Id of the snapshot + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def snapshot_revert(snap_id) + return call(VM_METHODS[:snapshotrevert], @pe_id, snap_id) + end + + # Deletes a VM snapshot + # + # @param snap_id [Integer] Id of the snapshot + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def snapshot_delete(snap_id) + return call(VM_METHODS[:snapshotdelete], @pe_id, snap_id) + end + + # Recovers an ACTIVE VM + # + # @param result [Boolean] Recover with success (true) or failure (false) + # @param result [info] Additional information needed to recover the VM + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def recover(result) + return call(VM_METHODS[:recover], @pe_id, result) + end + + ######################################################################## + # Helpers to get VirtualMachine information + ######################################################################## + + # Returns the VM state of the VirtualMachine (numeric value) + def state + self['STATE'].to_i + end + + # Returns the VM state of the VirtualMachine (string value) + def state_str + VM_STATE[state] + end + + # Returns the LCM state of the VirtualMachine (numeric value) + def lcm_state + self['LCM_STATE'].to_i + end + + # Returns the LCM state of the VirtualMachine (string value) + def lcm_state_str + LCM_STATE[lcm_state] + end + + # Returns the short status string for the VirtualMachine + def status + short_state_str=SHORT_VM_STATES[state_str] + + if short_state_str=="actv" + short_state_str=SHORT_LCM_STATES[lcm_state_str] + end + + short_state_str + end + + # Returns the group identifier + # [return] _Integer_ the element's group ID + def gid + self['GID'].to_i + end + + private + def action(name) + return Error.new('ID not defined') if !@pe_id + + rc = @client.call(VM_METHODS[:action], name, @pe_id) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/virtual_machine_pool.rb b/ruby-opennebula-4.4/lib/opennebula/virtual_machine_pool.rb new file mode 100644 index 0000000..2932bf3 --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/virtual_machine_pool.rb @@ -0,0 +1,323 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +require 'opennebula/pool' + +module OpenNebula + class VirtualMachinePool < Pool + ####################################################################### + # Constants and Class attribute accessors + ####################################################################### + + + VM_POOL_METHODS = { + :info => "vmpool.info", + :monitoring => "vmpool.monitoring", + :accounting => "vmpool.accounting" + } + + # Constants for info queries (include/RequestManagerPoolInfoFilter.h) + INFO_NOT_DONE = -1 + INFO_ALL_VM = -2 + + ####################################################################### + # Class constructor & Pool Methods + ####################################################################### + + + # +client+ a Client object that represents a XML-RPC connection + # +user_id+ is to refer to a Pool with VirtualMachines from that user + def initialize(client, user_id=0) + super('VM_POOL','VM',client) + + @user_id = user_id + end + + # Default Factory Method for the Pools + def factory(element_xml) + OpenNebula::VirtualMachine.new(element_xml,@client) + end + + ####################################################################### + # XML-RPC Methods for the Virtual Network Object + ####################################################################### + + # Retrieves all or part of the VirtualMachines in the pool. + # No arguments, returns the not-in-done VMs for the user + # [user_id, start_id, end_id] + # [user_id, start_id, end_id, state] + def info(*args) + case args.size + when 0 + info_filter(VM_POOL_METHODS[:info], + @user_id, + -1, + -1, + INFO_NOT_DONE) + when 1 + info_filter(VM_POOL_METHODS[:info], + args[0], + -1, + -1, + INFO_NOT_DONE) + when 3 + info_filter(VM_POOL_METHODS[:info], + args[0], + args[1], + args[2], + INFO_NOT_DONE) + when 4 + info_filter(VM_POOL_METHODS[:info], + args[0], + args[1], + args[2], + args[3]) + end + end + + def info_all() + return info_filter(VM_POOL_METHODS[:info], + INFO_ALL, + -1, + -1, + INFO_NOT_DONE) + end + + def info_mine() + return info_filter(VM_POOL_METHODS[:info], + INFO_MINE, + -1, + -1, + INFO_NOT_DONE) + end + + def info_group() + return info_filter(VM_POOL_METHODS[:info], + INFO_GROUP, + -1, + -1, + INFO_NOT_DONE) + end + + alias_method :info!, :info + alias_method :info_all!, :info_all + alias_method :info_mine!, :info_mine + alias_method :info_group!, :info_group + + # Retrieves the monitoring data for all the VMs in the pool + # + # @param [Array] xpath_expressions Elements to retrieve. + # @param [Integer] filter_flag Optional filter flag to retrieve all or + # part of the Pool. Possible values: INFO_ALL, INFO_GROUP, INFO_MINE. + # + # @return [Hash>>>>, + # OpenNebula::Error] The first level hash uses the VM ID as keys, and + # as value a Hash with the requested xpath expressions, + # and an Array of 'timestamp, value'. + # + # @example + # vm_pool.monitoring( ['CPU', 'NET_TX', 'TEMPLATE/CUSTOM_PROBE'] ) + # + # {"1"=> + # {"CPU"=> + # [["1337608271", "0"], ["1337608301", "0"], ["1337608331", "0"]], + # "NET_TX"=> + # [["1337608271", "510"], ["1337608301", "510"], ["1337608331", "520"]], + # "TEMPLATE/CUSTOM_PROBE"=> + # []}, + # + # "0"=> + # {"CPU"=> + # [["1337608271", "0"], ["1337608301", "0"], ["1337608331", "0"]], + # "NET_TX"=> + # [["1337608271", "510"], ["1337608301", "510"], ["1337608331", "520"]], + # "TEMPLATE/CUSTOM_PROBE"=> + # []}} + def monitoring(xpath_expressions, filter_flag=INFO_ALL) + return super(VM_POOL_METHODS[:monitoring], + 'VM', 'LAST_POLL', xpath_expressions, filter_flag) + end + + # Retrieves the monitoring data for all the VMs in the pool, in XML + # + # @param [Integer] filter_flag Optional filter flag to retrieve all or + # part of the Pool. Possible values: INFO_ALL, INFO_GROUP, INFO_MINE. + # + # @return [String] VM monitoring data, in XML + def monitoring_xml(filter_flag=INFO_ALL) + return @client.call(VM_POOL_METHODS[:monitoring], filter_flag) + end + + # Retrieves the accounting data for all the VMs in the pool + # + # @param [Integer] filter_flag Optional filter flag to retrieve all or + # part of the Pool. Possible values: INFO_ALL, INFO_GROUP, INFO_MINE + # or user_id + # @param [Hash] options + # @option params [Integer] :start_time Start date and time to take into account, + # if no start_time is required use -1 + # @option params [Integer] :end_time End date and time to take into account, + # if no end_time is required use -1 + # @option params [Integer] :host Host id to filter the results + # @option params [Integer] :group Group id to filter the results + # @option params [String] :xpath Xpath expression to filter the results. + # For example: HISTORY[ETIME>0] + # @option params [String] :order_by_1 Xpath expression to group the + # @option params [String] :order_by_2 Xpath expression to group the + # returned hash. This will be the second level of the hash + # + # @return [Hash, OpenNebula::Error] + # The first level hash uses the :order_by_1 values as keys, and + # as value a Hash with the :order_by_2 values and the HISTORY_RECORDS + # + # @example + # {"HISTORY_RECORDS"=> + # {"HISTORY"=> [ + # {"OID"=>"0", + # "SEQ"=>"0", + # "HOSTNAME"=>"dummy", + # ... + # }, + # {"OID"=>"0", + # "SEQ"=>"0", + # "HOSTNAME"=>"dummy", + # + # @example :order_by_1 => VM/UID + # {"0"=> + # {"HISTORY_RECORDS"=> + # {"HISTORY"=> [ + # {"OID"=>"0", + # "SEQ"=>"0", + # "HOSTNAME"=>"dummy", + # ... + # }, + # {"OID"=>"0", + # "SEQ"=>"0", + # "HOSTNAME"=>"dummy", + # + # @example :order_by_1 => VM/UID, :order_by_2 => VM/ID + # {"0"=> + # {"0"=> + # {"HISTORY_RECORDS"=> + # {"HISTORY"=> [ + # {"OID"=>"0", + # "SEQ"=>"0", + # "HOSTNAME"=>"dummy", + # ... + # }, + # {"OID"=>"0", + # "SEQ"=>"0", + # "HOSTNAME"=>"dummy", + # + def accounting(filter_flag=INFO_ALL, options={}) + acct_hash = Hash.new + + rc = build_accounting(filter_flag, options) do |history| + hash = acct_hash + + if options[:order_by_1] + id_1 = history[options[:order_by_1]] + acct_hash[id_1] ||= Hash.new + + if options[:order_by_2] + id_2 = history[options[:order_by_2]] + acct_hash[id_1][id_2] ||= Hash.new + + hash = acct_hash[id_1][id_2] + else + hash = acct_hash[id_1] + end + end + + hash["HISTORY_RECORDS"] ||= Hash.new + hash["HISTORY_RECORDS"]["HISTORY"] ||= Array.new + hash["HISTORY_RECORDS"]["HISTORY"] << history.to_hash['HISTORY'] + end + + return rc if OpenNebula.is_error?(rc) + + acct_hash + end + + # Retrieves the accounting data for all the VMs in the pool in xml + # + # @param [Integer] filter_flag Optional filter flag to retrieve all or + # part of the Pool. Possible values: INFO_ALL, INFO_GROUP, INFO_MINE + # or user_id + # @param [Hash] options + # @option params [Integer] :start_time Start date and time to take into account, + # if no start_time is required use -1 + # @option params [Integer] :end_time End date and time to take into account, + # if no end_time is required use -1 + # @option params [Integer] :host Host id to filter the results + # @option params [Integer] :group Group id to filter the results + # @option params [String] :xpath Xpath expression to filter the results. + # For example: HISTORY[ETIME>0] + # + # @return [String] the xml representing the accounting data + def accounting_xml(filter_flag=INFO_ALL, options={}) + acct_hash = Hash.new + xml_str = "\n" + + rc = build_accounting(filter_flag, options) do |history| + xml_str << history.to_xml + end + + return rc if OpenNebula.is_error?(rc) + + xml_str << "\n" + xml_str + end + + private + + def build_accounting(filter_flag, options, &block) + xml_str = @client.call(VM_POOL_METHODS[:accounting], + filter_flag, + options[:start_time], + options[:end_time]) + + return xml_str if OpenNebula.is_error?(xml_str) + + xmldoc = XMLElement.new + xmldoc.initialize_xml(xml_str, 'HISTORY_RECORDS') + + xpath_array = Array.new + xpath_array << "HISTORY[HID=#{options[:host]}]" if options[:host] + xpath_array << "HISTORY[VM/GID=#{options[:group]}]" if options[:group] + xpath_array << options[:xpath] if options[:xpath] + + if xpath_array.empty? + xpath_str = "HISTORY" + else + xpath_str = xpath_array.join(' | ') + end + + acct_hash = Hash.new + + xmldoc.each(xpath_str) do |history| + block.call(history) + end + + acct_hash + end + + def info_filter(xml_method, who, start_id, end_id, state) + return xmlrpc_info(xml_method, who, start_id, end_id, state) + end + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/virtual_network.rb b/ruby-opennebula-4.4/lib/opennebula/virtual_network.rb new file mode 100644 index 0000000..1ac2b66 --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/virtual_network.rb @@ -0,0 +1,253 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +require 'opennebula/pool_element' + +module OpenNebula + class VirtualNetwork < PoolElement + ####################################################################### + # Constants and Class Methods + ####################################################################### + + + VN_METHODS = { + :info => "vn.info", + :allocate => "vn.allocate", + :delete => "vn.delete", + :addleases => "vn.addleases", + :rmleases => "vn.rmleases", + :chown => "vn.chown", + :chmod => "vn.chmod", + :update => "vn.update", + :hold => "vn.hold", + :release => "vn.release", + :rename => "vn.rename" + } + + VN_TYPES=%w{RANGED FIXED} + + SHORT_VN_TYPES={ + "RANGED" => "R", + "FIXED" => "F" + } + + # Creates a VirtualNetwork description with just its identifier + # this method should be used to create plain VirtualNetwork objects. + # +id+ the id of the network + # + # Example: + # vnet = VirtualNetwork.new(VirtualNetwork.build_xml(3),rpc_client) + # + def VirtualNetwork.build_xml(pe_id=nil) + if pe_id + vn_xml = "#{pe_id}" + else + vn_xml = "" + end + + XMLElement.build_xml(vn_xml, 'VNET') + end + + # Class constructor + def initialize(xml, client) + super(xml,client) + end + + ####################################################################### + # XML-RPC Methods for the Virtual Network Object + ####################################################################### + + # Retrieves the information of the given VirtualNetwork. + def info() + super(VN_METHODS[:info], 'VNET') + end + + alias_method :info!, :info + + # Allocates a new VirtualNetwork in OpenNebula + # + # @param description [String] The template of the VirtualNetwork. + # @param cluster_id [Integer] Id of the cluster + # + # @return [Integer, OpenNebula::Error] the new ID in case of + # success, error otherwise + def allocate(description,cluster_id=ClusterPool::NONE_CLUSTER_ID) + super(VN_METHODS[:allocate], description, cluster_id) + end + + # Replaces the template contents + # + # @param new_template [String] New template contents + # @param append [true, false] True to append new attributes instead of + # replace the whole template + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def update(new_template=nil, append=false) + super(VN_METHODS[:update], new_template, append ? 1 : 0) + end + + # Publishes the VirtualNetwork, to be used by other users + def publish + set_publish(true) + end + + # Unplubishes the VirtualNetwork + def unpublish + set_publish(false) + end + + # Deletes the VirtualNetwork + def delete() + super(VN_METHODS[:delete]) + end + + # Adds a lease to the VirtualNetwork + def addleases(ip, mac = nil) + return Error.new('ID not defined') if !@pe_id + + lease_template = "LEASES = [ IP = #{ip}" + lease_template << ", MAC = #{mac}" if mac + lease_template << " ]" + + rc = @client.call(VN_METHODS[:addleases], @pe_id, lease_template) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + + # Removes a lease from the VirtualNetwork + def rmleases(ip) + return Error.new('ID not defined') if !@pe_id + + lease_template = "LEASES = [ IP = #{ip} ]" + + rc = @client.call(VN_METHODS[:rmleases], @pe_id, lease_template) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + + # Holds a virtual network Lease as used + # @param ip [String] IP to hold + def hold(ip) + return Error.new('ID not defined') if !@pe_id + + lease_template = "LEASES = [ IP = #{ip} ]" + + rc = @client.call(VN_METHODS[:hold], @pe_id, lease_template) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + + # Releases a virtual network Lease on hold + # @param ip [String] IP to release + def release(ip) + return Error.new('ID not defined') if !@pe_id + + lease_template = "LEASES = [ IP = #{ip} ]" + + rc = @client.call(VN_METHODS[:release], @pe_id, lease_template) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + + # Changes the owner/group + # + # @param uid [Integer] the new owner id. Set to -1 to leave the current one + # @param gid [Integer] the new group id. Set to -1 to leave the current one + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chown(uid, gid) + super(VN_METHODS[:chown], uid, gid) + end + + # Changes the virtual network permissions. + # + # @param octet [String] Permissions octed , e.g. 640 + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chmod_octet(octet) + super(VN_METHODS[:chmod], octet) + end + + # Changes the virtual network permissions. + # Each [Integer] argument must be 1 to allow, 0 deny, -1 do not change + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def chmod(owner_u, owner_m, owner_a, group_u, group_m, group_a, other_u, + other_m, other_a) + super(VN_METHODS[:chmod], owner_u, owner_m, owner_a, group_u, + group_m, group_a, other_u, other_m, other_a) + end + + # Renames this virtual network + # + # @param name [String] New name for the virtual network. + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def rename(name) + return call(VN_METHODS[:rename], @pe_id, name) + end + + ####################################################################### + # Helpers to get VirtualNetwork information + ####################################################################### + + # Returns the group identifier + # [return] _Integer_ the element's group ID + def gid + self['GID'].to_i + end + + # Returns the type of the Virtual Network (numeric value) + def type + self['TYPE'].to_i + end + + # Returns the type of the Virtual Network (string value) + def type_str + VN_TYPES[type] + end + + # Returns the state of the Virtual Network (string value) + def short_type_str + SHORT_VN_TYPES[type_str] + end + + def public? + if self['PERMISSIONS/GROUP_U'] == "1" || self['PERMISSIONS/OTHER_U'] == "1" + true + else + false + end + end + + private + def set_publish(published) + group_u = published ? 1 : 0 + + chmod(-1, -1, -1, group_u, -1, -1, -1, -1, -1) + end + + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/virtual_network_pool.rb b/ruby-opennebula-4.4/lib/opennebula/virtual_network_pool.rb new file mode 100644 index 0000000..38a8acc --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/virtual_network_pool.rb @@ -0,0 +1,79 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +require 'opennebula/pool' + +module OpenNebula + class VirtualNetworkPool < Pool + ####################################################################### + # Constants and Class attribute accessors + ####################################################################### + + + VN_POOL_METHODS = { + :info => "vnpool.info" + } + + ####################################################################### + # Class constructor & Pool Methods + ####################################################################### + + # +client+ a Client object that represents a XML-RPC connection + # +user_id+ is to refer to a Pool with VirtualNetworks from that user + def initialize(client, user_id=0) + super('VNET_POOL','VNET',client) + + @user_id = user_id + end + + # Default Factory Method for the Pools + def factory(element_xml) + OpenNebula::VirtualNetwork.new(element_xml,@client) + end + + ####################################################################### + # XML-RPC Methods for the Virtual Network Object + ####################################################################### + + # Retrieves all or part of the VirtualMachines in the pool. + def info(*args) + case args.size + when 0 + info_filter(VN_POOL_METHODS[:info],@user_id,-1,-1) + when 3 + info_filter(VN_POOL_METHODS[:info],args[0],args[1],args[2]) + end + end + + def info_all() + return super(VN_POOL_METHODS[:info]) + end + + def info_mine() + return super(VN_POOL_METHODS[:info]) + end + + def info_group() + return super(VN_POOL_METHODS[:info]) + end + + alias_method :info!, :info + alias_method :info_all!, :info_all + alias_method :info_mine!, :info_mine + alias_method :info_group!, :info_group + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/x509_auth.rb b/ruby-opennebula-4.4/lib/opennebula/x509_auth.rb new file mode 100644 index 0000000..24ff82d --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/x509_auth.rb @@ -0,0 +1,288 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +require 'openssl' +require 'base64' +require 'fileutils' +require 'yaml' + +module OpenNebula; end + +# X509 authentication class. It can be used as a driver for auth_mad +# as auth method is defined. It also holds some helper methods to be used +# by oneauth command +class OpenNebula::X509Auth + ########################################################################### + #Constants with paths to relevant files and defaults + ########################################################################### + if !ENV["ONE_LOCATION"] + ETC_LOCATION = "/etc/one" + else + ETC_LOCATION = ENV["ONE_LOCATION"] + "/etc" + end + + LOGIN_PATH = ENV['HOME']+'/.one/one_x509' + + X509_AUTH_CONF_PATH = ETC_LOCATION + "/auth/x509_auth.conf" + + X509_DEFAULTS = { + :ca_dir => ETC_LOCATION + "/auth/certificates" + } + + def self.escape_dn(dn) + dn.gsub(/\s/) { |s| "\\"+s[0].ord.to_s(16) } + end + + def self.unescape_dn(dn) + dn.gsub(/\\[0-9a-f]{2}/) { |s| s[1,2].to_i(16).chr } + end + + ########################################################################### + # Initialize x509Auth object + # + # @param [Hash] default options for path + # @option options [String] :certs_pem + # cert chain array in colon-separated pem format + # @option options [String] :key_pem + # key in pem format + # @option options [String] :ca_dir + # directory of trusted CA's. Needed for auth method, not for login. + def initialize(options={}) + @options ||= X509_DEFAULTS + @options.merge!(options) + + load_options(X509_AUTH_CONF_PATH) + + @cert_chain = @options[:certs_pem].collect do |cert_pem| + OpenSSL::X509::Certificate.new(cert_pem) + end + + if @options[:key_pem] + @key = OpenSSL::PKey::RSA.new(@options[:key_pem]) + end + end + + ########################################################################### + # Client side + ########################################################################### + + # Creates the login file for x509 authentication at ~/.one/one_x509. + # By default it is valid as long as the certificate is valid. It can + # be changed to any number of seconds with expire parameter (sec.) + def login(user, expire=0) + write_login(login_token(user,expire)) + end + + # Returns a valid password string to create a user using this auth driver. + # In this case the dn of the user certificate. + def password + self.class.escape_dn(@cert_chain[0].subject.to_s) + end + + # Generates a login token in the form: + # user_name:x509:user_name:time_expires:cert_chain + # - user_name:time_expires is encrypted with the user certificate + # - user_name:time_expires:cert_chain is base64 encoded + def login_token(user, expire) + if expire != 0 + expires = Time.now.to_i + expire.to_i + else + expires = @cert_chain[0].not_after.to_i + end + + text_to_sign = "#{user}:#{expires}" + signed_text = encrypt(text_to_sign) + + certs_pem = @cert_chain.collect{|cert| cert.to_pem}.join(":") + + token = "#{signed_text}:#{certs_pem}" + token64 = Base64::encode64(token).strip.delete("\n") + + login_out = "#{user}:#{token64}" + + login_out + end + + ########################################################################### + # Server side + ########################################################################### + # auth method for auth_mad + def authenticate(user, pass, signed_text) + begin + # Decryption demonstrates that the user posessed the private key. + _user, expires = decrypt(signed_text).split(':') + + return "User name missmatch" if user != _user + + return "x509 proxy expired" if Time.now.to_i >= expires.to_i + + # Some DN in the chain must match a DN in the password + dn_ok = @cert_chain.each do |cert| + if pass.split('|').include?( + self.class.escape_dn(cert.subject.to_s)) + break true + end + end + + unless dn_ok == true + return "Certificate subject missmatch" + end + + validate + + return true + rescue => e + return e.message + end + end + +private + # Writes a login_txt to the login file as defined in LOGIN_PATH + # constant + def write_login(login_txt) + # Inits login file path and creates ~/.one directory if needed + # Set instance variables + login_dir = File.dirname(LOGIN_PATH) + + begin + FileUtils.mkdir_p(login_dir) + rescue Errno::EEXIST + end + + file = File.open(LOGIN_PATH, "w") + file.write(login_txt) + file.close + + File.chmod(0600,LOGIN_PATH) + end + + # Load class options form a configuration file (yaml syntax) + def load_options(conf_file) + if File.readable?(conf_file) + conf_txt = File.read(conf_file) + conf_opt = YAML::load(conf_txt) + + @options.merge!(conf_opt) if conf_opt != false + end + end + + ########################################################################### + # Methods to encrpyt/decrypt keys + ########################################################################### + # Encrypts data with the private key of the user and returns + # base 64 encoded output in a single line + def encrypt(data) + return nil if !@key + Base64::encode64(@key.private_encrypt(data)).delete("\n").strip + end + + # Decrypts base 64 encoded data with pub_key (public key) + def decrypt(data) + @cert_chain[0].public_key.public_decrypt(Base64::decode64(data)) + end + + ########################################################################### + # Validate the user certificate + ########################################################################### + def validate + now = Time.now + + # Check start time and end time of certificates + @cert_chain.each do |cert| + if cert.not_before > now || cert.not_after < now + raise failed + "Certificate not valid. Current time is " + + now.localtime.to_s + "." + end + end + + begin + # Validate the proxy certifcates + signee = @cert_chain[0] + + check_crl(signee) + + @cert_chain[1..-1].each do |cert| + if !((signee.issuer.to_s == cert.subject.to_s) && + (signee.verify(cert.public_key))) + raise failed + signee.subject.to_s + " with issuer " + + signee.issuer.to_s + " was not verified by " + + cert.subject.to_s + "." + end + signee = cert + end + + # Validate the End Entity certificate + if !@options[:ca_dir] + raise failed + "No certifcate authority directory was specified." + end + + begin + ca_hash = signee.issuer.hash.to_s(16) + ca_path = @options[:ca_dir] + '/' + ca_hash + '.0' + + ca_cert = OpenSSL::X509::Certificate.new(File.read(ca_path)) + + if !((signee.issuer.to_s == ca_cert.subject.to_s) && + (signee.verify(ca_cert.public_key))) + raise failed + signee.subject.to_s + " with issuer " + + signee.issuer.to_s + " was not verified by " + + ca_cert.subject.to_s + "." + end + + signee = ca_cert + end while ca_cert.subject.to_s != ca_cert.issuer.to_s + rescue + raise + end + end + + def check_crl(signee) + failed = "Could not validate user credentials: " + + ca_hash = signee.issuer.hash.to_s(16) + ca_path = @options[:ca_dir] + '/' + ca_hash + '.0' + + crl_path = @options[:ca_dir] + '/' + ca_hash + '.r0' + + if !File.exist?(crl_path) + if @options[:check_crl] + raise failed + "CRL file #{crl_path} does not exist" + else + return + end + end + + ca_cert = OpenSSL::X509::Certificate.new( File.read(ca_path) ) + crl_cert = OpenSSL::X509::CRL.new( File.read(crl_path) ) + + # First verify the CRL itself with its signer + unless crl_cert.verify( ca_cert.public_key ) then + raise failed + "CRL is not verified by its Signer" + end + + # Extract the list of revoked certificates from the CRL + rc_array = crl_cert.revoked + + # Loop over the list and compare with the target personal + # certificate + rc_array.each do |e| + if e.serial.eql?(signee.serial) then + raise failed + "#{signee.subject.to_s} is found in the "<< + "CRL, i.e. it is revoked" + end + end + end +end diff --git a/ruby-opennebula-4.4/lib/opennebula/xml_element.rb b/ruby-opennebula-4.4/lib/opennebula/xml_element.rb new file mode 100644 index 0000000..a39f566 --- /dev/null +++ b/ruby-opennebula-4.4/lib/opennebula/xml_element.rb @@ -0,0 +1,427 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + + +module OpenNebula + # The XMLElement class provides an abstraction of the underlying + # XML parser engine. It provides XML-related methods for the Pool and + # PoolElement classes + class XMLElement + + # xml:: _opaque xml object_ an xml object as returned by build_xml + def initialize(xml=nil) + @xml = xml + end + + # Initialize a XML document for the element + # xml:: _String_ the XML document of the object + # root_element:: _String_ Base xml element + def initialize_xml(xml, root_element) + @xml = XMLElement.build_xml(xml, root_element) + + if OpenNebula.is_error?(@xml) + @xml = nil + else + if NOKOGIRI + if @xml.size == 0 + @xml = nil + end + else + if @xml.name != root_element + @xml = nil + end + end + end + end + + # Builds a XML document + # xml:: _String_ the XML document of the object + # root_element:: _String_ Base xml element + # [return] _XML_ object for the underlying XML engine + def self.build_xml(xml, root_element) + begin + if NOKOGIRI + doc = Nokogiri::XML(xml).xpath("/#{root_element}") + else + doc = REXML::Document.new(xml).root + end + rescue Exception => e + return OpenNebula::Error.new(e.message) + end + + return doc + end + + # Extract a text element from the XML description of the PoolElement. + # + # @param [String] key Xpath expression + # + # @return [String, nil] If a text element is found, the element's + # text value. Otherwise, an empty string or nil, depending + # on the backend + # + # @example + # vm['VID'] # gets VM id + # vm['HISTORY/HOSTNAME'] # get the hostname from the history + def [](key) + if NOKOGIRI + element=@xml.xpath(key.to_s) + + return nil if element.size == 0 + else + element=@xml.elements[key.to_s] + + return "" if element && !element.has_text? + end + + element.text if element + end + + # Delete an element from the xml + # xpath::_String_ xpath expression that selects the elemnts to be deleted + def delete_element(xpath) + if NOKOGIRI + @xml.xpath(xpath.to_s).remove + else + @xml.delete_element(xpath.to_s) + end + end + + # Add a new element to the xml + # xpath::_String_ xpath xpression where the elemente will be added + # elems::_Hash_ Hash containing the pairs key-value to be included + # Examples: + # add_element('VM', 'NEW_ITEM' => 'NEW_VALUE') + # NEW_VALUE... + # + # add_element('VM/TEMPLATE', 'V1' => {'X1' => 'A1', 'Y2' => 'A2'}) + # + def add_element(xpath, elems) + elems.each { |key, value| + if value.instance_of?(Hash) + if NOKOGIRI + elem = Nokogiri::XML::Node.new key, @xml.document + value.each { |k2, v2| + child = Nokogiri::XML::Node.new k2, elem + child.content = v2 + elem.add_child(child) + } + @xml.xpath(xpath.to_s).first.add_child(elem) + else + elem = REXML::Element.new(key) + value.each { |k2, v2| + elem.add_element(k2).text = v2 + } + @xml.elements[xpath].add_element(elem) + end + else + if NOKOGIRI + elem = Nokogiri::XML::Node.new key, @xml.document + elem.content = value + @xml.xpath(xpath.to_s).first.add_child(elem) + else + @xml.elements[xpath].add_element(key).text = value + end + end + } + end + + # Gets an array of text from elemenets extracted + # using the XPATH expression passed as filter + def retrieve_elements(filter) + elements_array = Array.new + + if NOKOGIRI + @xml.xpath(filter.to_s).each { |pelem| + elements_array << pelem.text if pelem.text + } + else + @xml.elements.each(filter.to_s) { |pelem| + elements_array << pelem.text if pelem.text + } + end + + if elements_array.size == 0 + return nil + else + return elements_array + end + + end + + # Gets an attribute from an elemenT + # key:: _String_ xpath for the element + # name:: _String_ name of the attribute + def attr(key,name) + value = nil + + if NOKOGIRI + element=@xml.xpath(key.to_s.upcase) + if element.size == 0 + return nil + end + + attribute = element.attr(name) + + value = attribute.text if attribute != nil + else + element=@xml.elements[key.to_s.upcase] + + value = element.attributes[name] if element != nil + end + + return value + end + + # Iterates over every Element in the XPath and calls the block with a + # a XMLElement + # block:: _Block_ + def each(xpath_str,&block) + if NOKOGIRI + @xml.xpath(xpath_str).each { |pelem| + block.call XMLElement.new(pelem) + } + else + @xml.elements.each(xpath_str) { |pelem| + block.call XMLElement.new(pelem) + } + end + end + + def each_xpath(xpath_str,&block) + if NOKOGIRI + @xml.xpath(xpath_str).each { |pelem| + block.call pelem.text + } + else + @xml.elements.each(xpath_str) { |pelem| + block.call pelem.text + } + end + end + + def name + @xml.name + end + + def text + if NOKOGIRI + @xml.content + else + @xml.text + end + end + + # Returns wheter there are elements for a given XPath + # xpath_str:: _String_ XPath expression to locate the element + def has_elements?(xpath_str) + if NOKOGIRI + element = @xml.xpath(xpath_str.to_s.upcase) + return element != nil && element.children.size > 0 + else + element = @xml.elements[xpath_str.to_s] + return element != nil && element.has_elements? + end + end + + # Returns the