mirror of
https://github.com/OpenNebula/one.git
synced 2026-11-08 21:16:39 +09:00
1154 lines
32 KiB
C++
1154 lines
32 KiB
C++
/* -------------------------------------------------------------------------- */
|
|
/* Copyright 2002-2022, OpenNebula Project, OpenNebula Systems */
|
|
/* */
|
|
/* 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. */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
#include "VirtualMachinePool.h"
|
|
|
|
#include "NebulaLog.h"
|
|
#include "Nebula.h"
|
|
#include "HookStateVM.h"
|
|
#include "HookManager.h"
|
|
#include "ImageManager.h"
|
|
|
|
#include <sstream>
|
|
|
|
using namespace std;
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
VirtualMachinePool::VirtualMachinePool(
|
|
SqlDB * db,
|
|
vector<const SingleAttribute *>& restricted_attrs,
|
|
vector<const SingleAttribute *>& encrypted_attrs,
|
|
bool on_hold,
|
|
float default_cpu_cost,
|
|
float default_mem_cost,
|
|
float default_disk_cost,
|
|
bool showback_only_running)
|
|
: PoolSQL(db, one_db::vm_table),
|
|
_submit_on_hold(on_hold),
|
|
_default_cpu_cost(default_cpu_cost), _default_mem_cost(default_mem_cost),
|
|
_default_disk_cost(default_disk_cost),
|
|
_showback_only_running(showback_only_running)
|
|
{
|
|
// Set restricted attributes
|
|
VirtualMachineTemplate::parse_restricted(restricted_attrs);
|
|
|
|
// Set encrypted attributes
|
|
VirtualMachineTemplate::parse_encrypted(encrypted_attrs);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
int VirtualMachinePool::update(PoolObjectSQL * objsql)
|
|
{
|
|
VirtualMachine * vm = dynamic_cast<VirtualMachine *>(objsql);
|
|
|
|
if ( vm == 0 )
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if ( HookStateVM::trigger(vm) )
|
|
{
|
|
std::string event = HookStateVM::format_message(vm);
|
|
|
|
Nebula::instance().get_hm()->trigger_send_event(event);
|
|
}
|
|
|
|
vm->set_prev_state();
|
|
|
|
return vm->update(db);
|
|
};
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
int VirtualMachinePool::insert_index(const string& deploy_id, int vmid,
|
|
bool replace)
|
|
{
|
|
ostringstream oss;
|
|
char * deploy_name = db->escape_str(deploy_id);
|
|
|
|
if (deploy_name == 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (replace)
|
|
{
|
|
oss << "REPLACE ";
|
|
}
|
|
else
|
|
{
|
|
oss << "INSERT ";
|
|
}
|
|
|
|
oss << "INTO " << one_db::vm_import_table
|
|
<< " (" << one_db::vm_import_db_names << ") "
|
|
<< " VALUES ('" << deploy_name << "'," << vmid << ")";
|
|
|
|
db->free_str(deploy_name);
|
|
|
|
return db->exec_wr(oss);
|
|
};
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
void VirtualMachinePool::drop_index(const string& deploy_id)
|
|
{
|
|
ostringstream oss;
|
|
char * deploy_name = db->escape_str(deploy_id);
|
|
|
|
if (deploy_name == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
oss << "DELETE FROM " << one_db::vm_import_table << " WHERE deploy_id='"
|
|
<< deploy_name << "'";
|
|
|
|
db->exec_wr(oss);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
int VirtualMachinePool::allocate(
|
|
int uid,
|
|
int gid,
|
|
const string& uname,
|
|
const string& gname,
|
|
int umask,
|
|
unique_ptr<VirtualMachineTemplate> vm_template,
|
|
int * oid,
|
|
string& error_str,
|
|
bool on_hold)
|
|
{
|
|
string deploy_id;
|
|
|
|
// ------------------------------------------------------------------------
|
|
// Build a new Virtual Machine object
|
|
// ------------------------------------------------------------------------
|
|
auto vm = new VirtualMachine(-1, uid, gid, uname, gname, umask, move(vm_template));
|
|
|
|
if ( _submit_on_hold == true || on_hold )
|
|
{
|
|
vm->state = VirtualMachine::HOLD;
|
|
|
|
vm->user_obj_template->replace("SUBMIT_ON_HOLD", true);
|
|
}
|
|
else
|
|
{
|
|
vm->state = VirtualMachine::PENDING;
|
|
}
|
|
|
|
vm->prev_state = vm->state;
|
|
|
|
vm->user_obj_template->get("DEPLOY_ID", deploy_id);
|
|
|
|
if (!deploy_id.empty())
|
|
{
|
|
vm->state = VirtualMachine::HOLD;
|
|
|
|
if (insert_index(deploy_id, -1, false) == -1) //Set import in progress
|
|
{
|
|
delete vm;
|
|
|
|
error_str = "Virtual Machine " + deploy_id + " already imported.";
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
// Insert the Object in the pool
|
|
// ------------------------------------------------------------------------
|
|
|
|
*oid = PoolSQL::allocate(vm, error_str);
|
|
|
|
// ------------------------------------------------------------------------
|
|
// Insert the deploy_id - vmid index for imported VMs
|
|
// ------------------------------------------------------------------------
|
|
|
|
if (!deploy_id.empty())
|
|
{
|
|
if (*oid >= 0)
|
|
{
|
|
insert_index(deploy_id, *oid, true);
|
|
}
|
|
else
|
|
{
|
|
drop_index(deploy_id);
|
|
}
|
|
}
|
|
|
|
if (*oid >= 0)
|
|
{
|
|
if (auto vm = get_ro(*oid))
|
|
{
|
|
std::string event = HookStateVM::format_message(vm.get());
|
|
|
|
Nebula::instance().get_hm()->trigger_send_event(event);
|
|
}
|
|
}
|
|
|
|
return *oid;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
int VirtualMachinePool::get_running(
|
|
vector<int>& oids,
|
|
int vm_limit,
|
|
time_t last_poll)
|
|
{
|
|
ostringstream os;
|
|
string where;
|
|
|
|
os << "last_poll <= " << last_poll << " and"
|
|
<< " state = " << VirtualMachine::ACTIVE
|
|
<< " and ( lcm_state = " << VirtualMachine::RUNNING
|
|
<< " or lcm_state = " << VirtualMachine::UNKNOWN << " )"
|
|
<< " ORDER BY last_poll ASC " << db->limit_string(vm_limit);
|
|
|
|
where = os.str();
|
|
|
|
return PoolSQL::search(oids, one_db::vm_table, where);
|
|
};
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
int VirtualMachinePool::get_pending(
|
|
vector<int>& oids)
|
|
{
|
|
ostringstream os;
|
|
string where;
|
|
|
|
os << "state = " << VirtualMachine::PENDING;
|
|
|
|
where = os.str();
|
|
|
|
return PoolSQL::search(oids, one_db::vm_table, where);
|
|
};
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
int VirtualMachinePool::dump_acct(string& oss, const string& where,
|
|
int time_start, int time_end)
|
|
{
|
|
ostringstream cmd;
|
|
|
|
cmd << "SELECT " << one_db::history_table << ".body FROM "
|
|
<< one_db::history_table
|
|
<< " INNER JOIN " << one_db::vm_table << " ON vid=oid";
|
|
|
|
if ( !where.empty() )
|
|
{
|
|
cmd << " AND " << where;
|
|
}
|
|
|
|
if ( time_start != -1 || time_end != -1 )
|
|
{
|
|
if ( time_start != -1 )
|
|
{
|
|
cmd << " AND (etime > " << time_start << " OR etime = 0)";
|
|
}
|
|
|
|
if ( time_end != -1 )
|
|
{
|
|
cmd << " AND stime < " << time_end;
|
|
}
|
|
}
|
|
|
|
cmd << " ORDER BY vid,seq";
|
|
|
|
return PoolSQL::dump(oss, "HISTORY_RECORDS", cmd);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
int VirtualMachinePool::dump_acct(string& oss,
|
|
int time_start, int time_end, int sid, int rows)
|
|
{
|
|
ostringstream cmd;
|
|
|
|
cmd << "SELECT " << "body FROM " << one_db::history_table;
|
|
|
|
if ( time_start != -1 || time_end != -1 )
|
|
{
|
|
string next = " WHERE ";
|
|
|
|
if ( time_start != -1 )
|
|
{
|
|
cmd << " WHERE (etime > " << time_start << " OR etime = 0)";
|
|
next = " AND ";
|
|
}
|
|
|
|
if ( time_end != -1 )
|
|
{
|
|
cmd << next << "stime < " << time_end;
|
|
}
|
|
}
|
|
|
|
cmd << " ORDER BY vid,seq";
|
|
|
|
if (rows != -1)
|
|
{
|
|
cmd << " " << db->limit_string(sid, rows);
|
|
}
|
|
|
|
return PoolSQL::dump(oss, "HISTORY_RECORDS", cmd);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
int VirtualMachinePool::dump_showback(string& oss,
|
|
const string& where,
|
|
int start_month,
|
|
int start_year,
|
|
int end_month,
|
|
int end_year)
|
|
{
|
|
ostringstream cmd;
|
|
|
|
cmd << "SELECT " << one_db::vm_showback_table << ".body FROM "
|
|
<< one_db::vm_showback_table
|
|
<< " INNER JOIN " << one_db::vm_table << " ON 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::dump_monitoring(
|
|
string& oss,
|
|
const string& where,
|
|
const int seconds)
|
|
{
|
|
ostringstream cmd;
|
|
|
|
switch (seconds)
|
|
{
|
|
case 0: //Get last monitor value
|
|
/*
|
|
* SELECT vm_monitoring.body
|
|
* FROM vm_monitoring
|
|
* INNER JOIN (
|
|
* SELECT vmid, MAX(last_poll) as last_poll
|
|
* FROM vm_monitoring
|
|
* GROUP BY vmid
|
|
* ) lpt on lpt.vmid = vm_monitoring.vmid AND lpt.last_poll = vm_monitoring.last_poll
|
|
* INNER JOIN vm_pool ON vm_monitoring.vmid = oid
|
|
* ORDER BY oid;
|
|
*/
|
|
cmd << "SELECT " << one_db::vm_monitor_table << ".body "
|
|
<< "FROM " << one_db::vm_monitor_table << " INNER JOIN ("
|
|
<< "SELECT vmid, MAX(last_poll) as last_poll FROM "
|
|
<< one_db::vm_monitor_table << " GROUP BY vmid) as lpt "
|
|
<< "ON lpt.vmid = " << one_db::vm_monitor_table << ".vmid "
|
|
<< "AND lpt.last_poll = " << one_db::vm_monitor_table
|
|
<< ".last_poll INNER JOIN " << one_db::vm_table
|
|
<< " ON " << one_db::vm_monitor_table << ".vmid = oid";
|
|
|
|
if ( !where.empty() )
|
|
{
|
|
cmd << " WHERE " << where;
|
|
}
|
|
|
|
cmd << " ORDER BY oid";
|
|
|
|
break;
|
|
|
|
case -1: //Get all monitoring
|
|
cmd << "SELECT " << one_db::vm_monitor_table << ".body FROM "
|
|
<< one_db::vm_monitor_table << " INNER JOIN " << one_db::vm_table
|
|
<< " ON vmid = oid";
|
|
|
|
if ( !where.empty() )
|
|
{
|
|
cmd << " WHERE " << where;
|
|
}
|
|
|
|
cmd << " ORDER BY vmid, " << one_db::vm_monitor_table << ".last_poll;";
|
|
|
|
break;
|
|
|
|
default: //Get monitor in last s seconds
|
|
cmd << "SELECT " << one_db::vm_monitor_table << ".body FROM "
|
|
<< one_db::vm_monitor_table << " INNER JOIN " << one_db::vm_table
|
|
<< " ON vmid = oid WHERE " << one_db::vm_monitor_table
|
|
<< ".last_poll > " << time(nullptr) - seconds;
|
|
|
|
if ( !where.empty() )
|
|
{
|
|
cmd << " AND " << where;
|
|
}
|
|
|
|
cmd << " ORDER BY vmid, " << one_db::vm_monitor_table << ".last_poll;";
|
|
|
|
break;
|
|
}
|
|
|
|
return PoolSQL::dump(oss, "MONITORING_DATA", cmd);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
VirtualMachineMonitorInfo VirtualMachinePool::get_monitoring(int vmid)
|
|
{
|
|
ostringstream cmd;
|
|
string monitor_str;
|
|
|
|
cmd << "SELECT " << one_db::vm_monitor_table << ".body FROM "
|
|
<< one_db::vm_monitor_table
|
|
<< " WHERE vmid = " << vmid
|
|
<< " AND last_poll=(SELECT MAX(last_poll) FROM "
|
|
<< one_db::vm_monitor_table
|
|
<< " WHERE vmid = " << vmid << ")";
|
|
|
|
VirtualMachineMonitorInfo info(vmid, 0);
|
|
|
|
if (PoolSQL::dump(monitor_str, "", cmd) == 0 && !monitor_str.empty())
|
|
{
|
|
info.from_xml(monitor_str);
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
int VirtualMachinePool::get_vmid(const string& deploy_id)
|
|
{
|
|
int rc;
|
|
int vmid = -1;
|
|
ostringstream oss;
|
|
|
|
auto sql_id = db->escape_str(deploy_id);
|
|
|
|
single_cb<int> cb;
|
|
|
|
cb.set_callback(&vmid);
|
|
|
|
oss << "SELECT vmid FROM " << one_db::vm_import_table
|
|
<< " WHERE deploy_id = '" << sql_id << "'";
|
|
|
|
rc = db->exec_rd(oss, &cb);
|
|
|
|
cb.unset_callback();
|
|
|
|
db->free_str(sql_id);
|
|
|
|
if (rc != 0 )
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return vmid;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
#ifdef SBDEBUG
|
|
#include <chrono>
|
|
|
|
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
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/**
|
|
* SBrecord is an implementation structure to aggregate metric costs. It
|
|
* includes a method to write the showback record to an xml stream
|
|
*/
|
|
struct SBRecord {
|
|
ostringstream& to_xml(ostringstream &oss)
|
|
{
|
|
string cpuc_s = one_util::float_to_str(cpu_cost);
|
|
string memc_s = one_util::float_to_str(mem_cost);
|
|
string diskc_s= one_util::float_to_str(disk_cost);
|
|
string hour_s = one_util::float_to_str(hours);
|
|
string rhour_s= one_util::float_to_str(rhours);
|
|
string cost_s = one_util::float_to_str(cpu_cost + mem_cost + disk_cost);
|
|
|
|
oss << "<CPU_COST>" << cpuc_s << "</CPU_COST>"
|
|
<< "<MEMORY_COST>"<< memc_s << "</MEMORY_COST>"
|
|
<< "<DISK_COST>" << diskc_s<< "</DISK_COST>"
|
|
<< "<TOTAL_COST>" << cost_s << "</TOTAL_COST>"
|
|
<< "<HOURS>" << hour_s << "</HOURS>"
|
|
<< "<RHOURS>" << rhour_s << "</RHOURS>";
|
|
|
|
return oss;
|
|
};
|
|
|
|
float cpu_cost = 0;
|
|
float mem_cost = 0;
|
|
float disk_cost = 0;
|
|
float hours = 0;
|
|
float rhours = 0;
|
|
};
|
|
|
|
struct VMCost {
|
|
int uid = -1;
|
|
int gid = -1;
|
|
|
|
string uname;
|
|
string gname;
|
|
|
|
string vmname;
|
|
|
|
map<time_t, SBRecord> totals;
|
|
};
|
|
|
|
int VirtualMachinePool::calculate_showback(
|
|
int start_month,
|
|
int start_year,
|
|
int end_month,
|
|
int end_year,
|
|
string &error_str)
|
|
{
|
|
vector<xmlNodePtr> nodes;
|
|
vector<time_t> showback_slots;
|
|
map<int, VMCost> vm_costs;
|
|
|
|
int rc;
|
|
ostringstream oss;
|
|
ostringstream body;
|
|
char * sql_body;
|
|
string sql_cmd_start;
|
|
string sql_cmd_separator;
|
|
string sql_cmd_end;
|
|
|
|
tm tmp_tm;
|
|
int vid;
|
|
time_t h_stime, h_rstime;
|
|
time_t h_etime, h_retime;
|
|
float cpu_cost;
|
|
float mem_cost;
|
|
float disk_cost;
|
|
float cpu;
|
|
float disk;
|
|
int mem;
|
|
|
|
#ifdef SBDEBUG
|
|
ostringstream debug;
|
|
auto debug_t_0 = std::chrono::high_resolution_clock::now();
|
|
#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
|
|
single_cb<time_t> cb;
|
|
|
|
cb.set_callback(&start_time);
|
|
|
|
oss << "SELECT MIN(stime) FROM " << one_db::history_table;
|
|
|
|
rc = db->exec_rd(oss, &cb);
|
|
|
|
cb.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;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// 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 ( auto 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
|
|
|
|
#ifdef SBDEBUG
|
|
auto debug_t_1 = std::chrono::high_resolution_clock::now();
|
|
#endif
|
|
|
|
int start_index = 0;
|
|
const int chunk_size = 10000;
|
|
|
|
do
|
|
{
|
|
//--------------------------------------------------------------------------
|
|
// Get accounting history records
|
|
//--------------------------------------------------------------------------
|
|
|
|
std::string acct_str;
|
|
|
|
rc = dump_acct(acct_str, start_time, end_time, start_index, chunk_size);
|
|
start_index += chunk_size;
|
|
|
|
ObjectXML xml(acct_str);
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Process the history records
|
|
//--------------------------------------------------------------------------
|
|
|
|
nodes.clear();
|
|
rc = xml.get_nodes("/HISTORY_RECORDS/HISTORY", nodes);
|
|
|
|
for ( auto node : nodes )
|
|
{
|
|
ObjectXML history(node);
|
|
|
|
history.xpath(vid, "/HISTORY/OID", -1);
|
|
|
|
history.xpath(h_stime, "/HISTORY/STIME", (time_t)0);
|
|
history.xpath(h_etime, "/HISTORY/ETIME", (time_t)0);
|
|
|
|
history.xpath(h_rstime, "/HISTORY/RSTIME", (time_t)0);
|
|
history.xpath(h_retime, "/HISTORY/RETIME", (time_t)0);
|
|
|
|
history.xpath<float>(cpu, "/HISTORY/VM/TEMPLATE/CPU", 0.0);
|
|
history.xpath(mem, "/HISTORY/VM/TEMPLATE/MEMORY", 0);
|
|
history.xpath<float>(disk, "sum(/HISTORY/VM/TEMPLATE/DISK/SIZE | "
|
|
"/HISTORY/VM/TEMPLATE/DISK/DISK_SNAPSHOT_TOTAL_SIZE)", 0.0);
|
|
|
|
history.xpath(cpu_cost, "/HISTORY/VM/TEMPLATE/CPU_COST", _default_cpu_cost);
|
|
history.xpath(mem_cost, "/HISTORY/VM/TEMPLATE/MEMORY_COST", _default_mem_cost);
|
|
history.xpath(disk_cost,"/HISTORY/VM/TEMPLATE/DISK_COST", _default_disk_cost);
|
|
|
|
auto& vm_cost = vm_costs[vid];
|
|
history.xpath(vm_cost.vmname, "/HISTORY/VM/NAME", "");
|
|
history.xpath(vm_cost.uid, "/HISTORY/VM/UID", -1);
|
|
history.xpath(vm_cost.uname, "/HISTORY/VM/UNAME", "");
|
|
history.xpath(vm_cost.gid, "/HISTORY/VM/GID", -1);
|
|
history.xpath(vm_cost.gname, "/HISTORY/VM/GNAME", "");
|
|
|
|
#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
|
|
<< "disk_cost " << disk_cost << endl
|
|
<< "cpu " << cpu << endl
|
|
<< "mem " << mem << endl
|
|
<< "disk " << disk;
|
|
|
|
NebulaLog::log("SHOWBACK", Log::DEBUG, debug);
|
|
#endif
|
|
|
|
for ( auto 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);
|
|
|
|
auto count_sb_record = [&](time_t st, time_t et, bool cpu_mem, bool disk_total, bool running_hours)
|
|
{
|
|
if( (et > t || et == 0) &&
|
|
(st != 0 && st <= t_next) ) {
|
|
|
|
time_t stime = t;
|
|
if(st != 0){
|
|
stime = max(t, st);
|
|
}
|
|
|
|
time_t etime = t_next;
|
|
if(et != 0){
|
|
etime = min(t_next, et);
|
|
}
|
|
|
|
float n_hours = difftime(etime, stime) / 60 / 60;
|
|
|
|
// Add to vm time slot.
|
|
SBRecord& totals = vm_cost.totals[t];
|
|
|
|
if (cpu_mem)
|
|
{
|
|
totals.cpu_cost += cpu_cost * cpu * n_hours;
|
|
totals.mem_cost += mem_cost * mem * n_hours;
|
|
}
|
|
if (disk_total)
|
|
{
|
|
totals.disk_cost+= disk_cost* disk* n_hours;
|
|
totals.hours += n_hours;
|
|
}
|
|
if (running_hours)
|
|
{
|
|
totals.rhours += n_hours;
|
|
}
|
|
}
|
|
};
|
|
|
|
if (_showback_only_running)
|
|
{
|
|
count_sb_record(h_stime, h_etime, false, true, false);
|
|
count_sb_record(h_rstime, h_retime, true, false, true);
|
|
}
|
|
else
|
|
{
|
|
count_sb_record(h_stime, h_etime, true, true, false);
|
|
count_sb_record(h_rstime, h_retime, false, false, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
xml.free_nodes(nodes);
|
|
}
|
|
while (!nodes.empty());
|
|
|
|
#ifdef SBDEBUG
|
|
auto debug_t_2 = std::chrono::high_resolution_clock::now();
|
|
#endif
|
|
|
|
// Write to DB
|
|
|
|
if (db->supports(SqlDB::SqlFeature::MULTIPLE_VALUE))
|
|
{
|
|
oss.str("");
|
|
|
|
oss << "REPLACE INTO " << one_db::vm_showback_table
|
|
<< " ("<< one_db::vm_showback_db_names <<") VALUES ";
|
|
|
|
sql_cmd_start = oss.str();
|
|
|
|
sql_cmd_separator = ",";
|
|
|
|
sql_cmd_end = "";
|
|
}
|
|
else
|
|
{
|
|
oss.str("");
|
|
oss << "BEGIN TRANSACTION; "
|
|
<< "REPLACE INTO " << one_db::vm_showback_table
|
|
<< " ("<< one_db::vm_showback_db_names <<") VALUES ";
|
|
|
|
sql_cmd_start = oss.str();
|
|
|
|
oss.str("");
|
|
oss << "; REPLACE INTO " << one_db::vm_showback_table
|
|
<< " ("<< one_db::vm_showback_db_names <<") VALUES ";
|
|
|
|
sql_cmd_separator = oss.str();
|
|
|
|
sql_cmd_end = "; COMMIT";
|
|
}
|
|
|
|
oss.str("");
|
|
|
|
int n_entries = 0;
|
|
|
|
for ( auto vm_it = vm_costs.begin(); vm_it != vm_costs.end(); vm_it++ )
|
|
{
|
|
int vmid = vm_it->first;
|
|
auto& vm_cost = vm_it->second;
|
|
map<time_t, SBRecord>& totals = vm_cost.totals;
|
|
|
|
for ( auto vm_month_it = totals.begin(); vm_month_it != totals.end(); vm_month_it++ )
|
|
{
|
|
localtime_r(&vm_month_it->first, &tmp_tm);
|
|
|
|
body.str("");
|
|
|
|
body << "<SHOWBACK>"
|
|
<< "<VMID>" << vmid << "</VMID>"
|
|
<< "<VMNAME>" << vm_cost.vmname << "</VMNAME>"
|
|
<< "<UID>" << vm_cost.uid << "</UID>"
|
|
<< "<GID>" << vm_cost.gid << "</GID>"
|
|
<< "<UNAME>" << vm_cost.uname << "</UNAME>"
|
|
<< "<GNAME>" << vm_cost.gname << "</GNAME>"
|
|
<< "<YEAR>" << tmp_tm.tm_year + 1900 << "</YEAR>"
|
|
<< "<MONTH>" << tmp_tm.tm_mon + 1 << "</MONTH>";
|
|
|
|
vm_month_it->second.to_xml(body) << "</SHOWBACK>";
|
|
|
|
sql_body = db->escape_str(body.str());
|
|
|
|
if ( sql_body == 0 )
|
|
{
|
|
error_str = "Error creating XML body.";
|
|
return -1;
|
|
}
|
|
|
|
if (n_entries == 0)
|
|
{
|
|
oss.str("");
|
|
oss << sql_cmd_start;
|
|
}
|
|
else
|
|
{
|
|
oss << sql_cmd_separator;
|
|
}
|
|
|
|
oss << " (" << vmid << ","
|
|
<< 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)
|
|
{
|
|
oss << sql_cmd_end;
|
|
|
|
rc = db->exec_wr(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 " << one_util::float_to_str(
|
|
vm_month_it->second.cpu_cost +
|
|
vm_month_it->second.mem_cost) << " €"
|
|
<< " HOURS " << vm_month_it->second.hours;
|
|
|
|
NebulaLog::log("SHOWBACK", Log::DEBUG, debug);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (n_entries > 0)
|
|
{
|
|
oss << sql_cmd_end;
|
|
|
|
rc = db->exec_wr(oss);
|
|
|
|
if (rc != 0)
|
|
{
|
|
error_str = "Error writing to DB.";
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
#ifdef SBDEBUG
|
|
auto debug_t_3 = std::chrono::high_resolution_clock::now();
|
|
|
|
debug.str("");
|
|
debug << "Preparation time: " << std::chrono::duration_cast<std::chrono::duration<double>>(debug_t_1 - debug_t_0).count();
|
|
NebulaLog::log("SHOWBACK", Log::DEBUG, debug);
|
|
|
|
debug.str("");
|
|
debug << "Time to process numbers: " << std::chrono::duration_cast<std::chrono::duration<double>>(debug_t_2 - debug_t_1).count();
|
|
NebulaLog::log("SHOWBACK", Log::DEBUG, debug);
|
|
|
|
debug.str("");
|
|
debug << "Time to write to db: " << std::chrono::duration_cast<std::chrono::duration<double>>(debug_t_3 - debug_t_2).count();
|
|
NebulaLog::log("SHOWBACK", Log::DEBUG, debug);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
void VirtualMachinePool::delete_attach_disk(std::unique_ptr<VirtualMachine> vm)
|
|
{
|
|
int uid;
|
|
int gid;
|
|
int oid;
|
|
|
|
VirtualMachineDisk * disk = nullptr;
|
|
|
|
disk = vm->delete_attach_disk();
|
|
|
|
uid = vm->get_uid();
|
|
gid = vm->get_gid();
|
|
oid = vm->get_oid();
|
|
|
|
update(vm.get());
|
|
|
|
vm.reset();
|
|
|
|
if ( disk == nullptr )
|
|
{
|
|
return;
|
|
}
|
|
|
|
Nebula& nd = Nebula::instance();
|
|
ImageManager* imagem = nd.get_imagem();
|
|
|
|
Template tmpl;
|
|
int image_id;
|
|
|
|
tmpl.set(disk->vector_attribute());
|
|
tmpl.add("VMS", 0);
|
|
|
|
if (disk->is_volatile())
|
|
{
|
|
Quotas::quota_del(Quotas::VM, uid, gid, &tmpl);
|
|
}
|
|
else
|
|
{
|
|
disk->vector_value("IMAGE_ID", image_id);
|
|
|
|
Quotas::quota_del(Quotas::IMAGE, uid, gid, &tmpl);
|
|
|
|
if (!disk->is_persistent())
|
|
{
|
|
Quotas::quota_del(Quotas::VM, uid, gid, &tmpl);
|
|
}
|
|
|
|
const Snapshots * snaps = disk->get_snapshots();
|
|
|
|
if (snaps != nullptr)
|
|
{
|
|
imagem->set_image_snapshots(image_id, *snaps);
|
|
}
|
|
else
|
|
{
|
|
imagem->clear_image_snapshots(image_id);
|
|
}
|
|
|
|
imagem->release_image(oid, image_id, false);
|
|
}
|
|
|
|
delete disk;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
void VirtualMachinePool::delete_attach_nic(std::unique_ptr<VirtualMachine> vm)
|
|
{
|
|
VirtualMachineNic * nic, * p_nic;
|
|
|
|
int uid;
|
|
int gid;
|
|
int oid;
|
|
|
|
set<int> pre, post;
|
|
|
|
Template tmpl;
|
|
|
|
vm->get_security_groups(pre);
|
|
|
|
nic = vm->delete_attach_nic();
|
|
|
|
if ( nic == nullptr )
|
|
{
|
|
update(vm.get());
|
|
|
|
return;
|
|
}
|
|
|
|
int nic_id = nic->get_nic_id();
|
|
|
|
if (!nic->is_alias())
|
|
{
|
|
vm->clear_nic_context(nic_id);
|
|
}
|
|
else
|
|
{
|
|
int parent_id, alias_id;
|
|
|
|
nic->vector_value("PARENT_ID", parent_id);
|
|
nic->vector_value("ALIAS_ID", alias_id);
|
|
|
|
vm->clear_nic_alias_context(parent_id, alias_id);
|
|
|
|
p_nic = vm->get_nic(parent_id);
|
|
|
|
// As NIC is an alias, parent ALIAS_IDS array should be updated
|
|
// to remove the alias_id
|
|
std::set<int> p_a_ids;
|
|
|
|
one_util::split_unique(p_nic->vector_value("ALIAS_IDS"), ',', p_a_ids);
|
|
|
|
p_a_ids.erase(nic_id);
|
|
|
|
p_nic->replace("ALIAS_IDS", one_util::join(p_a_ids, ','));
|
|
}
|
|
|
|
uid = vm->get_uid();
|
|
gid = vm->get_gid();
|
|
oid = vm->get_oid();
|
|
|
|
vm->get_security_groups(post);
|
|
|
|
for (auto sg :pre)
|
|
{
|
|
if ( post.find(sg) == post.end() )
|
|
{
|
|
vm->remove_security_group(sg);
|
|
}
|
|
}
|
|
|
|
std::set<int> a_ids;
|
|
|
|
one_util::split_unique(nic->vector_value("ALIAS_IDS"), ',', a_ids);
|
|
|
|
for (auto a_id : a_ids)
|
|
{
|
|
int alias_id;
|
|
|
|
auto nic_a = vm->get_nic(a_id);
|
|
|
|
nic_a->vector_value("ALIAS_ID", alias_id);
|
|
|
|
tmpl.set(nic_a->vector_attribute()->clone());
|
|
|
|
nic_a->release_network_leases(oid);
|
|
|
|
vm->clear_nic_alias_context(nic_id, alias_id);
|
|
}
|
|
|
|
nic->release_network_leases(oid);
|
|
|
|
vm->delete_attach_alias(nic);
|
|
|
|
update(vm.get());
|
|
|
|
vm.reset();
|
|
|
|
tmpl.set(nic->vector_attribute());
|
|
|
|
Quotas::quota_del(Quotas::NETWORK, uid, gid, &tmpl);
|
|
|
|
delete nic;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|