diff --git a/SConstruct b/SConstruct index 442299299b..d694ec1aa9 100644 --- a/SConstruct +++ b/SConstruct @@ -98,7 +98,7 @@ main_env.Append(LINKFLAGS=['-g', '-pthread']) # SQLITE sqlite_dir=ARGUMENTS.get('sqlite_dir', 'none') if sqlite_dir!='none': - main_env.Append(LIBPATH=[sqlite_dir+"/lib"]) + main_env.Append(LIBPATH=[sqlite_dir+"/lib", sqlite_dir+"/lib64"]) main_env.Append(CPPPATH=[sqlite_dir+"/include"]) sqlite=ARGUMENTS.get('sqlite', 'yes') @@ -121,7 +121,7 @@ else: # xmlrpc xmlrpc_dir=ARGUMENTS.get('xmlrpc', 'none') if xmlrpc_dir!='none': - main_env.Append(LIBPATH=[xmlrpc_dir+"/lib"]) + main_env.Append(LIBPATH=[xmlrpc_dir+"/lib", xmlrpc_dir+"/lib64"]) main_env.Append(CPPPATH=[xmlrpc_dir+"/include"]) # build lex/bison diff --git a/install.sh b/install.sh index 64ea77a958..67fe0d5d03 100755 --- a/install.sh +++ b/install.sh @@ -248,7 +248,8 @@ VAR_DIRS="$VAR_LOCATION/remotes \ $VAR_LOCATION/remotes/auth/server_cipher \ $VAR_LOCATION/remotes/auth/dummy" -SUNSTONE_DIRS="$SUNSTONE_LOCATION/models \ +SUNSTONE_DIRS="$SUNSTONE_LOCATION/routes \ + $SUNSTONE_LOCATION/models \ $SUNSTONE_LOCATION/models/OpenNebulaJSON \ $SUNSTONE_LOCATION/public \ $SUNSTONE_LOCATION/public/js \ @@ -919,6 +920,7 @@ DATASTORE_DRIVER_FS_SCRIPTS="src/datastore_mad/remotes/fs/cp \ DATASTORE_DRIVER_VMWARE_SCRIPTS="src/datastore_mad/remotes/vmware/cp \ src/datastore_mad/remotes/vmware/mkfs \ src/datastore_mad/remotes/vmware/stat \ + src/datastore_mad/remotes/vmware/clone \ src/datastore_mad/remotes/vmware/rm" DATASTORE_DRIVER_ISCSI_SCRIPTS="src/datastore_mad/remotes/iscsi/cp \ @@ -1192,7 +1194,8 @@ ONE_CLI_LIB_FILES="src/cli/one_helper/onegroup_helper.rb \ src/cli/one_helper/onevnet_helper.rb \ src/cli/one_helper/oneacl_helper.rb \ src/cli/one_helper/onedatastore_helper.rb \ - src/cli/one_helper/onecluster_helper.rb" + src/cli/one_helper/onecluster_helper.rb \ + src/cli/one_helper/oneacct_helper.rb" CLI_BIN_FILES="src/cli/onevm \ src/cli/onehost \ @@ -1203,7 +1206,8 @@ CLI_BIN_FILES="src/cli/onevm \ src/cli/onegroup \ src/cli/oneacl \ src/cli/onedatastore \ - src/cli/onecluster" + src/cli/onecluster \ + src/cli/oneacct" CLI_CONF_FILES="src/cli/etc/onegroup.yaml \ src/cli/etc/onehost.yaml \ diff --git a/src/cli/command_parser.rb b/src/cli/command_parser.rb index 7c844738f1..5db6483d84 100644 --- a/src/cli/command_parser.rb +++ b/src/cli/command_parser.rb @@ -54,61 +54,206 @@ module CommandParser attr_reader :options, :args def initialize(args=[], &block) - @opts = Array.new + @available_options = Array.new @commands = Hash.new @formats = Hash.new - @script = nil + + @main = nil + + @exit_code = nil @args = args @options = Hash.new - set :format, :file, "Path to a file" do |arg| - format_file(arg) - end - - set :format, :range, "List of id's in the form 1,8..15" do |arg| - format_range(arg) - end - - set :format, :text, "String" do |arg| - format_text(arg) - end + define_default_formats instance_eval(&block) self.run end + # Defines the usage information of the command + # @param [String] str def usage(str) - @usage=< description, + :proc => block + } + end + + # Defines a global option for the command that will be used for all the + # actions + # @param [Hash, Array] options the option to be included. An + # array of options can be also provided + # @option options [String] :name + # @option options [String] :short + # @option options [String] :large + # @option options [String] :description + # @option options [Class] :format + # @option options [Block] :proc The block receives the value of the + # option and the hash of options. The block must return an Array + # containing the result (0:success, 1:failure) and the + # new value for the argument or nil. More than one option can be + # specified in the block using the options hash. This hash will be + # available inside the command block. + # + # @example + # This example will define the following options: + # options[:type] = type + # + # TYPE={ + # :name => "type", + # :short => "-t type", + # :large => "--type type", + # :format => String, + # :description => "Type of the new Image" + # } + # + # This example will define the following options: + # options[:check] = true + # options[:datastore] = id + # + # DATASTORE = { + # :name => "datastore", + # :short => "-d id|name", + # :large => "--datastore id|name" , + # :description => "Selects the datastore", + # :format => String, + # :proc => lambda { |o, options| + # options[:check] = true + # [0, OpenNebulaHelper.dname_to_id(o)] + # } + # } + # + def option(options) + if options.instance_of?(Array) + options.each { |o| @available_options << o } + elsif options.instance_of?(Hash) + @available_options << options end end + # Defines the exit code to be returned by the command + # @param [Integer] code + def exit_code(code) + @exit_code = code + end + def exit_with_code(code, output=nil) puts output if output exit code end + # Defines a new action for the command, several actions can be defined + # for a command. For example: create, delete, list. + # The options and args variables can be used inside the block, and + # they contain the parsedarguments and options. + # + # @param [Symbol] name Name of the action (i.e: :create, :list) + # @param [String] desc Description of the action + # @param [Array>, Hash] args_format arguments + # or specific options for this actiion + # Note that the first argument of the command is the + # action and should not be defined using this parameter. The rest of + # the argument must be defined using this parameter. + # This parameter can use formats previously defined with the format + # method + # Options are specified using a hash :options => ... containing + # the hashes representing the options. The option method doc contains + # the hash that has to be used to specify an option + # @yieldreturn [Integer, Array[Integer, String]] the block must + # return the exit_code and if a String is returned it will be printed + # + # @example + # Definining two arguments: + # $ onetest test a1 a2 + # + # CommandParser::CmdParser.new(ARGV) do + # description "Test" + # usage "onetest [options]" + # version "1.0" + # + # options VERBOSE, HELP + # + # command :test, "Test", :test1, :test2, :options => XML do + # puts options[:xml] + # puts options[:verbose] + # puts args[0] + # puts args[1] + # [0, "It works"] + # end + # end + # + # + # Defining optional arguments: test1 is mandatory, test2 optional + # $ onetest test a1 | $ onetest test a1 a2 + # + # CommandParser::CmdParser.new(ARGV) do + # description "Test" + # usage "onetest [options]" + # version "1.0" + # + # options VERBOSE, HELP + # + # command :test, "Test", :test1, [:test2, nil], :options => XML do + # puts options[:xml] + # puts options[:verbose] + # puts args[0] + # puts "It works" + # 0 + # end + # end + # + # + # Defining an argument with different formats: + # $ onetest test a1 a2 | $ onetest test a1 123 + # + # CommandParser::CmdParser.new(ARGV) do + # description "Test" + # usage "onetest [options]" + # version "1.0" + # + # options VERBOSE, HELP + # + # format :format1, "String to Integer" do + # [0, arg.to_i] + # end + # + # command :test, "Test", :test1, [:format1, format2], :options => XML do + # puts options[:xml] + # puts options[:verbose] + # puts args[0] + # 0 + # end + # end + # def command(name, desc, *args_format, &block) cmd = Hash.new cmd[:desc] = desc @@ -130,28 +275,119 @@ EOT @commands[name.to_sym] = cmd end - def script(*args_format, &block) - @script=Hash.new - @script[:args_format] = Array.new + # Defines a new action for the command, several actions can be defined + # for a command. For example: create, delete, list. + # The options and args variables can be used inside the block, and + # they contain the parsedarguments and options. + # + # @param [Array>] args_format arguments + # or specific options for this actiion + # Note that the first argument of the command is the + # action and should not be defined using this parameter. The rest of + # the argument must be defined using this parameter. + # This parameter can use formats previously defined with the format + # method + # @yieldreturn [Integer, Array[Integer, String]] the block must + # return the exit_code and if a String is returned it will be printed + # + # @example + # Definining two arguments: + # $ onetest a1 a2 + # + # CommandParser::CmdParser.new(ARGV) do + # description "Test" + # usage "onetest [options]" + # version "1.0" + # + # options XML, VERBOSE, HELP + # + # main :test1, :test2 do + # puts options[:xml] + # puts options[:verbose] + # puts args[0] + # puts args[1] + # [0, "It works"] + # end + # end + # + # + # Defining optional arguments: test1 is mandatory, test2 optional + # $ onetest a1 | $ onetest a1 a2 + # + # CommandParser::CmdParser.new(ARGV) do + # description "Test" + # usage "onetest []" + # version "1.0" + # + # options XML, VERBOSE, HELP + # + # main :test1, [:test2, nil] do + # puts options[:xml] + # puts options[:verbose] + # puts args[0] + # puts "It works" + # 0 + # end + # end + # + # + # Defining an argument with different formats: + # $ onetest a1 a2 | $ onetest a1 123 + # + # CommandParser::CmdParser.new(ARGV) do + # description "Test" + # usage "onetest []" + # version "1.0" + # + # options XML, VERBOSE, HELP + # + # format :format1, "String to Integer" do + # [0, arg.to_i] + # end + # + # main :test1, [:format1, :format2] do + # puts options[:xml] + # puts options[:verbose] + # puts args[0] + # puts args[1] + # 0 + # end + # end + # + def main(*args_format, &block) + @main=Hash.new + @main[:arity] = 0 + @main[:args_format] = Array.new args_format.collect {|args| if args.instance_of?(Array) - @script[:arity]+=1 unless args.include?(nil) - @script[:args_format] << args + @main[:arity]+=1 unless args.include?(nil) + @main[:args_format] << args elsif args.instance_of?(Hash) && args[:options] - @opts << args[:options] + @available_options << args[:options] else - @script[:arity]+=1 - @script[:args_format] << [args] + @main[:arity]+=1 + @main[:args_format] << [args] end } - @script[:proc] = block + @main[:proc] = block end + # DEPRECATED, use format and options instead + def set(e, *args, &block) + case e + when :option + option(args[0]) + when :format + format(args[0], args[1], &block) + end + end + + def run comm_name="" - if @script - comm=@script + if @main + comm=@main elsif if @args[0] && !@args[0].match(/^-/) comm_name=@args.shift.to_sym @@ -160,7 +396,7 @@ EOT end if comm.nil? - help + print_help exit -1 end @@ -175,133 +411,17 @@ EOT puts rc[1] exit rc.first else - exit rc + exit(@exit_code || rc) end end end - def help - puts @usage if @usage - puts - puts @description if @description - puts - print_options - puts - print_commands - puts - print_formatters - puts - if @version - puts "## LICENSE" - puts @version - end - end - private - def print_options - puts "## OPTIONS" - - shown_opts = Array.new - opt_format = "#{' '*5}%-25s %s" - @commands.each{ |key,value| - value[:options].flatten.each { |o| - if shown_opts.include?(o[:name]) - next - else - shown_opts << o[:name] - - str = "" - str << o[:short].split(' ').first << ', ' if o[:short] - str << o[:large] - - printf opt_format, str, o[:description] - puts - end - } - } - - @opts.each{ |o| - str = "" - str << o[:short].split(' ').first << ', ' if o[:short] - str << o[:large] - - printf opt_format, str, o[:description] - puts - } - end - - def print_commands - puts "## COMMANDS" - - cmd_format5 = "#{' '*3}%s" - cmd_format10 = "#{' '*8}%s" - @commands.each{ |key,value| - printf cmd_format5, "* #{key} " - - args_str=value[:args_format].collect{ |a| - if a.include?(nil) - "[<#{a.compact.join("|")}>]" - else - "<#{a.join("|")}>" - end - }.join(' ') - printf "#{args_str}" - puts - - value[:desc].split("\n").each { |l| - printf cmd_format10, l - puts - } - - unless value[:options].empty? - opts_str=value[:options].flatten.collect{|o| - o[:name] - }.join(', ') - printf cmd_format10, "valid options: #{opts_str}" - puts - end - puts - } - end - - def print_formatters - puts "## ARGUMENT FORMATS" - - cmd_format5 = "#{' '*3}%s" - cmd_format10 = "#{' '*8}%s" - @formats.each{ |key,value| - printf cmd_format5, "* #{key}" - puts - - value[:desc].split("\n").each { |l| - printf cmd_format10, l - puts - } - - puts - } - end - - def add_option(option) - if option.instance_of?(Array) - option.each { |o| @opts << o } - elsif option.instance_of?(Hash) - @opts << option - end - end - - def add_format(format, description, block) - @formats[format] = { - :desc => description, - :proc => block - } - end - def parse(extra_options) @cmdparse=OptionParser.new do |opts| - merge = @opts - merge = @opts + extra_options if extra_options + merge = @available_options + merge = @available_options + extra_options if extra_options merge.flatten.each do |e| args = [] args << e[:short] if e[:short] @@ -311,9 +431,16 @@ EOT opts.on(*args) do |o| if e[:proc] - e[:proc].call(o, @options) + rc = e[:proc].call(o, @options) + if rc.instance_of?(Array) && rc[0] == 0 + options[e[:name].to_sym] = rc[1] + else + puts rc[1] + puts "option #{e[:name]}: Parsing error" + exit -1 + end elsif e[:name]=="help" - help + print_help exit elsif e[:name]=="version" puts @version @@ -399,7 +526,126 @@ EOT end ######################################################################## - # Formatters for arguments + # Printers + ######################################################################## + + def print_help + if @usage + puts "## SYNOPSIS" + puts + puts @usage + puts + end + puts @description if @description + puts + print_options + puts + print_commands + puts + print_formatters + puts + if @version + puts "## LICENSE" + puts @version + end + end + + + def print_options + puts "## OPTIONS" + + shown_opts = Array.new + opt_format = "#{' '*5}%-25s %s" + @commands.each{ |key,value| + value[:options].flatten.each { |o| + if shown_opts.include?(o[:name]) + next + else + shown_opts << o[:name] + + str = "" + str << o[:short].split(' ').first << ', ' if o[:short] + str << o[:large] + + printf opt_format, str, o[:description] + puts + end + } + } + + @available_options.each{ |o| + str = "" + str << o[:short].split(' ').first << ', ' if o[:short] + str << o[:large] + + printf opt_format, str, o[:description] + puts + } + end + + def print_commands + cmd_format5 = "#{' '*3}%s" + cmd_format10 = "#{' '*8}%s" + + if @main + print_command(@main) + else + puts "## COMMANDS" + + @commands.each{ |key,value| + printf cmd_format5, "* #{key} " + + print_command(value) + } + end + end + + def print_command(command) + args_str=command[:args_format].collect{ |a| + if a.include?(nil) + "[<#{a.compact.join("|")}>]" + else + "<#{a.join("|")}>" + end + }.join(' ') + printf "#{args_str}" + puts + + command[:desc].split("\n").each { |l| + printf cmd_format10, l + puts + } + + unless command[:options].empty? + opts_str=command[:options].flatten.collect{|o| + o[:name] + }.join(', ') + printf cmd_format10, "valid options: #{opts_str}" + puts + end + puts + end + + def print_formatters + puts "## ARGUMENT FORMATS" + + cmd_format5 = "#{' '*3}%s" + cmd_format10 = "#{' '*8}%s" + @formats.each{ |key,value| + printf cmd_format5, "* #{key}" + puts + + value[:desc].split("\n").each { |l| + printf cmd_format10, l + puts + } + + puts + } + end + + ######################################################################## + # Default Formatters for arguments ######################################################################## def format_text(arg) arg.instance_of?(String) ? [0,arg] : [-1] @@ -432,6 +678,20 @@ EOT return 0,ids.uniq end + + def define_default_formats + format :file, "Path to a file" do |arg| + format_file(arg) + end + + format :range, "List of id's in the form 1,8..15" do |arg| + format_range(arg) + end + + format :text, "String" do |arg| + format_text(arg) + end + end end end diff --git a/src/cli/one_helper/oneacct_helper.rb b/src/cli/one_helper/oneacct_helper.rb new file mode 100644 index 0000000000..f07d766fdb --- /dev/null +++ b/src/cli/one_helper/oneacct_helper.rb @@ -0,0 +1,179 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) # +# # +# 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 'one_helper' + +class AcctHelper < OpenNebulaHelper::OneHelper + START_TIME = { + :name => "start_time", + :short => "-s TIME", + :large => "--start TIME" , + :description => "Start date and time to take into account", + :format => String # TODO Time + } + + END_TIME = { + :name => "end_time", + :short => "-e TIME", + :large => "--end TIME" , + :description => "End date and time", + :format => String # TODO Time + } + + USER = { + :name => "user", + :short => "-u user", + :large => "--user user" , + :description => "User name or id to filter the results", + :format => String, + :proc => lambda { |o, options| + OpenNebulaHelper.rname_to_id(o, "USER") + } + } + + GROUP = { + :name => "group", + :short => "-g group", + :large => "--group group" , + :description => "Group name or id to filter the results", + :format => String, + :proc => lambda { |o, options| + puts o + OpenNebulaHelper.rname_to_id(o, "GROUP") + } + } + + HOST = { + :name => "host", + :short => "-H HOST", + :large => "--host HOST" , + :description => "Host name or id to filter the results", + :format => String, + :proc => lambda { |o, options| + OpenNebulaHelper.rname_to_id(o, "HOST") + } + } + + XPATH = { + :name => "xpath", + :large => "--xpath XPATH_EXPRESSION" , + :description => "Xpath expression to filter the results. \ + For example: oneacct --xpath 'HISTORY[ETIME>0]'", + :format => String + } + + XML = { + :name => "xml", + :short => "-x", + :large => "--xml", + :description => "Show the resource in xml format" + } + + JSON = { + :name => "json", + :short => "-j", + :large => "--json", + :description => "Show the resource in xml format" + } + + SPLIT={ + :name => "split", + :large => "--split", + :description => "Split the output in a table for each VM" + } + + ACCT_OPTIONS = [START_TIME, END_TIME, USER, GROUP, HOST, XPATH, XML, JSON, SPLIT] + + + ACCT_TABLE = CLIHelper::ShowTable.new("oneacct.yaml", nil) do + column :VID, "Virtual Machine ID", :size=>4 do |d| + d["OID"] + end + + column :SEQ, "History record sequence number", :size=>3 do |d| + d["SEQ"] + end + + column :HOSTNAME, "Host name", :left, :size=>15 do |d| + d["HOSTNAME"] + end + + column :REASON, "VM state change reason", :left, :size=>4 do |d| + VirtualMachine.get_reason d["REASON"] + end + + column :START_TIME, "Start time", :size=>14 do |d| + OpenNebulaHelper.time_to_str(d['STIME']) + end + + column :END_TIME, "End time", :size=>14 do |d| + OpenNebulaHelper.time_to_str(d['ETIME']) + end + + column :MEMORY, "Assigned memory", :size=>6 do |d| + OpenNebulaHelper.unit_to_str(d["VM"]["TEMPLATE"]["MEMORY"].to_i, {}, 'M') + end + + column :CPU, "Number of CPUs", :size=>3 do |d| + d["VM"]["TEMPLATE"]["CPU"] + end + + column :NET_RX, "Data received from the network", :size=>6 do |d| + # NET is measured in bytes, unit_to_str expects KBytes + OpenNebulaHelper.unit_to_str(d["VM"]["NET_RX"].to_i / 1024.0, {}) + end + + column :NET_TX, "Data sent to the network", :size=>6 do |d| + # NET is measured in bytes, unit_to_str expects KBytes + OpenNebulaHelper.unit_to_str(d["VM"]["NET_TX"].to_i / 1024.0, {}) + end + + default :VID, :HOSTNAME, :REASON, :START_TIME, :END_TIME, :MEMORY, :CPU, :NET_RX, :NET_TX + end + + def self.print_start_enc_time_header(start_time, end_time) + print "Showing active history records from " + + CLIHelper.scr_bold + if ( start_time != -1 ) + print Time.at(start_time).to_s + else + print "-" + end + + CLIHelper.scr_restore + print " to " + + CLIHelper.scr_bold + if ( end_time != -1 ) + print Time.at(end_time).to_s + else + print "-" + end + + CLIHelper.scr_restore + puts + puts + end + + def self.print_user_header(user_id) + CLIHelper.scr_bold + CLIHelper.scr_underline + puts "# User #{user_id}".ljust(80) + CLIHelper.scr_restore + puts + end +end \ No newline at end of file diff --git a/src/cli/one_helper/onecluster_helper.rb b/src/cli/one_helper/onecluster_helper.rb index d427c4a8f5..7235f2eb6f 100644 --- a/src/cli/one_helper/onecluster_helper.rb +++ b/src/cli/one_helper/onecluster_helper.rb @@ -25,18 +25,10 @@ class OneClusterHelper < OpenNebulaHelper::OneHelper :description => "Selects the cluster", :format => String, :proc => lambda { |o, options| - ch = OneClusterHelper.new - rc, cid = ch.to_id(o) - if rc == 0 - options[:cluster] = cid - else - puts cid - puts "option cluster: Parsing error" - exit -1 - end + OpenNebulaHelper.rname_to_id(o, "CLUSTER") } } - + def self.rname "CLUSTER" end diff --git a/src/cli/one_helper/onedatastore_helper.rb b/src/cli/one_helper/onedatastore_helper.rb index 3ec15e30f7..d8495bfa71 100644 --- a/src/cli/one_helper/onedatastore_helper.rb +++ b/src/cli/one_helper/onedatastore_helper.rb @@ -24,15 +24,7 @@ class OneDatastoreHelper < OpenNebulaHelper::OneHelper :description => "Selects the datastore", :format => String, :proc => lambda { |o, options| - ch = OneDatastoreHelper.new - rc, dsid = ch.to_id(o) - if rc == 0 - options[:datastore] = dsid - else - puts dsid - puts "option datastore: Parsing error" - exit -1 - end + OpenNebulaHelper.rname_to_id(o, "DATASTORE") } } diff --git a/src/cli/one_helper/onevm_helper.rb b/src/cli/one_helper/onevm_helper.rb index 7de8e51a32..e6bafadab4 100644 --- a/src/cli/one_helper/onevm_helper.rb +++ b/src/cli/one_helper/onevm_helper.rb @@ -32,14 +32,7 @@ class OneVMHelper < OpenNebulaHelper::OneHelper :description => "Selects the image", :format => String, :proc => lambda { |o, options| - rc, imid = OpenNebulaHelper.rname_to_id(o, "IMAGE") - if rc == 0 - options[:image] = imid - else - puts imid - puts "option image: Parsing error" - exit -1 - end + OpenNebulaHelper.rname_to_id(o, "IMAGE") } } @@ -188,13 +181,13 @@ class OneVMHelper < OpenNebulaHelper::OneHelper "USED CPU" => "CPU", "NET_TX" => "NET_TX", "NET_RX" => "NET_RX" - } + } - poll_attrs.each { |k,v| + poll_attrs.each { |k,v| if k == "USED CPU" - puts str % [k,vm[v]] + puts str % [k,vm[v]] elsif k == "USED MEMORY" - puts str % [k, OpenNebulaHelper.unit_to_str(vm[v].to_i, {})] + puts str % [k, OpenNebulaHelper.unit_to_str(vm[v].to_i, {})] else puts str % [k, OpenNebulaHelper.unit_to_str(vm[v].to_i/1024, {})] end diff --git a/src/cli/oneacct b/src/cli/oneacct index 08e1026cd7..46b6148e0b 100755 --- a/src/cli/oneacct +++ b/src/cli/oneacct @@ -16,7 +16,6 @@ # limitations under the License. # #--------------------------------------------------------------------------- # - ONE_LOCATION=ENV["ONE_LOCATION"] if !ONE_LOCATION @@ -28,356 +27,87 @@ end $: << RUBY_LIB_LOCATION $: << RUBY_LIB_LOCATION+"/cli" -require 'rubygems' +require 'command_parser' +require 'one_helper/oneacct_helper' -require 'cli/one_helper' -require 'cli/command_parser' require 'json' -require 'optparse' -require 'optparse/time' +cmd = CommandParser::CmdParser.new(ARGV) do + usage "`oneacct` []" + description "" + version OpenNebulaHelper::ONE_VERSION -################################################################################ -# CLI Helper -################################################################################ + option [AcctHelper::ACCT_OPTIONS + CommandParser::OPTIONS] -class AcctHelper < OpenNebulaHelper::OneHelper + main do + filter_flag = (options[:user] || VirtualMachinePool::INFO_ALL) + start_time = options[:start] ? options[:start].to_i : -1 + end_time = options[:end] ? options[:end].to_i : -1 - def self.conf_file - "oneacct.yaml" - end + common_opts = { + :start_time => start_time, + :end_time => end_time, + :host => options[:host], + :group => options[:group], + :xpath => options[:xpath] + } -=begin - List of child elements + client = OpenNebula::Client.new + pool = OpenNebula::VirtualMachinePool.new(client) - OID - SEQ - HOSTNAME - HID - STIME - ETIME - VMMMAD - VNMMAD - PSTIME - PETIME - RSTIME - RETIME - ESTIME - EETIME - REASON -=end - def list_history(data) - table = CLIHelper::ShowTable.new(self.class.table_conf, self) do - column :VID, "Virtual Machine ID", :size=>4 do |d| - d["OID"] + if options[:json] || options[:xml] + xml_str = pool.accounting_xml(filter_flag, common_opts) + if OpenNebula.is_error?(xml_str) + puts acct_hash.message + exit -1 end - column :SEQ, "History record sequence number", :size=>3 do |d| - d["SEQ"] + if options[:json] + xmldoc = XMLElement.new + xmldoc.initialize_xml(xml_str, 'HISTORY_RECORDS') + + puts xmldoc.to_hash.to_json + elsif options[:xml] + puts xml_str end - column :HOSTNAME, "Host name", :left, :size=>15 do |d| - d["HOSTNAME"] + exit_code 0 + else + order_by = Hash.new + order_by[:order_by_1] = 'VM/UID' + order_by[:order_by_2] = 'VM/ID' if options[:split] + + acct_hash = pool.accounting(filter_flag, common_opts.merge(order_by)) + if OpenNebula.is_error?(acct_hash) + puts acct_hash.message + exit -1 end - column :REASON, "VM state change reason", :left, :size=>4 do |d| - VirtualMachine.get_reason d["REASON"] + if ( start_time != -1 or end_time != -1 ) + AcctHelper.print_start_end_time_header(start_time, end_time) end - column :START_TIME, "Start time", :size=>14 do |d| - OpenNebulaHelper.time_to_str(d['STIME']) - end + acct_hash.each { |user_id, value| + AcctHelper.print_user_header(user_id) - column :END_TIME, "End time", :size=>14 do |d| - OpenNebulaHelper.time_to_str(d['ETIME']) - end - - column :MEMORY, "Assigned memory", :size=>6 do |d| - OpenNebulaHelper.unit_to_str(d["VM/TEMPLATE/MEMORY"].to_i, {}, 'M') - end - - column :CPU, "Number of CPUs", :size=>3 do |d| - d["VM/TEMPLATE/CPU"] - end - - column :NET_RX, "Data received from the network", :size=>6 do |d| - # NET is measured in bytes, unit_to_str expects KBytes - OpenNebulaHelper.unit_to_str(d["VM/NET_RX"].to_i / 1024.0, {}) - end - - column :NET_TX, "Data sent to the network", :size=>6 do |d| - # NET is measured in bytes, unit_to_str expects KBytes - OpenNebulaHelper.unit_to_str(d["VM/NET_TX"].to_i / 1024.0, {}) - end - - default :VID, :HOSTNAME, :REASON, :START_TIME, :END_TIME, :MEMORY, :CPU, :NET_RX, :NET_TX - end - - table.show(data) - end - -public - def list_users(xmldoc, options=nil) - - uids = xmldoc.retrieve_elements('HISTORY/VM/UID') - - if uids.nil? - puts "No records found." - exit 0 - end - - uids.uniq! - - history_elems = [] - - uids.each do |uid| - CLIHelper.scr_bold - CLIHelper.scr_underline - - username_elems = - xmldoc.retrieve_elements("HISTORY/VM[UID=#{uid}]/UNAME") - - username = username_elems.nil? ? "" : username_elems.uniq - - puts "# User #{uid} #{username}".ljust(80) - CLIHelper.scr_restore - puts - - history_elems.clear - - vm_ids = xmldoc.retrieve_elements("HISTORY/VM[UID=#{uid}]/ID") - vm_ids = [] if vm_ids.nil? - vm_ids.uniq! - - vm_ids.each do |vid| - - if ( options[:split] ) - history_elems.clear - end - - xmldoc.each("HISTORY[OID=#{vid}]") do |history| - history_elems << history - end - - if ( options[:split] ) - list_history(history_elems) + if options[:split] + # Use one table for each VM + value.each { |vm_id, history_array| + array = history_array['HISTORY_RECORDS']['HISTORY'] + AcctHelper::ACCT_TABLE.show(array) + puts + } + else + # Use the same table for all the VMs + array = value['HISTORY_RECORDS']['HISTORY'] + AcctHelper::ACCT_TABLE.show(array) puts end - end + } - if ( !options[:split] ) - list_history(history_elems) - puts - end + exit_code 0 end end -end - -################################################################################ -# Helper methods -################################################################################ - -def redo_xmldoc(xmldoc, xpath_str) - xml_str = "" - - xmldoc.each(xpath_str) do |history| - xml_str << history.to_xml - end - - xml_str << "" - - xmldoc = XMLElement.new - xmldoc.initialize_xml(xml_str, 'HISTORY_RECORDS') - - return xmldoc -end - -################################################################################ -# Main command -################################################################################ - -options = Hash.new - -options[:format] = :table - -opts = OptionParser.new do |opts| - opts.on('-s', '--start TIME', Time, - 'Start date and time to take into account') do |ext| - options[:start]=ext - end - - opts.on("-e", "--end TIME", Time, - "End date and time" ) do |ext| - options[:end]=ext - end - - opts.on("-u", "--user user", String, - "User id to filter the results" ) do |ext| - options[:user]=ext - end - - opts.on("-g", "--group group", String, - "Group id to filter the results" ) do |ext| - options[:group]=ext - end - - opts.on("-H", "--host hostname", String, - "Host id to filter the results" ) do |ext| - options[:host]=ext - end - - opts.on("--xpath expression", String, - "Xpath expression to filter the results. For example: oneacct --xpath 'HISTORY[ETIME>0]'" ) do |ext| - options[:xpath]=ext - end - - opts.on("-j", "--json", - "Output in json format" ) do |ext| - options[:format]=:json - end - - opts.on("-x", "--xml", - "Output in xml format" ) do |ext| - options[:format]=:xml - end - - opts.on("--split", - "Split the output in a table for each VM" ) do |ext| - options[:split]=ext - end - - opts.on("-h", "--help", "Show this message" ) do - puts opts - exit - end - - opts.on() -end - - -begin - opts.parse!(ARGV) -rescue OptionParser::ParseError => e - STDERR.puts "Error: " << e.message - exit(-1) -end - - -client = OpenNebula::Client.new - -acct_helper = AcctHelper.new() - -time_start = -1 -time_end = -1 -filter_flag = VirtualMachinePool::INFO_ALL - -time_start = options[:start].to_i if options[:start] -time_end = options[:end].to_i if options[:end] - -if options[:user] - rc = OpenNebulaHelper.rname_to_id(options[:user], "USER") - - if rc[0] == 0 - filter_flag = rc[1] - else - puts rc[1] - exit -1 - end -end - -xml_str = client.call("vmpool.accounting", - filter_flag, - time_start, - time_end) - -if OpenNebula.is_error?(xml_str) - puts xml_str.message - exit -1 -end - -xmldoc = XMLElement.new -xmldoc.initialize_xml(xml_str, 'HISTORY_RECORDS') - -xpath = nil -hid = nil -gid = nil - -if options[:host] - rc = OpenNebulaHelper.rname_to_id(options[:host], "HOST") - - if rc[0] == 0 - hid = rc[1] - else - puts rc[1] - exit -1 - end -end - -if options[:group] - rc = OpenNebulaHelper.rname_to_id(options[:group], "GROUP") - - if rc[0] == 0 - gid = rc[1] - else - puts rc[1] - exit -1 - end -end - -if options[:host] && options[:group] - xpath = "HISTORY[VM/GID=#{gid} and HID=#{hid}]" -elsif options[:host] - xpath = "HISTORY[HID=#{hid}]" -elsif options[:group] - xpath = "HISTORY[VM/GID=#{gid}]" -end - -xmldoc = redo_xmldoc(xmldoc, xpath) if !xpath.nil? - - -if options[:xpath] - xmldoc = redo_xmldoc(xmldoc, options[:xpath]) -end - - -case options[:format] -when :table - if ( time_start != -1 or time_end != -1 ) - print "Showing active history records from " - - CLIHelper.scr_bold - - if ( time_start != -1 ) - print Time.at(time_start).to_s - else - print "-" - end - - CLIHelper.scr_restore - - print " to " - - CLIHelper.scr_bold - - if ( time_end != -1 ) - print Time.at(time_end).to_s - else - print "-" - end - - CLIHelper.scr_restore - puts - puts - end - - acct_helper.list_users(xmldoc, options) - -when :xml - puts xmldoc.to_xml - -when :json - puts xmldoc.to_hash.to_json - -end +end \ No newline at end of file diff --git a/src/cli/oneuser b/src/cli/oneuser index 74d1e98895..49ac9c01f8 100755 --- a/src/cli/oneuser +++ b/src/cli/oneuser @@ -221,9 +221,9 @@ cmd=CommandParser::CmdParser.new(ARGV) do Creates the Login token for authentication Examples: oneuser login my_user --ssh --key /tmp/id_rsa --time 72000 - oneuser login my_user --x509 --cert /tmp/my_cert.pem \ + oneuser login my_user --x509 --cert /tmp/my_cert.pem --key /tmp/my_key.pk --time 72000 - oneuser login my_user --x509_proxy --proxy /tmp/my_cert.pem \ + oneuser login my_user --x509_proxy --proxy /tmp/my_cert.pem --time 72000 EOT @@ -232,6 +232,8 @@ cmd=CommandParser::CmdParser.new(ARGV) do end key_desc = <<-EOT.unindent + DEPRECATED, use login to generate auth files. + Shows a public key from a private SSH key. Use it as password for the SSH authentication mechanism. EOT @@ -247,7 +249,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do exit_with_code -1, e.message end - puts sshauth.public_key + puts sshauth.password exit_with_code 0 end diff --git a/src/cloud/common/CloudAuth.rb b/src/cloud/common/CloudAuth.rb index 9006b58183..ddc1a4fa78 100644 --- a/src/cloud/common/CloudAuth.rb +++ b/src/cloud/common/CloudAuth.rb @@ -86,7 +86,7 @@ class CloudAuth def client(username=nil) expiration_time = @lock.synchronize { time_now = Time.now.to_i - + if time_now > @token_expiration_time - EXPIRE_MARGIN @token_expiration_time = time_now + EXPIRE_DELTA end @@ -95,8 +95,8 @@ class CloudAuth } token = @server_auth.login_token(expiration_time,username) - - Client.new(token,@conf[:one_xmlrpc]) + + OpenNebula::Client.new(token,@conf[:one_xmlrpc]) end # diff --git a/src/datastore_mad/remotes/fs/mkfs b/src/datastore_mad/remotes/fs/mkfs index 381a7a461b..d1340e3445 100755 --- a/src/datastore_mad/remotes/fs/mkfs +++ b/src/datastore_mad/remotes/fs/mkfs @@ -74,7 +74,7 @@ fi # ------------ Create the image to the repository ------------ -MKFS_CMD=`mkfs_command $DST $FSTYPE` +MKFS_CMD=`mkfs_command $DST $FSTYPE $SIZE` exec_and_log "$DD if=/dev/zero of=$DST bs=1 count=1 seek=${SIZE}M" \ "Could not create image $DST" diff --git a/src/lcm/LifeCycleStates.cc b/src/lcm/LifeCycleStates.cc index 87a09d94cd..6672f9b1fb 100644 --- a/src/lcm/LifeCycleStates.cc +++ b/src/lcm/LifeCycleStates.cc @@ -313,6 +313,8 @@ void LifeCycleManager::deploy_failure_action(int vid) vm->set_reason(History::ERROR); + vmpool->update_history(vm); + vm->set_previous_etime(the_time); vm->set_previous_vm_info(); diff --git a/src/mad/sh/scripts_common.sh b/src/mad/sh/scripts_common.sh index a196082205..f0f346a82b 100644 --- a/src/mad/sh/scripts_common.sh +++ b/src/mad/sh/scripts_common.sh @@ -250,6 +250,10 @@ function mkfs_command { echo "$MKSWAP $DST" return 0 ;; + "qcow2") + echo "$QEMU_IMG create -f qcow2 $DST ${SIZE}M" + return 0 + ;; "vmdk_"*) VMWARE_DISK_TYPE=`echo $FSTYPE|cut -d'_' -f 2` echo "WHICH_SUDO=\$(which sudo) ; \ diff --git a/src/oca/ruby/OpenNebula.rb b/src/oca/ruby/OpenNebula.rb index eaec386365..34980fd0c3 100644 --- a/src/oca/ruby/OpenNebula.rb +++ b/src/oca/ruby/OpenNebula.rb @@ -46,6 +46,8 @@ require 'OpenNebula/Datastore' require 'OpenNebula/DatastorePool' require 'OpenNebula/Cluster' require 'OpenNebula/ClusterPool' +require 'OpenNebula/Document' +require 'OpenNebula/DocumentPool' module OpenNebula diff --git a/src/oca/ruby/OpenNebula/Document.rb b/src/oca/ruby/OpenNebula/Document.rb index f2e9e09245..8462c02262 100644 --- a/src/oca/ruby/OpenNebula/Document.rb +++ b/src/oca/ruby/OpenNebula/Document.rb @@ -18,15 +18,15 @@ require 'OpenNebula/Pool' module OpenNebula - # All subclasses must define the Document::TYPE constant. + # All subclasses must define the DOCUMENT_TYPE constant. # # @example # require 'OpenNebula/Document' # # module OpenNebula # class CustomObject < Document - # - # Document::TYPE = 400 + # + # DOCUMENT_TYPE = 400 # # end # end @@ -65,7 +65,7 @@ module OpenNebula # # @param [Nokogiri::XML::Node, REXML::Element] xml string # created by the build_xml() method - # @param [OpenNebula::Client] client the xml-rpc client + # @param [OpenNebula::Client] client the xml-rpc client # # @return [Document] the new object # @@ -94,7 +94,7 @@ module OpenNebula # @return [nil, OpenNebula::Error] nil in case of success, Error # otherwise def allocate(description) - super(DOCUMENT_METHODS[:allocate], description, TYPE) + super(DOCUMENT_METHODS[:allocate], description, document_type) end # Deletes the Document @@ -187,6 +187,10 @@ module OpenNebula end end + def document_type + self.class::DOCUMENT_TYPE + end + private def set_publish(published) diff --git a/src/oca/ruby/OpenNebula/DocumentPool.rb b/src/oca/ruby/OpenNebula/DocumentPool.rb index 3654947ed6..3b428aa698 100644 --- a/src/oca/ruby/OpenNebula/DocumentPool.rb +++ b/src/oca/ruby/OpenNebula/DocumentPool.rb @@ -18,7 +18,7 @@ require 'OpenNebula/Pool' module OpenNebula - # All subclasses must define the DocumentPool::TYPE constant + # All subclasses must define the DOCUMENT_TYPE constant # and the factory method. # # @example @@ -27,7 +27,7 @@ module OpenNebula # module OpenNebula # class CustomObjectPool < DocumentPool # - # DocumentPool::TYPE = 400 + # DOCUMENT_TYPE = 400 # # def factory(element_xml) # OpenNebula::CustomObject.new(element_xml, @client) @@ -72,22 +72,26 @@ module OpenNebula def info(*args) case args.size when 0 - info_filter(DOCUMENT_POOL_METHODS[:info],@user_id,-1,-1, TYPE) + 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], TYPE) + 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], TYPE) + return super(DOCUMENT_POOL_METHODS[:info], document_type) end def info_mine() - return super(DOCUMENT_POOL_METHODS[:info], TYPE) + return super(DOCUMENT_POOL_METHODS[:info], document_type) end def info_group() - return super(DOCUMENT_POOL_METHODS[:info], TYPE) + return super(DOCUMENT_POOL_METHODS[:info], document_type) + end + + def document_type + self.class::DOCUMENT_TYPE end end end diff --git a/src/oca/ruby/OpenNebula/VirtualMachinePool.rb b/src/oca/ruby/OpenNebula/VirtualMachinePool.rb index 01dc59fdcf..80aae1d94c 100644 --- a/src/oca/ruby/OpenNebula/VirtualMachinePool.rb +++ b/src/oca/ruby/OpenNebula/VirtualMachinePool.rb @@ -26,7 +26,8 @@ module OpenNebula VM_POOL_METHODS = { :info => "vmpool.info", - :monitoring => "vmpool.monitoring" + :monitoring => "vmpool.monitoring", + :accounting => "vmpool.accounting" } # Constants for info queries (include/RequestManagerPoolInfoFilter.h) @@ -56,7 +57,7 @@ module OpenNebula ####################################################################### # Retrieves all or part of the VirtualMachines in the pool. - # No arguments, returns the not-in-done VMs for the user + # 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) @@ -133,7 +134,7 @@ module OpenNebula # [["1337608271", "510"], ["1337608301", "510"], ["1337608331", "520"]], # "TEMPLATE/CUSTOM_PROBE"=> # []}, - # + # # "0"=> # {"CPU"=> # [["1337608271", "0"], ["1337608301", "0"], ["1337608331", "0"]], @@ -156,8 +157,160 @@ module OpenNebula return @client.call(VM_POOL_METHODS[:monitoring], filter_flag) end + # Retrieves the accounting data for all the VMs in the pool + # + # @param [Integer] filter_flag Optional filter flag to retrieve all or + # part of the Pool. Possible values: INFO_ALL, INFO_GROUP, INFO_MINE + # or user_id + # @param [Hash] options + # @option params [Integer] :start_time Start date and time to take into account, + # if no start_time is required use -1 + # @option params [Integer] :end_time End date and time to take into account, + # if no end_time is required use -1 + # @option params [Integer] :host Host id to filter the results + # @option params [Integer] :group Group id to filter the results + # @option params [String] :xpath Xpath expression to filter the results. + # For example: HISTORY[ETIME>0] + # @option params [String] :order_by_1 Xpath expression to group the + # @option params [String] :order_by_2 Xpath expression to group the + # returned hash. This will be the second level of the hash + # + # @return [Hash, OpenNebula::Error] + # The first level hash uses the :order_by_1 values as keys, and + # as value a Hash with the :order_by_2 values and the HISTORY_RECORDS + # + # @example + # {"HISTORY_RECORDS"=> + # {"HISTORY"=> [ + # {"OID"=>"0", + # "SEQ"=>"0", + # "HOSTNAME"=>"dummy", + # ... + # }, + # {"OID"=>"0", + # "SEQ"=>"0", + # "HOSTNAME"=>"dummy", + # + # @example :order_by_1 => VM/UID + # {"0"=> + # {"HISTORY_RECORDS"=> + # {"HISTORY"=> [ + # {"OID"=>"0", + # "SEQ"=>"0", + # "HOSTNAME"=>"dummy", + # ... + # }, + # {"OID"=>"0", + # "SEQ"=>"0", + # "HOSTNAME"=>"dummy", + # + # @example :order_by_1 => VM/UID, :order_by_2 => VM/ID + # {"0"=> + # {"0"=> + # {"HISTORY_RECORDS"=> + # {"HISTORY"=> [ + # {"OID"=>"0", + # "SEQ"=>"0", + # "HOSTNAME"=>"dummy", + # ... + # }, + # {"OID"=>"0", + # "SEQ"=>"0", + # "HOSTNAME"=>"dummy", + # + def accounting(filter_flag=INFO_ALL, options={}) + acct_hash = Hash.new + + rc = build_accounting(filter_flag, options) do |history| + hash = acct_hash + + if options[:order_by_1] + id_1 = history[options[:order_by_1]] + acct_hash[id_1] ||= Hash.new + + if options[:order_by_2] + id_2 = history[options[:order_by_2]] + acct_hash[id_1][id_2] ||= Hash.new + + hash = acct_hash[id_1][id_2] + else + hash = acct_hash[id_1] + end + end + + hash["HISTORY_RECORDS"] ||= Hash.new + hash["HISTORY_RECORDS"]["HISTORY"] ||= Array.new + hash["HISTORY_RECORDS"]["HISTORY"] << history.to_hash['HISTORY'] + end + + return rc if OpenNebula.is_error?(rc) + + acct_hash + end + + # Retrieves the accounting data for all the VMs in the pool in xml + # + # @param [Integer] filter_flag Optional filter flag to retrieve all or + # part of the Pool. Possible values: INFO_ALL, INFO_GROUP, INFO_MINE + # or user_id + # @param [Hash] options + # @option params [Integer] :start_time Start date and time to take into account, + # if no start_time is required use -1 + # @option params [Integer] :end_time End date and time to take into account, + # if no end_time is required use -1 + # @option params [Integer] :host Host id to filter the results + # @option params [Integer] :group Group id to filter the results + # @option params [String] :xpath Xpath expression to filter the results. + # For example: HISTORY[ETIME>0] + # + # @return [String] the xml representing the accounting data + def accounting_xml(filter_flag=INFO_ALL, options={}) + acct_hash = Hash.new + xml_str = "\n" + + rc = build_accounting(filter_flag, options) do |history| + xml_str << history.to_xml + end + + return rc if OpenNebula.is_error?(rc) + + xml_str << "\n" + xml_str + end + private + def build_accounting(filter_flag=INFO_ALL, 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 diff --git a/src/oca/ruby/OpenNebula/XMLUtils.rb b/src/oca/ruby/OpenNebula/XMLUtils.rb index 2af0b40850..13100fafdf 100644 --- a/src/oca/ruby/OpenNebula/XMLUtils.rb +++ b/src/oca/ruby/OpenNebula/XMLUtils.rb @@ -335,12 +335,33 @@ module OpenNebula return str end - # - # - # - def to_hash(hash={}, element=nil) - element ||= @xml.document.root + # @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?) @@ -349,14 +370,14 @@ module OpenNebula r = {} array.each { |c| if c.element? - to_hash(r, c) + build_hash(r, c) end } end else r = {} if element.has_elements? - element.each_element { |c| to_hash(r, c) } + element.each_element { |c| build_hash(r, c) } elsif element.has_text? r = element.text end @@ -376,8 +397,6 @@ module OpenNebula hash end - private - # # # diff --git a/src/sunstone/etc/sunstone-server.conf b/src/sunstone/etc/sunstone-server.conf index 1b15bbddfe..021fb3ce51 100644 --- a/src/sunstone/etc/sunstone-server.conf +++ b/src/sunstone/etc/sunstone-server.conf @@ -92,3 +92,24 @@ # Marketplace endpoint # :marketplace_url: https://marketplace.c12g.com/appliance + +################################################################################ +# Routes +################################################################################ + +# Array of files containing ruby code to extend the default sunstone-server +# routes. These files must be placed inside the custom_routes directory +# in the sunstone root dir (/usr/lib/one/sunstone/routes) +# +# Example: +# $ ls /usr/lib/one/sunstone/routes +# custom.rb other.rb +# +# $ cat /usr/lib/one/sunstone/routes/example.rb +# get '/custom' do +# body "It works" +# end +# +#:routes: +# - custom +# - other diff --git a/src/sunstone/models/SunstoneServer.rb b/src/sunstone/models/SunstoneServer.rb index 80cad5ca87..f0db09406a 100644 --- a/src/sunstone/models/SunstoneServer.rb +++ b/src/sunstone/models/SunstoneServer.rb @@ -237,6 +237,45 @@ class SunstoneServer < CloudServer return vnc.proxy(resource) end + # Retrieves the accounting data for all the VMs in the pool + # + # @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 + # returned hash. This will be the first level of the hash (i.e: VM/UID) + # @option params [String] :order_by_2 Xpath expression to group the + # returned hash. This will be the second level of the hash (i.e: VM/ID) + # + # @return [String] json representing the accounting data + def accounting(options) + opts = { + :start_time => options[:start_time], + :end_time => options[:end_time], + :host => options[:host], + :group => options[:group], + :order_by_1 => options[:order_by_1], + :order_by_2 => options[:order_by_2], + :xpath => options[:xpath] + } + + pool = VirtualMachinePool.new(@client) + acct_hash = pool.accounting(user_flag, opts) + + if OpenNebula.is_error?(acct_hash) + error = Error.new(acct_hash.message) + return [500, error.to_json] + end + + return [201, acct_hash.to_json] + end + ######################################################################## # ######################################################################## diff --git a/src/sunstone/sunstone-server.rb b/src/sunstone/sunstone-server.rb index 482946a859..80ec48eb01 100755 --- a/src/sunstone/sunstone-server.rb +++ b/src/sunstone/sunstone-server.rb @@ -205,6 +205,15 @@ after do end end +############################################################################## +# Custom routes +############################################################################## +if settings.config[:routes] + settings.config[:routes].each { |route| + require "routes/#{route}" + } +end + ############################################################################## # HTML Requests ############################################################################## @@ -299,6 +308,14 @@ get '/vm/:id/log' do @SunstoneServer.get_vm_log(params[:id]) end +############################################################################## +# Accounting +############################################################################## + +get '/acct' do + @SunstoneServer.accounting(params) +end + ############################################################################## # Monitoring ############################################################################## diff --git a/src/template/Template.cc b/src/template/Template.cc index e461a4afbe..f77aacd62b 100644 --- a/src/template/Template.cc +++ b/src/template/Template.cc @@ -417,27 +417,13 @@ bool Template::get( const string& name, int& value) const { - string sval; + float fvalue; + bool rc; - get(name, sval); + rc = get(name, fvalue); + value = static_cast(fvalue); - if ( sval == "" ) - { - value = 0; - return false; - } - - istringstream iss(sval); - - iss >> value; - - if (iss.fail() || !iss.eof()) - { - value = 0; - return false; - } - - return true; + return rc; } /* -------------------------------------------------------------------------- */ diff --git a/src/tm_mad/common/mkimage b/src/tm_mad/common/mkimage index 4c279f1f78..d393a16486 100755 --- a/src/tm_mad/common/mkimage +++ b/src/tm_mad/common/mkimage @@ -46,20 +46,21 @@ DST_PATH=`arg_path $DST` DST_HOST=`arg_host $DST` DST_DIR=`dirname $DST_PATH` -if ! echo "$FSTYPE"|grep -q ^vmdk; then - ssh_make_path $DST_HOST $DST_DIR -else - ssh_make_path $DST_HOST $DST_PATH -fi - #------------------------------------------------------------------------------- # Make the new image (file-based) #------------------------------------------------------------------------------- -MKFS_CMD=`mkfs_command $DST_PATH $FSTYPE $SIZE` +case "$FSTYPE" in + "vmdk_"*) + ssh_make_path $DST_HOST $DST_PATH + CREATE_IMAGE="" + ;; + *) + ssh_make_path $DST_HOST $DST_DIR + CREATE_IMAGE="$DD if=/dev/zero of=$DST_PATH bs=1 count=1 seek=${SIZE}M" + ;; +esac -if ! echo "$FSTYPE"|grep -q ^vmdk; then - CREATE_IMAGE="$DD if=/dev/zero of=$DST_PATH bs=1 count=1 seek=${SIZE}M" -fi +MKFS_CMD=`mkfs_command $DST_PATH $FSTYPE $SIZE` MKSCRIPT=$(cat <