1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-08 20:58:17 +03:00

Merge branch 'feature-3264'

Conflicts:
	src/sunstone/public/js/plugins/templates-tab.js
	src/vmm_mad/remotes/vcenter/vcenter_driver.rb
This commit is contained in:
Ruben S. Montero 2014-11-27 16:35:24 +01:00
commit 036600bc74
40 changed files with 1923 additions and 106 deletions

View File

@ -390,7 +390,7 @@ public:
*/
static string local_db_version()
{
return "4.9.80";
return "4.11.80";
}
/**

View File

@ -84,6 +84,15 @@ namespace one_util
const std::string& st,
char delim,
bool clean_empty=true);
/**
* Creates a string from the given float, using fixed notation. If the
* number has any decimals, they will be truncated to 2.
*
* @param num
* @return
*/
std::string float_to_str(const float &num);
};
#endif /* _NEBULA_UTIL_H_ */

View File

@ -82,6 +82,17 @@ public:
*/
int xpath(int& value, const char * xpath_expr, const int& def);
/**
* Gets and sets a xpath attribute, if the attribute is not found a default
* is used
* @param value to set
* @param xpath_expr of the xml element
* @param def default value if the element is not found
*
* @return -1 if default was set
*/
int xpath(float& value, const char * xpath_expr, const float& def);
/**
* Gets and sets a xpath attribute, if the attribute is not found a default
* is used

View File

@ -207,15 +207,6 @@ protected:
*/
void cleanup_quota(const string& qid);
/**
* Creates a string from the given float, using fixed notation. If the
* number has any decimals, they will be truncated to 2.
*
* @param num
* @return
*/
string float_to_str(const float &num);
private:
/**
* Creates an empty quota based on the given attribute. The attribute va

View File

@ -151,6 +151,31 @@ public:
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
class VirtualMachinePoolShowback : public RequestManagerPoolInfoFilter
{
public:
VirtualMachinePoolShowback():
RequestManagerPoolInfoFilter("VirtualMachinePoolShowback",
"Returns the virtual machine showback records",
"A:siiiii")
{
Nebula& nd = Nebula::instance();
pool = nd.get_vmpool();
auth_object = PoolObjectSQL::VM;
};
~VirtualMachinePoolShowback(){};
/* -------------------------------------------------------------------- */
void request_execute(
xmlrpc_c::paramList const& paramList, RequestAttributes& att);
};
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
class VirtualMachinePoolMonitoring : public RequestManagerPoolInfoFilter
{
public:

View File

@ -363,4 +363,29 @@ public:
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
class VirtualMachinePoolCalculateShowback : public RequestManagerVirtualMachine
{
public:
VirtualMachinePoolCalculateShowback():
RequestManagerVirtualMachine("VirtualMachinePoolCalculateShowback",
"Processes all the history records, and stores the monthly cost for each VM",
"A:sii")
{
Nebula& nd = Nebula::instance();
pool = nd.get_vmpool();
auth_object = PoolObjectSQL::VM;
};
~VirtualMachinePoolCalculateShowback(){};
/* -------------------------------------------------------------------- */
void request_execute(
xmlrpc_c::paramList const& paramList, RequestAttributes& att);
};
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
#endif

View File

@ -1517,10 +1517,12 @@ private:
ostringstream oss_vm(VirtualMachine::db_bootstrap);
ostringstream oss_monit(VirtualMachine::monit_db_bootstrap);
ostringstream oss_hist(History::db_bootstrap);
ostringstream oss_showback(VirtualMachine::showback_db_bootstrap);
rc = db->exec(oss_vm);
rc += db->exec(oss_monit);
rc += db->exec(oss_hist);
rc += db->exec(oss_showback);
return rc;
};
@ -1714,6 +1716,12 @@ protected:
static const char * monit_db_bootstrap;
static const char * showback_table;
static const char * showback_db_names;
static const char * showback_db_bootstrap;
/**
* Reads the Virtual Machine (identified with its OID) from the database.
* @param db pointer to the db

View File

@ -230,6 +230,29 @@ public:
int time_start,
int time_end);
/**
* Dumps the VM showback information in XML format. A filter can be also
* added to the query as well as a time frame.
* @param oss the output stream to dump the pool contents
* @param where filter for the objects, defaults to all
* @param start_month First month (+year) to include. January is 1.
* Use -1 to unset
* @param start_year First year (+month) to include. e.g. 2014.
* Use -1 to unset
* @param end_month Last month (+year) to include. January is 1.
* Use -1 to unset
* @param end_year Last year (+month) to include. e.g. 2014.
* Use -1 to unset
*
* @return 0 on success
*/
int dump_showback(ostringstream& oss,
const string& where,
int start_month,
int start_year,
int end_month,
int end_year);
/**
* Dumps the VM monitoring information entries in XML format. A filter
* can be also added to the query.
@ -260,6 +283,28 @@ public:
return dump_monitoring(oss, filter.str());
}
/**
* Processes all the history records, and stores the monthly cost for each
* VM
* @param start_month First month (+year) to process. January is 1.
* Use -1 to unset
* @param start_year First year (+month) to process. e.g. 2014.
* Use -1 to unset
* @param end_month Last month (+year) to process. January is 1.
* Use -1 to unset
* @param end_year Last year (+month) to process. e.g. 2014.
* Use -1 to unset
* @param error_str Returns the error reason, if any
*
* @return 0 on success
*/
int calculate_showback(
int start_month,
int start_year,
int end_month,
int end_year,
string &error_str);
private:
/**
* Factory method to produce VM objects
@ -279,6 +324,11 @@ private:
* True or false whether to submit new VM on HOLD or not
*/
static bool _submit_on_hold;
/**
* Callback used in calculate_showback
*/
int min_stime_cb(void * _min_stime, int num, char **values, char **names);
};
#endif /*VIRTUAL_MACHINE_POOL_H_*/

View File

@ -591,6 +591,7 @@ BIN_FILES="src/nebula/oned \
src/scheduler/src/sched/mm_sched \
src/cli/onevm \
src/cli/oneacct \
src/cli/oneshowback \
src/cli/onehost \
src/cli/onevnet \
src/cli/oneuser \
@ -1180,7 +1181,8 @@ ONEDB_SHARED_MIGRATOR_FILES="src/onedb/shared/2.0_to_2.9.80.rb \
src/onedb/shared/4.5.80_to_4.6.0.rb"
ONEDB_LOCAL_MIGRATOR_FILES="src/onedb/local/4.5.80_to_4.7.80.rb \
src/onedb/local/4.7.80_to_4.9.80.rb"
src/onedb/local/4.7.80_to_4.9.80.rb \
src/onedb/local/4.9.80_to_4.11.80.rb"
#-------------------------------------------------------------------------------
# Configuration files for OpenNebula, to be installed under $ETC_LOCATION
@ -1458,7 +1460,8 @@ CLI_BIN_FILES="src/cli/onevm \
src/cli/onezone \
src/cli/oneflow \
src/cli/oneflow-template \
src/cli/oneacct"
src/cli/oneacct \
src/cli/oneshowback"
CLI_CONF_FILES="src/cli/etc/onegroup.yaml \
src/cli/etc/onehost.yaml \
@ -1471,7 +1474,8 @@ CLI_CONF_FILES="src/cli/etc/onegroup.yaml \
src/cli/etc/onedatastore.yaml \
src/cli/etc/onecluster.yaml \
src/cli/etc/onezone.yaml \
src/cli/etc/oneacct.yaml"
src/cli/etc/oneacct.yaml \
src/cli/etc/oneshowback.yaml"
#-----------------------------------------------------------------------------
# Sunstone files
@ -1802,6 +1806,7 @@ ONEFLOW_LIB_MODELS_FILES="src/flow/lib/models/role.rb \
#-----------------------------------------------------------------------------
MAN_FILES="share/man/oneacct.1.gz \
share/man/oneshowback.1.gz \
share/man/oneacl.1.gz \
share/man/onehost.1.gz \
share/man/oneimage.1.gz \

View File

@ -763,6 +763,8 @@ VM_RESTRICTED_ATTR = "DISK/WRITE_BYTES_SEC"
VM_RESTRICTED_ATTR = "DISK/TOTAL_IOPS_SEC"
VM_RESTRICTED_ATTR = "DISK/READ_IOPS_SEC"
VM_RESTRICTED_ATTR = "DISK/WRITE_IOPS_SEC"
VM_RESTRICTED_ATTR = "CPU_COST"
VM_RESTRICTED_ATTR = "MEMORY_COST"
#VM_RESTRICTED_ATTR = "RANK"
#VM_RESTRICTED_ATTR = "SCHED_RANK"

View File

@ -48,6 +48,7 @@ env.Man('econe-terminate-instances')
env.Man('econe-upload')
env.Man('oneacct')
env.Man('oneshowback')
env.Man('oneacl')
env.Man('onecluster')
env.Man('onedatastore')

50
share/man/oneshowback.1 Normal file
View File

@ -0,0 +1,50 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "ONESHOWBACK" "1" "November 2014" "" "oneshowback(1) -- OpenNebula Showback Tool"
.
.SH "NAME"
\fBoneshowback\fR
.
.SH "SYNOPSIS"
\fBoneshowback\fR \fIcommand\fR [\fIoptions\fR]
.
.SH "OPTIONS"
.
.nf
\-s, \-\-start TIME First month of the data
\-e, \-\-end TIME Last month of the data
\-u, \-\-userfilter user User name or id to filter the results
\-g, \-\-group group Group name or id to filter the results
\-x, \-\-xml Show the resource in xml format
\-j, \-\-json Show the resource in json format
\-v, \-\-verbose Verbose mode
\-h, \-\-help Show this message
\-V, \-\-version Show version and copyright information
\-\-describe Describe list columns
\-l, \-\-list x,y,z Selects columns to display with list command
\-\-csv Write table in csv format
\-\-user name User name used to connect to OpenNebula
\-\-password password Password to authenticate with OpenNebula
\-\-endpoint endpoint URL of OpenNebula xmlrpc frontend
.
.fi
.
.SH "COMMANDS"
.
.IP "\(bu" 4
list Returns the showback records valid options: start_time, end_time, userfilter, group, xml, json, verbose, help, version, describe, list, csv, user, password, endpoint
.
.IP "\(bu" 4
calculate Calculates the showback records valid options: start_time, end_time
.
.IP "" 0
.
.SH "ARGUMENT FORMATS"
.
.SH "LICENSE"
OpenNebula 4\.10\.0 Copyright 2002\-2014, OpenNebula Project (OpenNebula\.org), C12G Labs
.
.P
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

View File

@ -0,0 +1,50 @@
---
:UID:
:desc: User ID
:size: 4
:USER_NAME:
:desc: User name
:size: 12
:GID:
:desc: Group ID
:size: 4
:GROUP_NAME:
:desc: Group name
:size: 12
:VM_ID:
:desc: Virtual Machine ID
:size: 6
:VM_NAME:
:desc: Virtual Machine name
:size: 12
:MONTH:
:desc: Month
:size: 5
:YEAR:
:desc: Year
:size: 5
:HOURS:
:desc: Hours
:size: 6
:COST:
:desc: Cost
:size: 15
:default:
- :USER_NAME
- :GROUP_NAME
- :VM_ID
- :VM_NAME
- :MONTH
- :YEAR
- :HOURS
- :COST

View File

@ -18,19 +18,35 @@ require 'one_helper'
require 'optparse/time'
class AcctHelper < OpenNebulaHelper::OneHelper
START_TIME = {
START_TIME_ACCT = {
:name => "start_time",
:short => "-s TIME",
:large => "--start TIME" ,
:description => "Start date and time to take into account",
:description => "First day of the data to retrieve",
:format => Time
}
END_TIME = {
END_TIME_ACCT = {
:name => "end_time",
:short => "-e TIME",
:large => "--end TIME" ,
:description => "End date and time",
:description => "Last day of the data to retrieve",
:format => Time
}
START_TIME_SHOWBACK = {
:name => "start_time",
:short => "-s TIME",
:large => "--start TIME" ,
:description => "First month of the data",
:format => Time
}
END_TIME_SHOWBACK = {
:name => "end_time",
:short => "-e TIME",
:large => "--end TIME" ,
:description => "Last month of the data",
:format => Time
}
@ -95,8 +111,8 @@ class AcctHelper < OpenNebulaHelper::OneHelper
:description => "Split the output in a table for each VM"
}
ACCT_OPTIONS = [START_TIME, END_TIME, USERFILTER, GROUP, HOST, XPATH, XML, JSON, SPLIT]
ACCT_OPTIONS = [START_TIME_ACCT, END_TIME_ACCT, USERFILTER, GROUP, HOST, XPATH, XML, JSON, SPLIT]
SHOWBACK_OPTIONS = [START_TIME_SHOWBACK, END_TIME_SHOWBACK, USERFILTER, GROUP, XML, JSON]
ACCT_TABLE = CLIHelper::ShowTable.new("oneacct.yaml", nil) do
column :UID, "User ID", :size=>4 do |d|
@ -152,6 +168,50 @@ class AcctHelper < OpenNebulaHelper::OneHelper
default :VID, :HOSTNAME, :ACTION, :REASON, :START_TIME, :END_TIME, :MEMORY, :CPU, :NET_RX, :NET_TX
end
SHOWBACK_TABLE = CLIHelper::ShowTable.new("oneshowback.yaml", nil) do
column :UID, "User ID", :size=>4 do |d|
d["UID"]
end
column :USER_NAME, "User name", :left, :size=>12 do |d|
d["UNAME"]
end
column :GID, "Group ID", :size=>4 do |d|
d["GID"]
end
column :GROUP_NAME, "Group name", :left, :size=>12 do |d|
d["GNAME"]
end
column :VM_ID, "Virtual Machine ID", :size=>6 do |d|
d["VMID"]
end
column :VM_NAME, "Virtual Machine name", :left, :size=>12 do |d|
d["VMNAME"]
end
column :MONTH, "Month", :size=>5 do |d|
d["MONTH"]
end
column :YEAR, "Year", :size=>5 do |d|
d["YEAR"]
end
column :HOURS, "Hours", :size=>6 do |d|
d["HOURS"]
end
column :COST, "Cost", :size=>15 do |d|
d["COST"]
end
default :USER_NAME, :GROUP_NAME, :VM_ID, :VM_NAME, :MONTH, :YEAR, :HOURS, :COST
end
def self.print_start_end_time_header(start_time, end_time)
print "Showing active history records from "
@ -184,4 +244,12 @@ class AcctHelper < OpenNebulaHelper::OneHelper
CLIHelper.scr_restore
puts
end
def self.print_month_header(year, month)
CLIHelper.scr_bold
CLIHelper.scr_underline
puts "# Showback for #{month}/#{year}".ljust(80)
CLIHelper.scr_restore
puts
end
end

View File

@ -70,7 +70,7 @@ cmd = CommandParser::CmdParser.new(ARGV) do
if options[:json] || options[:xml]
xml_str = pool.accounting_xml(filter_flag, common_opts)
if OpenNebula.is_error?(xml_str)
puts acct_hash.message
puts xml_str.message
exit -1
end

178
src/cli/oneshowback Executable file
View File

@ -0,0 +1,178 @@
#!/usr/bin/env ruby
# -------------------------------------------------------------------------- #
# Copyright 2002-2014, 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. #
#--------------------------------------------------------------------------- #
ONE_LOCATION=ENV["ONE_LOCATION"]
if !ONE_LOCATION
RUBY_LIB_LOCATION="/usr/lib/one/ruby"
else
RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby"
end
$: << RUBY_LIB_LOCATION
$: << RUBY_LIB_LOCATION+"/cli"
require 'command_parser'
require 'one_helper/oneacct_helper'
require 'json'
cmd = CommandParser::CmdParser.new(ARGV) do
@formats = Hash.new
usage "`oneshowback` <command> [<options>]"
description ""
version OpenNebulaHelper::ONE_VERSION
helper=OpenNebulaHelper::OneHelper.new
before_proc do
helper.set_client(options)
end
command :list, "Returns the showback records", :options =>
AcctHelper::SHOWBACK_OPTIONS + CommandParser::OPTIONS +
[OpenNebulaHelper::DESCRIBE, CLIHelper::LIST, CLIHelper::CSV_OPT] +
OpenNebulaHelper::CLIENT_OPTIONS do
if options[:describe]
AcctHelper::SHOWBACK_TABLE.describe_columns
exit(0)
end
filter_flag = (options[:userfilter] || VirtualMachinePool::INFO_ALL)
start_month = -1
start_year = -1
if (options[:start_time])
start_month = options[:start_time].month
start_year = options[:start_time].year
end
end_month = -1
end_year = -1
if (options[:end_time])
end_month = options[:end_time].month
end_year = options[:end_time].year
end
common_opts = {
:start_month => start_month,
:start_year => start_year,
:end_month => end_month,
:end_year => end_year,
:group => options[:group],
:xpath => options[:xpath]
}
pool = OpenNebula::VirtualMachinePool.new(helper.client)
if options[:json] || options[:xml]
xml_str = pool.showback_xml(filter_flag, common_opts)
if OpenNebula.is_error?(xml_str)
puts xml_str.message
exit -1
end
if options[:json]
xmldoc = XMLElement.new
xmldoc.initialize_xml(xml_str, 'SHOWBACK_RECORDS')
puts JSON.pretty_generate(xmldoc.to_hash)
elsif options[:xml]
puts xml_str
end
exit_code 0
else
order_by = Hash.new
if !options[:csv]
order_by[:order_by_1] = 'YEAR'
order_by[:order_by_2] = 'MONTH'
end
data_hash = pool.showback(filter_flag,
common_opts.merge(order_by))
if OpenNebula.is_error?(data_hash)
puts data_hash.message
exit -1
end
if options[:csv]
a = Array.new
if data_hash['SHOWBACK_RECORDS']
a = data_hash['SHOWBACK_RECORDS']['SHOWBACK']
end
AcctHelper::SHOWBACK_TABLE.show(a, options)
exit(0)
end
data_hash.each { |year, value|
value.each { |month, showback_array|
AcctHelper.print_month_header(year, month)
array = showback_array['SHOWBACK_RECORDS']['SHOWBACK']
AcctHelper::SHOWBACK_TABLE.show(array, options)
puts
}
}
exit_code 0
end
end
command :"calculate", "Calculates the showback records", :options =>
[AcctHelper::START_TIME_SHOWBACK, AcctHelper::END_TIME_SHOWBACK] do
start_month = -1
start_year = -1
if (options[:start_time])
start_month = options[:start_time].month
start_year = options[:start_time].year
end
end_month = -1
end_year = -1
if (options[:end_time])
end_month = options[:end_time].month
end_year = options[:end_time].year
end
rc = OpenNebula::VirtualMachinePool.new(helper.client).
calculate_showback(start_month, start_year, end_month, end_year)
if OpenNebula.is_error?(rc)
warn rc.message
exit -1
else
puts rc
exit_code 0
end
end
end

View File

@ -27,6 +27,7 @@
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <math.h>
using namespace std;
@ -238,3 +239,24 @@ vector<string> one_util::split(const string& st, char delim, bool clean_empty)
return parts;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
string one_util::float_to_str(const float &num)
{
ostringstream oss;
if ( num == ceil(num) )
{
oss.precision(0);
}
else
{
oss.precision(2);
}
oss << fixed << num;
return oss.str();
}

View File

@ -25,9 +25,11 @@ module OpenNebula
VM_POOL_METHODS = {
:info => "vmpool.info",
:monitoring => "vmpool.monitoring",
:accounting => "vmpool.accounting"
:info => "vmpool.info",
:monitoring => "vmpool.monitoring",
:accounting => "vmpool.accounting",
:showback => "vmpool.showback",
:calculate_showback => "vmpool.calculateshowback"
}
# Constants for info queries (include/RequestManagerPoolInfoFilter.h)
@ -162,6 +164,27 @@ module OpenNebula
return @client.call(VM_POOL_METHODS[:monitoring], filter_flag)
end
# Processes all the history records, and stores the monthly cost for
# each VM
#
# @param [Integer] start_month First month (+year) to process. January is 1.
# Use -1 to unset
# @param [Integer] start_year First year (+month) to process. e.g. 2014.
# Use -1 to unset
# @param [Integer] end_month Last month (+year) to process. January is 1.
# Use -1 to unset
# @param [Integer] end_year Last year (+month) to process. e.g. 2014.
# Use -1 to unset
def calculate_showback(start_month, start_year, end_month, end_year)
start_month ||= -1
start_year ||= -1
end_month ||= -1
end_year ||= -1
return @client.call(VM_POOL_METHODS[:calculate_showback],
start_month, start_year, end_month, end_year)
end
# Retrieves the accounting data for all the VMs in the pool
#
# @param [Integer] filter_flag Optional filter flag to retrieve all or
@ -283,6 +306,92 @@ module OpenNebula
xml_str
end
# Retrieves the showback 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_year First month (+year) to take
# into account, if no start time is required use -1
# @option params [Integer] :start_month First year (+month) to take
# into account, if no start time is required use -1
# @option params [Integer] :end_year Last month (+year) to take
# into account, if no end time is required use -1
# @option params [Integer] :end_month Last year (+month) to take
# into account, if no end time is required use -1
# @option params [Integer] :group Group id to filter the results
# @option params [String] :xpath Xpath expression to filter the results.
# For example: SHOWBACK[COST>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 SHOWBACK_RECORDS
def showback(filter_flag=INFO_ALL, options={})
data_hash = Hash.new
rc = build_showback(filter_flag, options) do |record|
hash = data_hash
if options[:order_by_1]
id_1 = record[options[:order_by_1]]
data_hash[id_1] ||= Hash.new
if options[:order_by_2]
id_2 = record[options[:order_by_2]]
data_hash[id_1][id_2] ||= Hash.new
hash = data_hash[id_1][id_2]
else
hash = data_hash[id_1]
end
end
hash["SHOWBACK_RECORDS"] ||= Hash.new
hash["SHOWBACK_RECORDS"]["SHOWBACK"] ||= Array.new
hash["SHOWBACK_RECORDS"]["SHOWBACK"] << record.to_hash['SHOWBACK']
end
return rc if OpenNebula.is_error?(rc)
data_hash
end
# Retrieves the showback 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_year First month (+year) to take
# into account, if no start time is required use -1
# @option params [Integer] :start_month First year (+month) to take
# into account, if no start time is required use -1
# @option params [Integer] :end_year Last month (+year) to take
# into account, if no end time is required use -1
# @option params [Integer] :end_month Last year (+month) to take
# into account, if no end time is required use -1
# @option params [Integer] :group Group id to filter the results
# @option params [String] :xpath Xpath expression to filter the results.
# For example: SHOWBACK[COST>10]
#
# @return [String] the xml representing the showback data
def showback_xml(filter_flag=INFO_ALL, options={})
xml_str = "<SHOWBACK_RECORDS>\n"
rc = build_showback(filter_flag, options) do |showback|
xml_str << showback.to_xml
end
return rc if OpenNebula.is_error?(rc)
xml_str << "\n</SHOWBACK_RECORDS>"
xml_str
end
private
def build_accounting(filter_flag, options, &block)
@ -316,6 +425,38 @@ module OpenNebula
acct_hash
end
def build_showback(filter_flag, options, &block)
xml_str = @client.call(VM_POOL_METHODS[:showback],
filter_flag,
options[:start_month],
options[:start_year],
options[:end_month],
options[:end_year])
return xml_str if OpenNebula.is_error?(xml_str)
xmldoc = XMLElement.new
xmldoc.initialize_xml(xml_str, 'SHOWBACK_RECORDS')
xpath_array = Array.new
xpath_array << "SHOWBACK[GID=#{options[:group]}]" if options[:group]
xpath_array << options[:xpath] if options[:xpath]
if xpath_array.empty?
xpath_str = "SHOWBACK"
else
xpath_str = xpath_array.join(' | ')
end
data_hash = Hash.new
xmldoc.each(xpath_str) do |showback|
block.call(showback)
end
data_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

View File

@ -0,0 +1,42 @@
# -------------------------------------------------------------------------- #
# Copyright 2002-2014, 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 'nokogiri'
module Migrator
def db_version
"4.11.80"
end
def one_version
"OpenNebula 4.11.80"
end
def up
init_log_time()
########################################################################
# Showback
########################################################################
@db.run "CREATE TABLE vm_showback (vmid INTEGER, year INTEGER, month INTEGER, body MEDIUMTEXT, PRIMARY KEY(vmid, year, month));"
log_time()
return true
end
end

View File

@ -302,6 +302,9 @@ void RequestManager::register_xml_methods()
xmlrpc_c::methodPtr vm_pool_acct(new VirtualMachinePoolAccounting());
xmlrpc_c::methodPtr vm_pool_monitoring(new VirtualMachinePoolMonitoring());
xmlrpc_c::methodPtr vm_pool_showback(new VirtualMachinePoolShowback());
xmlrpc_c::methodPtr vm_pool_calculate_showback(new VirtualMachinePoolCalculateShowback());
// VirtualNetwork Methods
xmlrpc_c::methodPtr vn_add_ar(new VirtualNetworkAddAddressRange());
xmlrpc_c::methodPtr vn_rm_ar(new VirtualNetworkRmAddressRange());
@ -438,6 +441,8 @@ void RequestManager::register_xml_methods()
RequestManagerRegistry.addMethod("one.vmpool.info", vm_pool_info);
RequestManagerRegistry.addMethod("one.vmpool.accounting", vm_pool_acct);
RequestManagerRegistry.addMethod("one.vmpool.monitoring", vm_pool_monitoring);
RequestManagerRegistry.addMethod("one.vmpool.showback", vm_pool_showback);
RequestManagerRegistry.addMethod("one.vmpool.calculateshowback", vm_pool_calculate_showback);
/* VM Template related methods*/
RequestManagerRegistry.addMethod("one.template.update", template_update);

View File

@ -165,6 +165,50 @@ void VirtualMachinePoolAccounting::request_execute(
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
void VirtualMachinePoolShowback::request_execute(
xmlrpc_c::paramList const& paramList,
RequestAttributes& att)
{
int filter_flag = xmlrpc_c::value_int(paramList.getInt(1));
int start_month = xmlrpc_c::value_int(paramList.getInt(2));
int start_year = xmlrpc_c::value_int(paramList.getInt(3));
int end_month = xmlrpc_c::value_int(paramList.getInt(4));
int end_year = xmlrpc_c::value_int(paramList.getInt(5));
ostringstream oss;
string where;
int rc;
if ( filter_flag < MINE )
{
failure_response(XML_RPC_API,
request_error("Incorrect filter_flag",""),
att);
return;
}
where_filter(att, filter_flag, -1, -1, "", "", false, false, false, where);
rc = (static_cast<VirtualMachinePool *>(pool))->dump_showback(oss,
where,
start_month,
start_year,
end_month,
end_year);
if ( rc != 0 )
{
failure_response(INTERNAL,request_error("Internal Error",""), att);
return;
}
success_response(oss.str(), att);
return;
}
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
void VirtualMachinePoolMonitoring::request_execute(
xmlrpc_c::paramList const& paramList,
RequestAttributes& att)

View File

@ -2219,3 +2219,41 @@ void VirtualMachineRecover::request_execute(
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void VirtualMachinePoolCalculateShowback::request_execute(
xmlrpc_c::paramList const& paramList,
RequestAttributes& att)
{
int start_month = xmlrpc_c::value_int(paramList.getInt(1));
int start_year = xmlrpc_c::value_int(paramList.getInt(2));
int end_month = xmlrpc_c::value_int(paramList.getInt(3));
int end_year = xmlrpc_c::value_int(paramList.getInt(4));
ostringstream oss;
string where;
int rc;
string error_str;
if ( att.gid != 0 )
{
failure_response(AUTHORIZATION,
authorization_error("Action reserved for group 0 only", att),
att);
return;
}
rc = (static_cast<VirtualMachinePool *>(pool))->calculate_showback(
start_month, start_year, end_month, end_year, error_str);
if (rc != 0)
{
failure_response(AUTHORIZATION,
request_error(error_str, ""),
att);
return;
}
success_response("", att);
return;
}

View File

@ -26,6 +26,8 @@ enabled_tabs:
enterprise-tab: true
zones-tab: true
autorefresh: true
features:
showback: true
tabs:
dashboard-tab:
panel_tabs:
@ -51,6 +53,8 @@ tabs:
panel_tabs:
user_info_tab: true
user_quotas_tab: true
user_accounting_tab: true
user_showback_tab: true
table_columns:
- 0 # Checkbox
- 1 # ID
@ -74,7 +78,11 @@ tabs:
User.delete: true
groups-tab:
panel_tabs:
group_info_tab: true
group_quotas_tab: true
group_providers_tab: true
group_accounting_tab: true
group_shoback_tab: true
table_columns:
- 0 # Checkbox
- 1 # ID

View File

@ -1,6 +1,8 @@
provision_logo: images/one_small_logo.png
enabled_tabs:
provision-tab: true
features:
showback: true
tabs:
provision-tab:
panel_tabs:

View File

@ -25,6 +25,8 @@ enabled_tabs:
community-tab: false
enterprise-tab: false
autorefresh: true
features:
showback: true
tabs:
dashboard-tab:
panel_tabs:
@ -51,6 +53,8 @@ tabs:
panel_tabs:
user_info_tab: true
user_quotas_tab: true
user_accounting_tab: true
user_showback_tab: true
table_columns:
- 0 # Checkbox
- 1 # ID
@ -73,7 +77,11 @@ tabs:
User.delete: true
groups-tab:
panel_tabs:
group_info_tab: true
group_quotas_tab: true
group_providers_tab: true
group_accounting_tab: true
group_shoback_tab: true
table_columns:
- 0 # Checkbox
- 1 # ID

View File

@ -26,6 +26,8 @@ enabled_tabs:
enterprise-tab: true
zones-tab: true
autorefresh: true
features:
showback: true
tabs:
dashboard-tab:
panel_tabs:
@ -51,6 +53,8 @@ tabs:
panel_tabs:
user_info_tab: true
user_quotas_tab: true
user_accounting_tab: true
user_showback_tab: true
table_columns:
- 0 # Checkbox
- 1 # ID
@ -74,7 +78,11 @@ tabs:
User.delete: true
groups-tab:
panel_tabs:
group_info_tab: true
group_quotas_tab: true
group_providers_tab: true
group_accounting_tab: true
group_shoback_tab: true
table_columns:
- 0 # Checkbox
- 1 # ID

View File

@ -1,6 +1,8 @@
provision_logo: images/one_small_logo.png
enabled_tabs:
provision-tab: true
features:
showback: true
tabs:
provision-tab:
panel_tabs:

View File

@ -391,6 +391,33 @@ class SunstoneServer < CloudServer
return [200, rc.to_json]
end
def get_vm_showback(options)
pool = VirtualMachinePool.new(@client)
filter_flag = options[:userfilter] ? options[:userfilter].to_i : VirtualMachinePool::INFO_ALL
start_month = options[:start_month] ? options[:start_month].to_i : -1
start_year = options[:start_year] ? options[:start_year].to_i : -1
end_month = options[:end_month] ? options[:end_month].to_i : -1
end_year = options[:end_year] ? options[:end_year].to_i : -1
acct_options = {
:start_month => start_month,
:start_year => start_year,
:end_month => end_month,
:end_year => end_year,
:group => options[:group]
}
rc = pool.showback(filter_flag, acct_options)
if OpenNebula.is_error?(rc)
error = Error.new(rc.message)
return [500, error.to_json]
end
return [200, rc.to_json]
end
private

View File

@ -639,6 +639,31 @@ var OpenNebula = {
}
});
},
"showback": function(params, resource, path){
var callback = params.success;
var callback_error = params.error;
var data = params.data;
var method = "showback";
var request = OpenNebula.Helper.request(resource,method, data);
var url = path ? path : resource.toLowerCase() + "/showback";
$.ajax({
url: url,
type: "GET",
data: data,
dataType: "json",
success: function(response){
return callback ? callback(request, response) : null;
},
error: function(response){
return callback_error ?
callback_error(request, OpenNebula.Error(response)) : null;
}
});
}
},
"Auth": {
@ -1045,6 +1070,9 @@ var OpenNebula = {
"accounting": function(params){
OpenNebula.Action.accounting(params,OpenNebula.VM.resource);
},
"showback": function(params){
OpenNebula.Action.showback(params,OpenNebula.VM.resource);
}
},
"Group": {

View File

@ -31,6 +31,7 @@ Config = {
var enabled = config['view']['enabled_tabs'][tab_name];
return enabled;
},
"isTabActionEnabled": function(tab_name, action_name, panel_name){
var enabled;
if (panel_name) {
@ -51,6 +52,14 @@ Config = {
}
},
"isFeatureEnabled": function(feature_name){
if (config['view']['features'] && config['view']['features'][feature_name]) {
return true;
} else {
return false;
}
},
"tabTableColumns": function(tab_name){
var columns = config['view']['tabs'][tab_name]['table_columns'];
@ -361,15 +370,6 @@ function setupConfigDialog() {
});
}
function tr(str){
var tmp = locale[str];
if ( tmp == null || tmp == "" ) {
//console.debug("Missing translation: "+str);
tmp = str;
}
return tmp;
};
function updateUserConfigInfo(request,user_json) {
var info = user_json.USER;

View File

@ -734,10 +734,26 @@ function updateGroupInfo(request,group){
content: '<div id="group_accounting"></div>'
};
Sunstone.updateInfoPanelTab("group_info_panel","group_info_tab",info_tab);
Sunstone.updateInfoPanelTab("group_info_panel","group_quotas_tab",quotas_tab);
Sunstone.updateInfoPanelTab("group_info_panel","group_providers_tab",providers_tab);
Sunstone.updateInfoPanelTab("group_info_panel","group_accouning_tab",accounting_tab);
Sunstone.updateInfoPanelTab("group_info_panel","group_accounting_tab",accounting_tab);
if (Config.isFeatureEnabled("showback")) {
var showback_tab = {
title: tr("Showback"),
icon: "fa-money",
content: '<div id="group_showback"></div>'
};
Sunstone.updateInfoPanelTab("group_info_panel","group_showback_tab",showback_tab);
showbackGraphs(
$("#group_showback","#group_info_panel"),
{ fixed_group: info.ID });
}
Sunstone.popUpInfoPanel("group_info_panel", 'groups-tab');
$("#add_rp_button", $("#group_info_panel")).click(function(){

View File

@ -825,9 +825,10 @@ var provision_user_info = '<div id="provision_user_info" class="hidden section_c
'<div class="row">'+
'<div class="large-12 large-centered columns">'+
'<dl class="tabs text-center" data-tab style="width: 100%">'+
'<dd class="active" style="width: 34%;"><a href="#provision_info_settings"><i class="fa fa-fw fa-lg fa-cogs"/>&emsp;'+ tr("Settings") +'</a></dd>'+
'<dd style="width: 33%;"><a href="#provision_info_acct"><i class="fa fa-fw fa-lg fa-bar-chart-o"/>&emsp;'+ tr("Accounting") +'</a></dd>'+
'<dd style="width: 33%;"><a href="#provision_info_quotas"><i class="fa fa-fw fa-lg fa-align-left"/>&emsp;'+ tr("Quotas") +'</a></dd>'+
'<dd class="active" style="width: '+ (Config.isFeatureEnabled("showback") ? '25%' : '33%')+';"><a href="#provision_info_settings"><i class="fa fa-fw fa-lg fa-cogs"/>&emsp;'+ tr("Settings") +'</a></dd>'+
(Config.isFeatureEnabled("showback") ? '<dd style="width: 25%;"><a href="#provision_info_showback"><i class="fa fa-fw fa-lg fa-money"/>&emsp;'+ tr("Showback") +'</a></dd>' : '') +
'<dd style="width: '+ (Config.isFeatureEnabled("showback") ? '25%' : '33%')+';"><a href="#provision_info_acct"><i class="fa fa-fw fa-lg fa-bar-chart-o"/>&emsp;'+ tr("Accounting") +'</a></dd>'+
'<dd style="width: '+ (Config.isFeatureEnabled("showback") ? '25%' : '33%')+';"><a href="#provision_info_quotas"><i class="fa fa-fw fa-lg fa-align-left"/>&emsp;'+ tr("Quotas") +'</a></dd>'+
'</dl>'+
'<br>'+
'</div>'+
@ -839,6 +840,12 @@ var provision_user_info = '<div id="provision_user_info" class="hidden section_c
'</div>'+
'</div>'+
'</div>'+
(Config.isFeatureEnabled("showback") ? '<div class="content" id="provision_info_showback">'+
'<div class="row">'+
'<div id="provision_user_info_showback_div" class="large-12 large-centered columns">'+
'</div>'+
'</div>'+
'</div>' : '')+
'<div class="content" id="provision_info_quotas">'+
'<div class="row">'+
'<div id="provision_user_info_quotas_div" class="large-9 large-centered columns quotas">'+
@ -1199,6 +1206,21 @@ var provision_manage_vdc = '<div id="provision_manage_vdc" class="hidden section
'</div>'+
'</div>'+
'<br>'+
(Config.isFeatureEnabled("showback") ? '<div class="row">'+
'<div class="large-11 large-centered columns">'+
'<h3 class="subheader text-right">'+
'<span class="left">'+
tr("VDC Showback")+
'</span>'+
'</h3>'+
'</div>'+
'</div>'+
'<br>'+
'<div class="row">'+
'<div id="provision_info_vdc_group_showback" class="large-10 large-centered columns">'+
'</div>'+
'</div>'+
'<br>' : '') +
'<div class="row">'+
'<div class="large-11 large-centered columns">'+
'<h3 class="subheader text-right">'+
@ -1899,10 +1921,10 @@ function generate_custom_attrs(context, custom_attrs) {
}
}
function generate_cardinality_selector(context, role_template) {
function generate_cardinality_selector(context, role_template, template_json) {
context.off();
var min_vms = (role_template.min_vms||1);
var max_vms = (role_template.max_vms||100);
var max_vms = (role_template.max_vms||20);
context.html(
'<br>'+
@ -1922,33 +1944,60 @@ function generate_cardinality_selector(context, role_template) {
'<div class="row">'+
'<div class="large-12 columns">'+
'<div class="row">'+
'<div class="large-5 text-center columns">'+
'<span class="cardinality_value" style="color: #777; font-size:60px">'+role_template.cardinality+'</span>'+
'<div class="large-2 text-center columns">'+
'<span class="cardinality_value" style="color: #777; font-size:40px">'+role_template.cardinality+'</span>'+
'<br>'+
'<span style="color: #999;">'+tr("VMs")+'</span>'+
'</div>'+
'<div class="large-7 columns">'+
'<div class="cardinality_slider_div">'+
'<span class="" style="color: #777;">'+tr("Change cardinality")+'</span>'+
'<br>'+
'<div class="range-slider radius cardinality_slider" data-slider data-options="start: 1; end: 50;">'+
'<span class="range-slider-handle"></span>'+
'<span class="range-slider-active-segment"></span>'+
'<input type="hidden">'+
'<div class="large-6 columns">'+
'<div class="cardinality_slider_div">'+
'<span class="" style="color: #777;">'+tr("Change cardinality")+'</span>'+
'<br>'+
'<div class="range-slider radius cardinality_slider" data-slider data-options="start: 1; end: 50;">'+
'<span class="range-slider-handle"></span>'+
'<span class="range-slider-active-segment"></span>'+
'<input type="hidden">'+
'</div>'+
'<span class="left" style="color: #999;">'+min_vms+'</span>'+
'<span class="right" style="color: #999;">'+max_vms+'</span>'+
'</div>'+
'<div class="cardinality_no_slider_div">'+
'<br>'+
'<br>'+
'<span class="" style="color: #999;">'+tr("The cardinality for this role cannot be changed")+'</span>'+
'</div>'+
'<span class="left" style="color: #999;">'+min_vms+'</span>'+
'<span class="right" style="color: #999;">'+max_vms+'</span>'+
'</div>'+
'<div class="cardinality_no_slider_div">'+
'<div class="large-4 columns text-center provision_create_service_cost_div hidden">'+
'<span class="cost_value" style="color: #777; font-size:40px"></span>'+
'<br>'+
'<br>'+
'<span class="" style="color: #999;">'+tr("The cardinality for this role cannot be changed")+'</span>'+
'</div>'+
'<span style="color: #999;">'+tr("COST")+' / ' + tr("HOUR") + '</span>'+
'</div>'+
'</div>'+
'</div>'+
'</div>');
var capacity = template_json.VMTEMPLATE.TEMPLATE;
var cost = 0;
if (capacity.CPU_COST || capacity.MEMORY_COST && Config.isFeatureEnabled("showback")) {
$(".provision_create_service_cost_div").show();
if (capacity.CPU && capacity.CPU_COST) {
cost += capacity.CPU * capacity.CPU_COST
$(".cost_value").data("CPU_COST", capacity.CPU_COST);
}
if (capacity.MEMORY && capacity.MEMORY_COST) {
cost += capacity.MEMORY * capacity.MEMORY_COST
$(".cost_value").data("MEMORY_COST", capacity.MEMORY_COST);
}
$(".provision_create_service_cost_div", context).data("cost", cost)
var cost_value = cost*parseInt(role_template.cardinality);
$(".cost_value").html(cost_value.toFixed(2));
} else {
$(".provision_create_service_cost_div").hide();
}
if (max_vms > min_vms) {
$( ".cardinality_slider", context).attr('data-options', 'start: '+min_vms+'; end: '+max_vms+';')
context.foundation();
@ -1959,9 +2008,9 @@ function generate_cardinality_selector(context, role_template) {
$( ".cardinality_slider", context).on('change', function(){
$(".cardinality_value",context).html($(this).attr('data-slider'))
var cost_value = $(".provision_create_service_cost_div", context).data("cost")*$(this).attr('data-slider');
$(".cost_value").html(cost_value.toFixed(2));
});
} else {
$( ".cardinality_slider_div", context).hide();
$( ".cardinality_no_slider_div", context).show();
@ -2000,18 +2049,23 @@ function generate_provision_instance_type_accordion(context, capacity) {
'<div class="row">'+
'<div class="large-12 large-centered columns">'+
'<div class="row text-center">'+
'<div class="large-6 columns">'+
'<div class="large-4 columns">'+
'<span class="cpu_value" style="color: #777; font-size:60px">'+capacity.CPU+'</span>'+
'<br>'+
'<span style="color: #999;">'+tr("CPU")+'</span>'+
'</div>'+
'<div class="large-6 columns">'+
'<div class="large-4 columns">'+
'<span class="memory_value" style="color: #777; font-size:60px">'+memory_value+'</span>'+
' '+
'<span class="memory_unit" style="color: #777; font-size:30px">'+memory_unit+'</span>'+
'<br>'+
'<span style="color: #999;">'+tr("MEMORY")+'</span>'+
'</div>'+
'<div class="large-4 columns provision_create_template_cost_div hidden">'+
'<span class="cost_value" style="color: #777; font-size:60px"></span>'+
'<br>'+
'<span style="color: #999;">'+tr("COST")+' / ' + tr("HOUR") + '</span>'+
'</div>'+
'</div>'+
'</div>'+
'</div>'+
@ -2054,6 +2108,25 @@ function generate_provision_instance_type_accordion(context, capacity) {
'</div>'+
'<br>');
var cost = 0;
if (capacity.CPU_COST || capacity.MEMORY_COST && Config.isFeatureEnabled("showback")) {
$(".provision_create_template_cost_div").show();
if (capacity.CPU && capacity.CPU_COST) {
cost += capacity.CPU * capacity.CPU_COST
$(".cost_value").data("CPU_COST", capacity.CPU_COST);
}
if (capacity.MEMORY && capacity.MEMORY_COST) {
cost += capacity.MEMORY * capacity.MEMORY_COST
$(".cost_value").data("MEMORY_COST", capacity.MEMORY_COST);
}
$(".cost_value").html(cost);
} else {
$(".provision_create_template_cost_div").hide();
}
provision_instance_type_accordion_id += 1;
var provision_instance_types_datatable = $('.provision_instance_types_table', context).dataTable({
@ -2146,6 +2219,20 @@ function generate_provision_instance_type_accordion(context, capacity) {
$(".memory_value", context).html(memory_value);
$(".memory_unit", context).html(memory_unit);
if (Config.isFeatureEnabled("showback")) {
var cost = 0;
if ($(".cost_value").data("CPU_COST")) {
cost += $(this).attr("cpu") * $(".cost_value").data("CPU_COST")
}
if ($(".cost_value").data("MEMORY_COST")) {
cost += $(this).attr("memory") * $(".cost_value").data("MEMORY_COST")
}
$(".cost_value").html(cost);
}
$('.accordion a', context).first().trigger("click");
})
@ -2690,6 +2777,13 @@ function show_provision_user_info_callback(request, response) {
$("#provision_user_info_acct_div"),
{ fixed_user: info.ID,
fixed_group_by: "vm" });
if (Config.isFeatureEnabled("showback")) {
showbackGraphs(
$("#provision_user_info_showback_div"),
{ fixed_user: info.ID});
}
}
@ -2715,6 +2809,12 @@ function show_provision_group_info_callback(request, response) {
{ fixed_group: info.ID,
init_group_by: "user" });
if (Config.isFeatureEnabled("showback")) {
showbackGraphs(
$("#provision_info_vdc_group_showback", context),
{ fixed_group: info.ID });
}
$("#acct_placeholder", context).hide();
}
@ -5010,6 +5110,9 @@ function setup_provision_user_info(context) {
'<span class="provision_vdc_user_info_show_acct button medium radius" data-tooltip title="'+tr("User Accounting")+'" style="margin-right: 10px">'+
'<i class="fa fa-bar-chart-o fa-lg"></i>'+
'</span>'+
(Config.isFeatureEnabled("showback") ? '<span class="provision_vdc_user_info_show_showback button medium radius" data-tooltip title="'+tr("User Showback")+'" style="margin-right: 10px">'+
'<i class="fa fa-money fa-lg"></i>'+
'</span>' : '') +
'</li>'+
'<li class="provision-bullet-item text-left">'+
'</li>')
@ -5146,6 +5249,21 @@ function setup_provision_user_info(context) {
'</h2>')
})
if (Config.isFeatureEnabled("showback")) {
context.on("click", ".provision_vdc_user_info_show_showback", function(){
$(".provision_vdc_info_container", context).html("");
showbackGraphs(
$(".provision_vdc_info_container", context),
{ fixed_user: $(".provision_info_vdc_user", context).attr("opennebula_id")});
$(".provision_vdc_info_container", context).prepend(
'<h2 class="subheader">'+
$(".provision_info_vdc_user", context).attr("uname") + ' ' + tr("Showback")+
'</h2>')
})
};
context.on("click", ".provision_vdc_user_delete_confirm_button", function(){
$(".provision_vdc_user_confirm_action", context).html(
'<div data-alert class="alert-box secondary radius">'+
@ -6294,26 +6412,17 @@ $(document).ready(function(){
var template_id = role.vm_template;
var role_html_id = "#provision_create_flow_role_"+index;
generate_cardinality_selector(
$(".provision_cardinality_selector", context),
role);
OpenNebula.Template.show({
data : {
id: template_id
},
success: function(request,template_json){
var role_context = $(role_html_id)
//var template_nic = template_json.VMTEMPLATE.TEMPLATE.NIC
//var nics = []
//if ($.isArray(template_nic))
// nics = template_nic
//else if (!$.isEmptyObject(template_nic))
// nics = [template_nic]
//
//generate_provision_instance_type_accordion(
// $(".provision_capacity_selector", role_context),
// template_json.VMTEMPLATE.TEMPLATE);
generate_cardinality_selector(
$(".provision_cardinality_selector", context),
role,
template_json);
if (template_json.VMTEMPLATE.TEMPLATE.USER_INPUTS) {
generate_custom_attrs(

View File

@ -607,9 +607,24 @@ function updateUserInfo(request,user){
content: '<div id="user_accounting"></div>'
};
Sunstone.updateInfoPanelTab("user_info_panel","user_info_tab",info_tab);
Sunstone.updateInfoPanelTab("user_info_panel","user_quotas_tab",quotas_tab);
Sunstone.updateInfoPanelTab("user_info_panel","user_accouning_tab",accounting_tab);
Sunstone.updateInfoPanelTab("user_info_panel","user_accounting_tab",accounting_tab);
if (Config.isFeatureEnabled("showback")) {
var showback_tab = {
title: tr("Showback"),
icon: "fa-money",
content: '<div id="user_showback"></div>'
};
Sunstone.updateInfoPanelTab("user_info_panel","user_showback_tab",showback_tab);
showbackGraphs(
$("#user_showback","#user_info_panel"),
{ fixed_user: info.ID });
}
//Sunstone.updateInfoPanelTab("user_info_panel","user_acct_tab",acct_tab);
Sunstone.popUpInfoPanel("user_info_panel", 'users-tab');

View File

@ -2304,13 +2304,15 @@ function printCapacity(vm_info){
html += '\
<div class="row">\
<div class="large-6 columns">\
<div class="large-9 columns">\
<table class="info_table dataTable extended_table">\
<thead>\
<tr>\
<th>'+tr("CPU")+'</th>\
<th>'+tr("VCPU")+'</th>\
<th>'+tr("MEMORY")+'</th>\
<th>'+tr("Cost / CPU")+'</th>\
<th>'+tr("Cost / MByte")+'</th>\
<th></th>\
</tr>\
</thead>\
@ -2319,6 +2321,8 @@ function printCapacity(vm_info){
<td id="cpu_info">' + vm_info.TEMPLATE.CPU + '</td>\
<td id="vcpu_info">' + (vm_info.TEMPLATE.VCPU ? vm_info.TEMPLATE.VCPU : '-') + '</td>\
<td id="memory_info" one_value="'+vm_info.TEMPLATE.MEMORY+'">' + humanize_size_from_mb(vm_info.TEMPLATE.MEMORY) + '</td>\
<td id="cpu_cost_info">' + (vm_info.TEMPLATE.CPU_COST ? vm_info.TEMPLATE.CPU_COST : '-') + '</td>\
<td id="memory_cost_info" >' + (vm_info.TEMPLATE.MEMORY_COST ? vm_info.TEMPLATE.MEMORY_COST : '-') + '</td>\
<td>';
if (Config.isTabActionEnabled("vms-tab", "VM.resize")) {

View File

@ -47,6 +47,19 @@ var top_interval_ids = {};
var QUOTA_LIMIT_DEFAULT = "-1";
var QUOTA_LIMIT_UNLIMITED = "-2";
function tr(str){
var tmp = locale[str];
if ( tmp == null || tmp == "" ) {
//console.debug("Missing translation: "+str);
tmp = str;
}
return tmp;
};
var $months = new Array(
tr("January"),tr("February"),tr("March"),tr("April"),tr("May"),
tr("June"),tr("July"),tr("August"),tr("September"),tr("October"),
tr("November"),tr("December"));
//Sunstone configuration is formed by predifined "actions", main tabs
//and "info_panels". Each tab has "content" and "buttons". Each
//"info_panel" has "tabs" with "content".
@ -1202,11 +1215,7 @@ function pretty_time_runtime(time){
function _format_date(unix_timestamp) {
var difference_in_seconds = (Math.round((new Date()).getTime() / 1000)) - unix_timestamp,
current_date = new Date(unix_timestamp * 1000), minutes, hours,
months = new Array(
'January','February','March','April','May',
'June','July','August','September','October',
'November','December');
current_date = new Date(unix_timestamp * 1000), minutes, hours;
if(difference_in_seconds < 60) {
return difference_in_seconds + "s" + " ago";
@ -1218,9 +1227,9 @@ function _format_date(unix_timestamp) {
return hours + "h" + " ago";
} else if (difference_in_seconds > 60*60*24){
if(current_date.getYear() !== new Date().getYear())
return current_date.getDay() + " " + months[current_date.getMonth()].substr(0,3) + " " + _fourdigits(current_date.getYear());
return current_date.getDay() + " " + $months[current_date.getMonth()].substr(0,3) + " " + _fourdigits(current_date.getYear());
else {
return current_date.getDay() + " " + months[current_date.getMonth()].substr(0,3);
return current_date.getDay() + " " + $months[current_date.getMonth()].substr(0,3);
}
}
@ -4745,6 +4754,304 @@ function time_UTC(time){
return d.getUTCFullYear() + '/' + (d.getUTCMonth()+1) + '/' + d.getUTCDate();
}
// div is a jQuery selector
// The following options can be set:
// fixed_user fix an owner user ID
// fixed_group fix an owner group ID
// init_group_by "user", "group", "vm". init the group-by selector
// fixed_group_by "user", "group", "vm". set a fixed group-by selector
function showbackGraphs(div, opt){
if(div.html() != ""){
return false;
}
div.html(
'<div class="row">\
<div id="showback_owner_container" class="left columns">\
<label for="showback_owner">' + tr("Filter") + '</label>\
<div class="row">\
<div class="large-5 columns">\
<select id="showback_owner" name="showback_owner">\
<option value="showback_owner_all">' + tr("All") + '</option>\
<option value="showback_owner_group">' + tr("Group") + '</option>\
<option value="showback_owner_user">' + tr("User") + '</option>\
</select>\
</div>\
<div class="large-7 columns">\
<div id="showback_owner_select"/>\
</div>\
</div>\
</div>\
<div id="showback_button_container" class="left columns">\
<button class="button radius success right" id="showback_submit" type="button">'+tr("Get Showback")+'</button>\
</div>\
</div>\
<div id="showback_placeholder">\
<div class="row">\
<div class="large-8 large-centered columns">\
<div class="text-center">\
<span class="fa-stack fa-5x" style="color: #dfdfdf">\
<i class="fa fa-cloud fa-stack-2x"></i>\
<i class="fa fa-money fa-stack-1x fa-inverse"></i>\
</span>\
<div id="showback_no_data" class="hidden">\
<br>\
<p style="font-size: 18px; color: #999">'+
tr("There are no showback records")+
'</p>\
</div>\
</div>\
</div>\
</div>\
</div>\
<div id="showback_content" class="hidden">\
<div class="row showback_table">\
<div class="large-12 columns graph_legend">\
<h3 class="subheader">'+tr("Showback")+'</h3>\
</div>\
<div class="large-6 columns" style="overflow:auto">\
<table id="showback_datatable" class="datatable twelve">\
<thead>\
<tr>\
<th>dateint</th>\
<th>'+tr("Year")+'</th>\
<th>'+tr("Month")+'</th>\
<th>'+tr("Date")+'</th>\
<th>'+tr("Cost")+'</th>\
</tr>\
</thead>\
<tbody id="tbody_showback_datatable">\
</tbody>\
</table>\
<span class="label secondary radius showback_select_a_row">'+tr("Select a row to get detailed information of the month")+'</span>\
</div>\
<div class="large-6 columns">\
<div class="large-12 columns centered graph" id="showback_graph" style="height: 200px;">\
</div>\
</div>\
</div>\
<div class="row showback_vms_table hidden">\
<div class="large-12 columns graph_legend">\
<h3 class="subheader" id="showback_vms_title">'+tr("VMs")+'</h3>\
</div>\
<div class="large-12 columns" style="overflow:auto">\
<table id="showback_vms_datatable" class="datatable twelve">\
<thead>\
<tr>\
<th>'+tr("ID")+'</th>\
<th>'+tr("Name")+'</th>\
<th>'+tr("Owner")+'</th>\
<th>'+tr("Hours")+'</th>\
<th>'+tr("Cost")+'</th>\
</tr>\
</thead>\
<tbody id="tbody_showback_datatable">\
</tbody>\
</table>\
</div>\
</div>\
</div>');
if (opt == undefined){
opt = {};
}
//--------------------------------------------------------------------------
// VM owner: all, group, user
//--------------------------------------------------------------------------
if (opt.fixed_user != undefined || opt.fixed_group != undefined){
$("#showback_owner_container", div).hide();
} else {
$("select#showback_owner", div).change(function(){
var value = $(this).val();
switch (value){
case "showback_owner_all":
$("#showback_owner_select", div).hide();
break;
case "showback_owner_group":
$("#showback_owner_select", div).show();
insertSelectOptions("#showback_owner_select", div, "Group");
break;
case "showback_owner_user":
$("#showback_owner_select", div).show();
insertSelectOptions("#showback_owner_select", div, "User", -1, false,
'<option value="-1">'+tr("<< me >>")+'</option>');
break;
}
});
}
showback_dataTable = $("#showback_datatable",div).dataTable({
"bSortClasses" : false,
"bDeferRender": true,
"iDisplayLength": 6,
"sDom": "t<'row collapse'<'small-12 columns'p>>",
"aoColumnDefs": [
{ "bVisible": false, "aTargets": [0,1,2]}
]
});
showback_dataTable.fnSort( [ [0, "desc"] ] );
showback_vms_dataTable = $("#showback_vms_datatable",div).dataTable({
"bSortClasses" : false,
"bDeferRender": true
});
showback_dataTable.on("click", "tbody tr", function(){
var cells = showback_dataTable.fnGetData(this);
var year = cells[1];
var month = cells[2];
showback_vms_dataTable.fnClearTable();
showback_vms_dataTable.fnAddData(showback_dataTable.data("vms_per_date")[year][month].VMS)
$("#showback_vms_title", div).text($months[month-1] + " " + year + " " + tr("VMs"))
$(".showback_vms_table", div).show();
$(".showback_select_a_row", div).hide();
})
//--------------------------------------------------------------------------
// Submit request
//--------------------------------------------------------------------------
$("#showback_submit", div).on("click", function(){
var options = {};
if (opt.fixed_user != undefined){
options.userfilter = opt.fixed_user;
} else if (opt.fixed_group != undefined){
options.group = opt.fixed_group;
} else {
var select_val = $("#showback_owner_select .resource_list_select", div).val();
switch ($("select#showback_owner", div).val()){
case "showback_owner_all":
break;
case "showback_owner_group":
if(select_val != ""){
options.group = select_val;
}
break;
case "showback_owner_user":
if(select_val != ""){
options.userfilter = select_val;
}
break;
}
}
OpenNebula.VM.showback({
// timeout: true,
success: function(req, response){
fillShowback(div, req, response);
},
error: onError,
data: options
});
return false;
});
}
function fillShowback(div, req, response) {
$("#showback_no_data", div).hide();
if(response.SHOWBACK_RECORDS == undefined){
$("#showback_placeholder", div).show();
$("#showback_content", div).hide();
$("#showback_no_data", div).show();
return false;
}
var vms_per_date = {};
$.each(response.SHOWBACK_RECORDS.SHOWBACK, function(index, showback){
if (vms_per_date[showback.YEAR] == undefined) {
vms_per_date[showback.YEAR] = {}
}
if (vms_per_date[showback.YEAR][showback.MONTH] == undefined) {
vms_per_date[showback.YEAR][showback.MONTH] = {
"VMS": [],
"TOTAL": 0
}
}
vms_per_date[showback.YEAR][showback.MONTH].VMS.push([showback.VMID, showback.VMNAME, showback.UNAME, showback.HOURS, showback.COST]);
vms_per_date[showback.YEAR][showback.MONTH].TOTAL += parseFloat(showback.COST);
});
var series = []
var showback_data = [];
$.each(vms_per_date, function(year, months){
$.each(months, function(month, value){
series.push([(new Date(year, month-1)).getTime(), year, month, $months[month-1] + " " + year, value.TOTAL.toFixed(2)])
showback_data.push([(new Date(year, month-1)), value.TOTAL.toFixed(2) ])
})
})
showback_dataTable.fnClearTable();
if (series.length > 0) {
showback_dataTable.data("vms_per_date", vms_per_date)
showback_dataTable.fnAddData(series);
}
var showback_plot_series = [];
showback_plot_series.push(
{
label: tr("Showback"),
data: showback_data
});
var options = {
// colors: [ "#cdebf5", "#2ba6cb", "#6f6f6f" ]
colors: [ "#2ba6cb", "#707D85", "#AC5A62" ],
legend: {
show: false
},
xaxis : {
mode: "time",
color: "#efefef",
size: 8,
minTickSize: [1, "month"]
},
yaxis : {
show: false
},
series: {
bars: {
show: true,
lineWidth: 0,
barWidth: 24 * 60 * 60 * 1000 * 20,
fill: true,
align: "left"
}
},
grid: {
borderWidth: 1,
borderColor: "#efefef",
hoverable: true
}
//tooltip: true,
//tooltipOpts: {
// content: "%x"
//}
};
var showback_plot = $.plot($("#showback_graph", div), showback_plot_series, options);
$("#showback_placeholder", div).hide();
$("#showback_content", div).show();
}
// div is a jQuery selector
// The following options can be set:
// fixed_user fix an owner user ID

View File

@ -446,6 +446,13 @@ get '/vm/accounting' do
@SunstoneServer.get_vm_accounting(params)
end
##############################################################################
# Showback
##############################################################################
get '/vm/showback' do
@SunstoneServer.get_vm_showback(params)
end
##############################################################################
# Marketplace

View File

@ -15,7 +15,7 @@
/* -------------------------------------------------------------------------- */
#include "Quota.h"
#include <math.h>
#include "NebulaUtil.h"
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
@ -79,7 +79,7 @@ void Quota::add_to_quota(VectorAttribute * attr, const string& va_name, float nu
total += num;
attr->replace(va_name, float_to_str(total));
attr->replace(va_name, one_util::float_to_str(total));
}
/* -------------------------------------------------------------------------- */
@ -395,7 +395,7 @@ int Quota::update_limits(
return -1;
}
quota->replace(metrics[i], float_to_str(limit_f));
quota->replace(metrics[i], one_util::float_to_str(limit_f));
}
return 0;
@ -431,7 +431,7 @@ VectorAttribute * Quota::new_quota(VectorAttribute * va)
return 0;
}
limits.insert(make_pair(metrics[i], float_to_str(limit_f)));
limits.insert(make_pair(metrics[i], one_util::float_to_str(limit_f)));
limits.insert(make_pair(metrics_used, "0"));
}
@ -444,24 +444,3 @@ VectorAttribute * Quota::new_quota(VectorAttribute * va)
return new VectorAttribute(template_name,limits);
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
string Quota::float_to_str(const float &num)
{
ostringstream oss;
if ( num == ceil(num) )
{
oss.precision(0);
}
else
{
oss.precision(2);
}
oss << fixed << num;
return oss.str();
}

View File

@ -115,6 +115,16 @@ const char * VirtualMachine::monit_db_bootstrap = "CREATE TABLE IF NOT EXISTS "
"vm_monitoring (vmid INTEGER, last_poll INTEGER, body MEDIUMTEXT, "
"PRIMARY KEY(vmid, last_poll))";
const char * VirtualMachine::showback_table = "vm_showback";
const char * VirtualMachine::showback_db_names = "vmid, year, month, body";
const char * VirtualMachine::showback_db_bootstrap =
"CREATE TABLE IF NOT EXISTS vm_showback "
"(vmid INTEGER, year INTEGER, month INTEGER, body MEDIUMTEXT, "
"PRIMARY KEY(vmid, year, month))";
const char * VirtualMachine::NO_NIC_DEFAULTS[] = {"NETWORK_ID", "NETWORK",
"NETWORK_UID", "NETWORK_UNAME"};
@ -326,6 +336,32 @@ int VirtualMachine::insert(SqlDB * db, string& error_str)
obj_template->add("VCPU", ivalue);
}
// ------------------------------------------------------------------------
// Check the cost attributes
// ------------------------------------------------------------------------
if ( user_obj_template->get("CPU_COST", fvalue) == true )
{
if ( fvalue < 0 )
{
goto error_cpu_cost;
}
user_obj_template->erase("CPU_COST");
obj_template->add("CPU_COST", fvalue);
}
if ( user_obj_template->get("MEMORY_COST", fvalue) == true )
{
if ( fvalue < 0 )
{
goto error_memory_cost;
}
user_obj_template->erase("MEMORY_COST");
obj_template->add("MEMORY_COST", fvalue);
}
// ------------------------------------------------------------------------
// Check the OS attribute
// ------------------------------------------------------------------------
@ -448,6 +484,14 @@ error_memory:
error_str = "MEMORY attribute must be a positive integer value.";
goto error_common;
error_cpu_cost:
error_str = "CPU_COST attribute must be a positive float or integer value.";
goto error_common;
error_memory_cost:
error_str = "MEMORY_COST attribute must be a positive float or integer value.";
goto error_common;
error_os:
error_defaults:
error_name:

View File

@ -354,6 +354,49 @@ int VirtualMachinePool::dump_acct(ostringstream& oss,
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
int VirtualMachinePool::dump_showback(ostringstream& oss,
const string& where,
int start_month,
int start_year,
int end_month,
int end_year)
{
ostringstream cmd;
cmd << "SELECT " << VirtualMachine::showback_table << ".body FROM "
<< VirtualMachine::showback_table
<< " INNER JOIN " << VirtualMachine::table
<< " WHERE vmid=oid";
if ( !where.empty() )
{
cmd << " AND " << where;
}
if ( (start_month != -1 && start_year != -1) ||
(end_month != -1 && end_year != -1) )
{
if (start_month != -1 && start_year != -1)
{
cmd << " AND (year > " << start_year <<
" OR (year = " << start_year << " AND month >= " << start_month << ") )";
}
if (end_month != -1 && end_year != -1)
{
cmd << " AND (year < " << end_year <<
" OR (year = " << end_year << " AND month <= " << end_month << ") )";
}
}
cmd << " ORDER BY year,month,vmid";
return PoolSQL::dump(oss, "SHOWBACK_RECORDS", cmd);
};
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
int VirtualMachinePool::clean_expired_monitoring()
{
if ( _monitor_expiration == 0 )
@ -413,3 +456,415 @@ int VirtualMachinePool::dump_monitoring(
return PoolSQL::dump(oss, "MONITORING_DATA", cmd);
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
int VirtualMachinePool::min_stime_cb(void * _min_stime, int num, char **values, char **names)
{
if ( num == 0 || values == 0 || values[0] == 0 )
{
return -1;
}
*static_cast<int*>(_min_stime) = atoi(values[0]);
return 0;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
#ifdef SBDEBUG
static string put_time(tm tmp_tm)
{
ostringstream oss;
oss << tmp_tm.tm_mday << "/" << tmp_tm.tm_mon+1 << "/" << tmp_tm.tm_year+1900
<< " " << tmp_tm.tm_hour << ":" << tmp_tm.tm_min << ":" << tmp_tm.tm_sec;
return oss.str();
}
static string put_time(time_t t)
{
tm tmp_tm = *localtime(&t);
return put_time(tmp_tm);
}
#endif
/* -------------------------------------------------------------------------- */
int VirtualMachinePool::calculate_showback(
int start_month,
int start_year,
int end_month,
int end_year,
string &error_str)
{
vector<xmlNodePtr> nodes;
vector<xmlNodePtr>::iterator node_it;
vector<time_t> showback_slots;
vector<time_t>::iterator slot_it;
// map<vid, map<month, pair<total_cost, n_hours> > >
map<int, map<time_t, pair<float,float> > > vm_cost;
map<int, map<time_t, pair<float, float> > >::iterator vm_it;
map<time_t, pair<float,float> >::iterator vm_month_it;
VirtualMachine* vm;
int rc;
ostringstream oss;
ostringstream body;
char * sql_body;
tm tmp_tm;
int vid;
int h_stime;
int h_etime;
float cpu_cost;
float mem_cost;
float cpu;
int mem;
#ifdef SBDEBUG
ostringstream debug;
time_t debug_t_0 = time(0);
#endif
//--------------------------------------------------------------------------
// Set start and end times for the window to process
//--------------------------------------------------------------------------
tzset();
time_t start_time = time(0);
time_t end_time = time(0);
if (start_month != -1 && start_year != -1)
{
// First day of the given month
tmp_tm.tm_sec = 0;
tmp_tm.tm_min = 0;
tmp_tm.tm_hour = 0;
tmp_tm.tm_mday = 1;
tmp_tm.tm_mon = start_month - 1;
tmp_tm.tm_year = start_year - 1900;
tmp_tm.tm_isdst = -1;
start_time = mktime(&tmp_tm);
}
else
{
// Set start time to the lowest stime from the history records
set_callback(static_cast<Callbackable::Callback>(&VirtualMachinePool::min_stime_cb),
static_cast<void *>(&start_time));
oss << "SELECT MIN(stime) FROM " << History::table;
rc = db->exec(oss, this);
unset_callback();
}
if (end_month != -1 && end_year != -1)
{
// First day of the next month
tmp_tm.tm_sec = 0;
tmp_tm.tm_min = 0;
tmp_tm.tm_hour = 0;
tmp_tm.tm_mday = 1;
tmp_tm.tm_mon = end_month;
tmp_tm.tm_year = end_year - 1900;
tmp_tm.tm_isdst = -1;
time_t end_time_tmp = mktime(&tmp_tm);
if (end_time_tmp < end_time)
{
end_time = end_time_tmp;
}
}
//--------------------------------------------------------------------------
// Get accounting history records
//--------------------------------------------------------------------------
oss.str("");
rc = dump_acct(oss, "", start_time, end_time);
ObjectXML xml(oss.str());
#ifdef SBDEBUG
time_t debug_t_1 = time(0);
#endif
//--------------------------------------------------------------------------
// Create the monthly time slots
//--------------------------------------------------------------------------
// Reset stime to 1st of month, 00:00
localtime_r(&start_time, &tmp_tm);
tmp_tm.tm_sec = 0;
tmp_tm.tm_min = 0;
tmp_tm.tm_hour = 0;
tmp_tm.tm_mday = 1;
tmp_tm.tm_isdst = -1;
time_t tmp_t = mktime(&tmp_tm);
while(tmp_t < end_time)
{
showback_slots.push_back(tmp_t);
tmp_tm.tm_sec = 0;
tmp_tm.tm_min = 0;
tmp_tm.tm_hour = 0;
tmp_tm.tm_mday = 1;
tmp_tm.tm_mon++;
tmp_tm.tm_isdst = -1;
tmp_t = mktime(&tmp_tm);
}
// Extra slot that won't be used. Is needed only to calculate the time
// for the second-to-last slot
showback_slots.push_back(end_time);
#ifdef SBDDEBUG
for ( slot_it = showback_slots.begin(); slot_it != showback_slots.end(); slot_it++ )
{
debug.str("");
debug << "Slot: " << put_time(*slot_it);
NebulaLog::log("SHOWBACK", Log::DEBUG, debug);
}
#endif
//--------------------------------------------------------------------------
// Process the history records
//--------------------------------------------------------------------------
rc = xml.get_nodes("/HISTORY_RECORDS/HISTORY", nodes);
for ( node_it = nodes.begin(); node_it != nodes.end(); node_it++ )
{
ObjectXML history(*node_it);
history.xpath(vid, "/HISTORY/OID", -1);
history.xpath(h_stime, "/HISTORY/STIME", 0);
history.xpath(h_etime, "/HISTORY/ETIME", 0);
history.xpath(cpu, "/HISTORY/VM/TEMPLATE/CPU", 0);
history.xpath(mem, "/HISTORY/VM/TEMPLATE/MEMORY", 0);
history.xpath(cpu_cost, "/HISTORY/VM/TEMPLATE/CPU_COST", 0);
history.xpath(mem_cost, "/HISTORY/VM/TEMPLATE/MEMORY_COST", 0);
#ifdef SBDDEBUG
int seq;
history.xpath(seq, "/HISTORY/SEQ", -1);
debug.str("");
debug << "VM " << vid << " SEQ " << seq << endl
<< "h_stime " << h_stime << endl
<< "h_etime " << h_etime << endl
<< "cpu_cost " << cpu_cost << endl
<< "mem_cost " << mem_cost << endl
<< "cpu " << cpu << endl
<< "mem " << mem;
NebulaLog::log("SHOWBACK", Log::DEBUG, debug);
#endif
for ( slot_it = showback_slots.begin(); slot_it != showback_slots.end()-1; slot_it++ )
{
time_t t = *slot_it;
time_t t_next = *(slot_it+1);
if( (h_etime > t || h_etime == 0) &&
(h_stime != 0 && h_stime <= t_next) ) {
time_t stime = t;
if(h_stime != 0){
stime = (t < h_stime) ? h_stime : t; //max(t, h_stime);
}
time_t etime = t_next;
if(h_etime != 0){
etime = (t_next < h_etime) ? t_next : h_etime; //min(t_next, h_etime);
}
float n_hours = difftime(etime, stime) / 60 / 60;
float cost = 0;
cost += cpu_cost * cpu * n_hours;
cost += mem_cost * mem * n_hours;
// Add to vm time slot.
map<time_t, pair<float,float> >& totals = vm_cost[vid];
if(totals.count(t) == 0)
{
totals[t] = make_pair(0,0);
}
totals[t].first += cost;
totals[t].second += n_hours;
}
}
}
xml.free_nodes(nodes);
#ifdef SBDEBUG
time_t debug_t_2 = time(0);
#endif
// Write to DB
oss.str("");
oss << "REPLACE INTO " << VirtualMachine::showback_table
<< " ("<< VirtualMachine::showback_db_names <<") VALUES ";
int n_entries = 0;
for ( vm_it = vm_cost.begin(); vm_it != vm_cost.end(); vm_it++ )
{
map<time_t, pair<float,float> >& totals = vm_it->second;
for ( vm_month_it = totals.begin(); vm_month_it != totals.end(); vm_month_it++ )
{
int vmid = vm_it->first;
vm = get(vmid, true);
int uid = 0;
int gid = 0;
string uname = "";
string gname = "";
string vmname = "";
if (vm != 0)
{
uid = vm->get_uid();
gid = vm->get_gid();
uname = vm->get_uname();
gname = vm->get_gname();
vmname = vm->get_name();
vm->unlock();
}
localtime_r(&vm_month_it->first, &tmp_tm);
body.str("");
string cost = one_util::float_to_str(vm_month_it->second.first);
string hours = one_util::float_to_str(vm_month_it->second.second);
body << "<SHOWBACK>"
<< "<VMID>" << vmid << "</VMID>"
<< "<VMNAME>" << vmname << "</VMNAME>"
<< "<UID>" << uid << "</UID>"
<< "<GID>" << gid << "</GID>"
<< "<UNAME>" << uname << "</UNAME>"
<< "<GNAME>" << gname << "</GNAME>"
<< "<YEAR>" << tmp_tm.tm_year + 1900 << "</YEAR>"
<< "<MONTH>" << tmp_tm.tm_mon + 1 << "</MONTH>"
<< "<COST>" << cost << "</COST>"
<< "<HOURS>" << hours << "</HOURS>"
<< "</SHOWBACK>";
sql_body = db->escape_str(body.str().c_str());
if ( sql_body == 0 )
{
error_str = "Error creating XML body.";
return -1;
}
if (n_entries == 0)
{
oss.str("");
oss << "REPLACE INTO " << VirtualMachine::showback_table
<< " ("<< VirtualMachine::showback_db_names <<") VALUES ";
}
else
{
oss << ",";
}
oss << " (" << vm_it->first << ","
<< tmp_tm.tm_year + 1900 << ","
<< tmp_tm.tm_mon + 1 << ","
<< "'" << sql_body << "')";
db->free_str(sql_body);
n_entries++;
// To avoid the oss to grow indefinitely, flush contents
if (n_entries == 1000)
{
rc = db->exec(oss);
if (rc != 0)
{
error_str = "Error writing to DB.";
return -1;
}
n_entries = 0;
}
#ifdef SBDDEBUG
debug.str("");
debug << "VM " << vm_it->first
<< " cost for Y " << tmp_tm.tm_year + 1900
<< " M " << tmp_tm.tm_mon + 1
<< " COST " << cost << ""
<< " HOURS " << hours;
NebulaLog::log("SHOWBACK", Log::DEBUG, debug);
#endif
}
}
if (n_entries > 0)
{
rc = db->exec(oss);
if (rc != 0)
{
error_str = "Error writing to DB.";
return -1;
}
}
#ifdef SBDEBUG
time_t debug_t_3 = time(0);
debug.str("");
debug << "Time to dump acct to mem: " << debug_t_1 - debug_t_0;
NebulaLog::log("SHOWBACK", Log::DEBUG, debug);
debug.str("");
debug << "Time to process numbers: " << debug_t_2 - debug_t_1;
NebulaLog::log("SHOWBACK", Log::DEBUG, debug);
debug.str("");
debug << "Time to write to db: " << debug_t_3 - debug_t_2;
NebulaLog::log("SHOWBACK", Log::DEBUG, debug);
#endif
return 0;
}

View File

@ -188,6 +188,39 @@ int ObjectXML::xpath(int& value, const char * xpath_expr, const int& def)
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
int ObjectXML::xpath(float& value, const char * xpath_expr, const float& def)
{
vector<string> values;
int rc = 0;
values = (*this)[xpath_expr];
if (values.empty() == true)
{
value = def;
rc = -1;
}
else
{
istringstream iss;
iss.str(values[0]);
iss >> dec >> value;
if (iss.fail() == true)
{
value = def;
rc = -1;
}
}
return rc;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
int ObjectXML::xpath(unsigned int& value, const char * xpath_expr,
const unsigned int& def)
{