--- /dev/null
+
+ 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.
--- /dev/null
+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
--- /dev/null
+ruby-opennebula (4.4.0-1) UNRELEASED; urgency=medium
+
+ * Initial release (Closes: #nnnn)
+
+ -- František Dvořák <valtri@civ.zcu.cz> Wed, 12 Mar 2014 18:30:49 +0100
--- /dev/null
+Source: ruby-opennebula
+Section: ruby
+Priority: optional
+Maintainer: Debian Ruby Extras Maintainers <pkg-ruby-extras-maintainers@lists.alioth.debian.org>
+Uploaders: František Dvořák <valtri@civ.zcu.cz>
+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
--- /dev/null
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: opennebula
+Source: FIXME <http://example.com/>
+
+Files: *
+Copyright: <years> <put author's name and email here>
+ <years> <likewise for another author>
+License: GPL-2+ (FIXME)
+
+Files: debian/*
+Copyright: 2014 František Dvořák <valtri@civ.zcu.cz>
+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'.
--- /dev/null
+# FIXME: READMEs found
--- /dev/null
+#!/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
--- /dev/null
+3.0 (quilt)
--- /dev/null
+version=3
+http://pkg-ruby-extras.alioth.debian.org/cgi-bin/gemwatch/opennebula .*/opennebula-(.*).tar.gz
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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 -> #<num>
+ # @<num>
+ # ALL
+ # RESOURCE -> + separated list and "/{#,@,%}<num>|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 = "<ACL><ID>#{pe_id}</ID></ACL>"
+ else
+ acl_xml = "<ACL></ACL>"
+ 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 [#<id>, @<id>, *] 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
+ # (#<id>), group (@<id>) 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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 = "<CLUSTER><ID>#{pe_id}</ID></CLUSTER>"
+ else
+ cluster_xml = "<CLUSTER></CLUSTER>"
+ 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<Integer>]
+ 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<Integer>]
+ 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<Integer>]
+ 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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 = "<DATASTORE><ID>#{pe_id}</ID></DATASTORE>"
+ else
+ datastore_xml = "<DATASTORE></DATASTORE>"
+ 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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 = "<DOCUMENT><ID>#{pe_id}</ID></DOCUMENT>"
+ else
+ obj_xml = "<DOCUMENT></DOCUMENT>"
+ 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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 = "<TEMPLATE>"
+
+ text << "<NAME>#{name}</NAME>" if name
+
+ text << "<#{TEMPLATE_TAG}>"
+ text << "<![CDATA[#{template_json}]]>"
+ text << "</#{TEMPLATE_TAG}>"
+
+ text << "</TEMPLATE>"
+
+ text
+ end
+ end
+end
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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 = "<GROUP><ID>#{pe_id}</ID></GROUP>"
+ else
+ group_xml = "<GROUP></GROUP>"
+ 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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 = "<HOST><ID>#{pe_id}</ID></HOST>"
+ else
+ host_xml = "<HOST></HOST>"
+ 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<String>] xpath_expressions Elements to retrieve.
+ #
+ # @return [Hash<String, Array<Array<int>>>, 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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<String>] xpath_expressions Elements to retrieve.
+ #
+ # @return [Hash<String, <Hash<String, Array<Array<int>>>>>,
+ # 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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 = "<IMAGE><ID>#{pe_id}</ID></IMAGE>"
+ else
+ image_xml = "<IMAGE></IMAGE>"
+ 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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
--- /dev/null
+# ---------------------------------------------------------------------------- #
+# 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
+
--- /dev/null
+# ---------------------------------------------------------------------------- #
+# 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
+
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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<String>] xpath_expressions Elements to retrieve.
+ # @param args arguemnts for the xml_method call
+ #
+ # @return [Hash<String, <Hash<String, Array<Array<int>>>>>,
+ # 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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<String>] Xpath expressions for the
+ # elements to retrieve.
+ #
+ # @return [Hash<String, Array<Array<int>>, 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<String>] xpath_expressions Elements to retrieve.
+ #
+ # @return [Hash<String, Array<Array<int>>, 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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 (<user>:<passwd>)"
+ 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
+
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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 = "<VMTEMPLATE><ID>#{pe_id}</ID></VMTEMPLATE>"
+ else
+ obj_xml = "<VMTEMPLATE></VMTEMPLATE>"
+ 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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 = "<USER><ID>#{pe_id}</ID></USER>"
+ else
+ user_xml = "<USER></USER>"
+ 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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 = "<VM><ID>#{pe_id}</ID></VM>"
+ else
+ vm_xml = "<VM></VM>"
+ 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 <USER_TEMPLATE> 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 <USER_TEMPLATE> 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<String>] xpath_expressions Elements to retrieve.
+ #
+ # @return [Hash<String, Array<Array<int>>>, 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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<String>] 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<String, <Hash<String, Array<Array<int>>>>>,
+ # 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 = "<HISTORY_RECORDS>\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</HISTORY_RECORDS>"
+ 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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 = "<VNET><ID>#{pe_id}</ID></VNET>"
+ else
+ vn_xml = "<VNET></VNET>"
+ 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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')
+ # <VM><NEW_ITEM>NEW_VALUE</NEW_ITEM>...</VM>
+ #
+ # add_element('VM/TEMPLATE', 'V1' => {'X1' => 'A1', 'Y2' => 'A2'})
+ # <VM><TEMPLATE><V1><X1>A1</X1><Y2>A2</Y2>...</TEMPLATE></VM>
+ 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 <TEMPLATE> element in text form
+ # indent:: _Boolean_ indents the resulting string, default true
+ def template_str(indent=true)
+ template_like_str('TEMPLATE', indent)
+ end
+
+ # Returns the <TEMPLATE> element in XML form
+ def template_xml
+ if NOKOGIRI
+ @xml.xpath('TEMPLATE').to_s
+ else
+ @xml.elements['TEMPLATE'].to_s
+ end
+ end
+
+ # Returns the xml of an element
+ def element_xml(xpath)
+ if NOKOGIRI
+ @xml.xpath(xpath).to_s
+ else
+ @xml.elements[xpath].to_s
+ end
+ end
+
+ # Returns elements in text form
+ # root_element:: _String_ base element
+ # indent:: _Boolean_ indents the resulting string, default true
+ # xpath_exp:: _String_ filter elements with a XPath
+ def template_like_str(root_element, indent=true, xpath_exp=nil)
+ if NOKOGIRI
+ xml_template = @xml.xpath(root_element).to_s
+ rexml = REXML::Document.new(xml_template).root
+ else
+ rexml = @xml.elements[root_element]
+ end
+
+ if indent
+ ind_enter = "\n"
+ ind_tab = ' '
+ else
+ ind_enter = ''
+ ind_tab = ' '
+ end
+
+ str = rexml.elements.collect(xpath_exp) {|n|
+ next if n.class != REXML::Element
+
+ str_line = ""
+
+ if n.has_elements?
+ str_line << "#{n.name}=[#{ind_enter}" << n.collect { |n2|
+
+ next if n2.class != REXML::Element or !n2.has_text?
+
+ str = "#{ind_tab}#{n2.name}=#{attr_to_str(n2.text)}"
+
+ }.compact.join(",#{ind_enter}") << " ]"
+ else
+ next if !n.has_text?
+
+ str_line << "#{n.name}=#{attr_to_str(n.text)}"
+ end
+
+ str_line
+ }.compact.join("\n")
+
+ return str
+ end
+
+ #
+ #
+ #
+ def to_xml(pretty=false)
+ if NOKOGIRI && pretty
+ str = @xml.to_xml
+ elsif REXML_FORMATTERS && pretty
+ str = String.new
+
+ formatter = REXML::Formatters::Pretty.new
+ formatter.compact = true
+
+ formatter.write(@xml,str)
+ else
+ str = @xml.to_s
+ end
+
+ return str
+ end
+
+ # @return [Hash] a hash representing the resource
+ def to_hash
+ hash = {}
+
+ if NOKOGIRI
+ if @xml.instance_of?(Nokogiri::XML::NodeSet)
+ @xml.each { |c|
+ if c.element?
+ build_hash(hash, c)
+ end
+ }
+ else
+ build_hash(hash, @xml)
+ end
+ else
+ build_hash(hash, @xml)
+ end
+
+ hash
+ end
+
+ private
+
+ #
+ #
+ #
+ def build_hash(hash, element)
+ if NOKOGIRI
+ array = element.children
+ if array.length==1 and (array.first.text? or array.first.cdata?)
+ r = array.first.text
+ else
+ r = {}
+ array.each { |c|
+ if c.element?
+ build_hash(r, c)
+ end
+ }
+ end
+ else
+ r = {}
+ if element.has_elements?
+ element.each_element { |c| build_hash(r, c) }
+ elsif element.has_text?
+ r = element.text
+ end
+ end
+
+ key = element.name
+ if hash.has_key?(key)
+ if hash[key].instance_of?(Array)
+ hash[key] << r
+ else
+ hash[key] = [hash[key], r]
+ end
+ else
+ hash[key] = r
+ end
+
+ hash
+ end
+
+ #
+ #
+ #
+ def attr_to_str(attr)
+ attr.gsub!('"',"\\\"")
+ attr = "\"#{attr}\""
+
+ return attr
+ end
+ end
+
+ # The XMLUtilsPool module provides an abstraction of the underlying
+ # XML parser engine. It provides XML-related methods for the Pools
+ class XMLPool < XMLElement
+
+ def initialize(xml=nil)
+ super(xml)
+ end
+
+ #Executes the given block for each element of the Pool
+ #block:: _Block_
+ def each_element(block)
+ if NOKOGIRI
+ @xml.xpath(
+ "#{@element_name}").each {|pelem|
+ block.call self.factory(pelem)
+ }
+ else
+ @xml.elements.each(
+ "#{@element_name}") {|pelem|
+ block.call self.factory(pelem)
+ }
+ end
+ end
+ end
+
+end
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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_element'
+
+module OpenNebula
+ # The XMLUtilsPool module provides an abstraction of the underlying
+ # XML parser engine. It provides XML-related methods for the Pools
+ class XMLPool < XMLElement
+
+ def initialize(xml=nil)
+ super(xml)
+ end
+
+ #Executes the given block for each element of the Pool
+ #block:: _Block_
+ def each_element(block)
+ if NOKOGIRI
+ @xml.xpath(
+ "#{@element_name}").each {|pelem|
+ block.call self.factory(pelem)
+ }
+ else
+ @xml.elements.each(
+ "#{@element_name}") {|pelem|
+ block.call self.factory(pelem)
+ }
+ end
+ end
+ end
+
+end
--- /dev/null
+# -------------------------------------------------------------------------- #
+# 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_pool'
+
+module OpenNebula
+
+ begin
+ require 'nokogiri'
+ NOKOGIRI=true
+ rescue LoadError
+ NOKOGIRI=false
+ end
+
+ begin
+ require 'rexml/formatters/pretty'
+ REXML_FORMATTERS=true
+ rescue LoadError
+ REXML_FORMATTERS=false
+ end
+end
--- /dev/null
+--- !ruby/object:Gem::Specification
+name: opennebula
+version: !ruby/object:Gem::Version
+ version: 4.4.0
+platform: ruby
+authors:
+- OpenNebula
+autorequire:
+bindir: bin
+cert_chain: []
+date: 2013-12-02 00:00:00.000000000 Z
+dependencies:
+- !ruby/object:Gem::Dependency
+ name: nokogiri
+ requirement: !ruby/object:Gem::Requirement
+ requirements:
+ - - '>='
+ - !ruby/object:Gem::Version
+ version: '0'
+ type: :runtime
+ prerelease: false
+ version_requirements: !ruby/object:Gem::Requirement
+ requirements:
+ - - '>='
+ - !ruby/object:Gem::Version
+ version: '0'
+- !ruby/object:Gem::Dependency
+ name: json
+ requirement: !ruby/object:Gem::Requirement
+ requirements:
+ - - '>='
+ - !ruby/object:Gem::Version
+ version: '0'
+ type: :runtime
+ prerelease: false
+ version_requirements: !ruby/object:Gem::Requirement
+ requirements:
+ - - '>='
+ - !ruby/object:Gem::Version
+ version: '0'
+description: Libraries needed to talk to OpenNebula
+email: contact@opennebula.org
+executables: []
+extensions: []
+extra_rdoc_files: []
+files:
+- lib/opennebula.rb
+- lib/opennebula/acl.rb
+- lib/opennebula/acl_pool.rb
+- lib/opennebula/client.rb
+- lib/opennebula/cluster.rb
+- lib/opennebula/cluster_pool.rb
+- lib/opennebula/datastore.rb
+- lib/opennebula/datastore_pool.rb
+- lib/opennebula/document.rb
+- lib/opennebula/document_json.rb
+- lib/opennebula/document_pool.rb
+- lib/opennebula/document_pool_json.rb
+- lib/opennebula/error.rb
+- lib/opennebula/group.rb
+- lib/opennebula/group_pool.rb
+- lib/opennebula/host.rb
+- lib/opennebula/host_pool.rb
+- lib/opennebula/image.rb
+- lib/opennebula/image_pool.rb
+- lib/opennebula/oneflow_client.rb
+- lib/opennebula/pool.rb
+- lib/opennebula/pool_element.rb
+- lib/opennebula/system.rb
+- lib/opennebula/template.rb
+- lib/opennebula/template_pool.rb
+- lib/opennebula/user.rb
+- lib/opennebula/user_pool.rb
+- lib/opennebula/virtual_machine.rb
+- lib/opennebula/virtual_machine_pool.rb
+- lib/opennebula/virtual_network.rb
+- lib/opennebula/virtual_network_pool.rb
+- lib/opennebula/xml_element.rb
+- lib/opennebula/xml_pool.rb
+- lib/opennebula/xml_utils.rb
+- lib/opennebula/ldap_auth.rb
+- lib/opennebula/ldap_auth_spec.rb
+- lib/opennebula/server_cipher_auth.rb
+- lib/opennebula/server_x509_auth.rb
+- lib/opennebula/ssh_auth.rb
+- lib/opennebula/x509_auth.rb
+- NOTICE
+- LICENSE
+homepage: http://opennebula.org
+licenses: []
+metadata: {}
+post_install_message:
+rdoc_options: []
+require_paths:
+- lib
+required_ruby_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - '>='
+ - !ruby/object:Gem::Version
+ version: '0'
+required_rubygems_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - '>='
+ - !ruby/object:Gem::Version
+ version: '0'
+requirements: []
+rubyforge_project:
+rubygems_version: 2.0.3
+signing_key:
+specification_version: 4
+summary: OpenNebula Client API
+test_files: []