1
0
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:
Javi Fontan 2011-11-29 17:17:53 +01:00 committed by Ruben S. Montero
parent d756ffe062
commit 61962e5dc3
3 changed files with 420 additions and 0 deletions

View File

@ -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
View 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
View 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