diff --git a/install.sh b/install.sh
index 669dfeee3c..6a9cccc32a 100755
--- a/install.sh
+++ b/install.sh
@@ -484,6 +484,7 @@ INSTALL_ETC_FILES=(
BIN_FILES="src/nebula/oned \
src/scheduler/src/sched/mm_sched \
src/cli/onevm \
+ src/cli/oneacct \
src/cli/onehost \
src/cli/onevnet \
src/cli/oneuser \
@@ -1180,6 +1181,7 @@ ACCT_BIN_FILES="src/acct/oneacctd"
ACCT_LIB_FILES="src/acct/monitoring.rb \
src/acct/accounting.rb \
src/acct/acctd.rb \
+ src/acct/oneacct.rb \
src/acct/watch_helper.rb \
src/acct/watch_client.rb"
diff --git a/src/acct/oneacct.rb b/src/acct/oneacct.rb
new file mode 100644
index 0000000000..2ad12cd891
--- /dev/null
+++ b/src/acct/oneacct.rb
@@ -0,0 +1,183 @@
+# --------------------------------------------------------------------------
+# Copyright 2010-2011, C12G Labs S.L.
+#
+# This file is part of OpenNebula addons.
+#
+# OpenNebula addons are 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 3 of
+# the License, or the hope That it will be useful, but (at your
+# option) any later version.
+#
+# OpenNebula addons are distributed in 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 OpenNebula addons. If not, see
+#
+# --------------------------------------------------------------------------
+
+
+
+require 'acct/watch_helper'
+
+class AcctClient
+ def initialize(filters={})
+ @filters=filters
+ @deltas=[]
+ @users={}
+ end
+
+ def account(time_start=nil, time_end=nil, user_id=nil)
+ @filters[:start]=time_start if time_start
+ @filters[:end]=time_end if time_end
+ @filters[:user]=user_id if user_id
+
+ get_users_consumption
+
+ @users
+ end
+
+private
+
+ def get_users_consumption
+ # Get all the deltas that match the filters
+ @deltas=calculate_deltas.map {|q| q.values }
+
+ @users=slices_by_user
+
+ user_slices_and_deltas_to_vms
+ end
+
+ def slices_by_user
+ # Get all VM slices that match the filters
+ query=get_vm_slices(@filters)
+
+ # This hash will hold the users with the resources consumed
+ users={}
+
+ query.each do |reg|
+ vm=reg.vm
+ uid=vm.uid.to_i
+
+ # Create a new user register if it still does not exist
+ user=users[uid]||={
+ :vm_slices => [],
+ }
+
+ user[:vm_slices] << reg.values
+ end
+
+ users
+ end
+
+ def user_slices_and_deltas_to_vms
+ @users.each do |user, data|
+ # Get the VM ids array for this user
+ vms=data[:vm_slices].map {|vm| vm[:id] }.sort.uniq
+
+ data[:vms]={}
+
+ vms.each do |vm|
+ # Get the slices array for this VM
+ slices=data[:vm_slices].select {|slice| slice[:id]==vm }
+
+ data[:vms][vm]={
+ :slices => [],
+ :time => 0,
+ }
+
+ # Get the deltas sum for this VM
+ vm_delta=@deltas.find {|d| d[:vm_id]==vm }
+
+ data[:vms][vm][:network]=vm_delta
+ data[:vms][vm][:vmid]=vm
+
+ # Calculate the time consumed by the VM
+ slices.each do |slice|
+ data[:vms][vm][:slices] << slice
+
+ time=calculate_time(slice,
+ @filters[:start], @filters[:end])
+ data[:vms][vm][:time]+=time
+ end
+ end
+
+ # Delete redundant slices data
+ data.delete(:vm_slices)
+ end
+ end
+
+ def get_vm_slices(filters={})
+ vms=WatchHelper::Register
+
+ query=vms.join(:vms, :id => :vm_id)
+ query=query.filter({:vms__uid => filters[:user]}) if filters[:user]
+ query=query.filter(
+ {:retime => 0} | (:retime > filters[:start])) if filters[:start]
+ query=query.filter(:rstime <= filters[:end]) if filters[:end]
+
+ query
+ end
+
+ def get_deltas(filters={})
+ if filters[:data]
+ query=filters[:data]
+ else
+ query=WatchHelper::VmDelta
+ end
+
+ query=query.filter( :ptimestamp >= filters[:start] ) if filters[:start]
+ query=query.filter( :ptimestamp <= filters[:end] ) if filters[:end]
+ query=query.filter( { :vm_id => filters[:vmid] } ) if filters[:vmid]
+
+ query
+ end
+
+ def calculate_deltas
+ query=WatchHelper::VmDelta.select(
+ :ptimestamp, :vm_id,
+ 'sum(net_tx) AS net_tx'.lit, 'sum(net_rx) AS net_rx'.lit)
+
+ query=query.group(:vm_id)
+
+ new_filters=@filters.merge(:data => query)
+
+ get_deltas(new_filters)
+ end
+
+ def calculate_time(slice, period_start, period_end)
+ ts=slice[:rstime].to_i
+ te=slice[:retime].to_i
+
+ pstart=period_start.to_i
+ pend=period_end.to_i
+
+ pend=Time.now.to_i if pend==0
+
+ ts=pstart if tspend or te==0
+ te=pend
+ end
+
+ te-ts
+ end
+end
+
+if $0 == __FILE__
+
+ require 'json'
+
+ acct=AcctClient.new(
+ :start => 1319476322,
+ :end => 1319637455
+ )
+
+ a=acct.account()
+
+ puts JSON.pretty_generate(a)
+
+end
+
diff --git a/src/cli/oneacct b/src/cli/oneacct
new file mode 100755
index 0000000000..065d2c09eb
--- /dev/null
+++ b/src/cli/oneacct
@@ -0,0 +1,235 @@
+#!/usr/bin/env ruby
+
+# --------------------------------------------------------------------------
+# Copyright 2010-2011, C12G Labs S.L.
+#
+# This file is part of OpenNebula addons.
+#
+# OpenNebula addons are 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 3 of
+# the License, or the hope That it will be useful, but (at your
+# option) any later version.
+#
+# OpenNebula addons are distributed in 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 OpenNebula addons. If not, see
+#
+# --------------------------------------------------------------------------
+
+
+ONE_LOCATION=ENV['ONE_LOCATION']
+
+$: << ONE_LOCATION+'/lib/ruby'
+$: << ONE_LOCATION+'/lib/ruby/cli'
+
+require 'rubygems'
+
+require 'acct/oneacct'
+require 'cli/one_helper'
+require 'cli/command_parser'
+require 'json'
+
+require 'optparse'
+require 'optparse/time'
+
+REG_DATE=/((\d{4})\/)?(\d\d?)(\/(\d\d?))?/
+REG_TIME=/(\d\d?):(\d\d?)(:(\d\d?))?/
+
+class AcctHelper
+
+ def format_vm(options=nil)
+ table = CLIHelper::ShowTable.new(nil, nil) do
+ column :VMID, "VM ID", :size=>4 do |d|
+ d[:vmid]
+ end
+
+ column :MEMORY, "Consumed memory", :right, :size=>8 do |d|
+ OpenNebulaHelper.unit_to_str(
+ d[:slices].first[:mem]*1024,
+ {})
+ end
+
+ column :CPU, "Group of the User", :right, :size=>8 do |d|
+ d[:slices].first[:cpu]
+ end
+
+ column :NETRX, "Group of the User", :right, :size=>10 do |d|
+ OpenNebulaHelper.unit_to_str(
+ d[:network][:net_rx]/1024.0,
+ {})
+ end
+
+ column :NETTX, "Group of the User", :right, :size=>10 do |d|
+ OpenNebulaHelper.unit_to_str(
+ d[:network][:net_tx]/1024.0,
+ {})
+ end
+
+ column :TIME, "Group of the User", :right, :size=>15 do |d|
+ OpenNebulaHelper.time_to_str(d[:time])
+ end
+
+ default :VMID, :MEMORY, :CPU, :NETRX, :NETTX, :TIME
+ end
+
+ table
+ end
+
+ def list_vms(data)
+ format_vm().show(data)
+ end
+
+ def list_users(filters)
+ a=gen_accounting(filters)
+
+ a.each do |user, data|
+
+ CLIHelper.scr_bold
+ CLIHelper.scr_underline
+ puts "# User #{user}"
+ CLIHelper.scr_restore
+ puts
+
+ vms=data[:vms].map do |k, v|
+ v
+ end
+
+ self.list_vms(vms)
+
+ puts
+ puts
+
+ end
+ end
+
+ def gen_accounting(filters)
+ acct=AcctClient.new(filters)
+ acct.account()
+ end
+
+ def gen_json(filters)
+ begin
+ require 'json'
+ rescue LoadError
+ STDERR.puts "JSON gem is needed to give the result in this format"
+ exit(-1)
+ end
+
+ acct=gen_accounting(filters)
+ acct.to_json
+ end
+
+ def xml_tag(tag, value)
+ "<#{tag}>#{value}#{tag}>\n"
+ end
+
+ def gen_xml(filters)
+ acct=gen_accounting(filters)
+
+ xml=""
+
+ acct.each do |user, data|
+ xml<<"\n"
+
+ data[:vms].each do |vmid, vm|
+ xml<<" \n"
+
+ xml<<" "<\n"
+
+ slice.each do |key, value|
+ xml<<" "<\n"
+ end
+
+ xml<<" \n"
+ end
+
+ xml<<"\n"
+ end
+
+ xml
+ end
+end
+
+
+@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", Integer,
+ "User id to make accounting" ) do |ext|
+ @options[:user]=ext.to_i
+ 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()
+end
+
+
+begin
+ opts.parse!(ARGV)
+rescue OptionParser::ParseError => e
+ STDERR.puts "Error: " << e.message
+ exit(-1)
+end
+
+
+acct_helper=AcctHelper.new
+
+
+filters=Hash.new
+
+filters[:start]=@options[:start].to_i if @options[:start]
+filters[:end]=@options[:end].to_i if @options[:end]
+filters[:user]=@options[:user].to_i if @options[:user]
+
+
+case @options[:format]
+when :table
+ acct_helper.list_users(filters)
+when :json
+ puts acct_helper.gen_json(filters)
+when :xml
+ puts acct_helper.gen_xml(filters)
+end
+
+
+
+
+
+