mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-21 14:50:08 +03:00
Feature: Added accounting tools for OpenNebula. Contributed by C12G
This commit is contained in:
parent
d756ffe062
commit
61962e5dc3
@ -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"
|
||||
|
||||
|
183
src/acct/oneacct.rb
Normal file
183
src/acct/oneacct.rb
Normal file
@ -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
|
||||
# <http://www.gnu.org/licenses/>
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
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 ts<pstart
|
||||
if te>pend 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
|
||||
|
235
src/cli/oneacct
Executable file
235
src/cli/oneacct
Executable file
@ -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
|
||||
# <http://www.gnu.org/licenses/>
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
|
||||
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<<"<user id=\"#{user}\">\n"
|
||||
|
||||
data[:vms].each do |vmid, vm|
|
||||
xml<<" <vm id=\"#{vmid}\">\n"
|
||||
|
||||
xml<<" "<<xml_tag(:name, vm[:name])
|
||||
xml<<" "<<xml_tag(:time, vm[:time])
|
||||
xml<<" "<<xml_tag(:cpu, vm[:slices].first[:cpu])
|
||||
xml<<" "<<xml_tag(:mem, vm[:slices].first[:mem])
|
||||
xml<<" "<<xml_tag(:net_rx, vm[:network][:net_rx])
|
||||
xml<<" "<<xml_tag(:net_tx, vm[:network][:net_tx])
|
||||
|
||||
vm[:slices].each do |slice|
|
||||
xml<<" <slice seq=\"#{slice[:seq]}\">\n"
|
||||
|
||||
slice.each do |key, value|
|
||||
xml<<" "<<xml_tag(key, value)
|
||||
end
|
||||
|
||||
xml<<" </slice>\n"
|
||||
end
|
||||
|
||||
xml<<" </vm>\n"
|
||||
end
|
||||
|
||||
xml<<"</user>\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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user