mirror of
https://github.com/OpenNebula/one.git
synced 2026-11-02 10:08:02 +09:00
516 lines
13 KiB
C++
516 lines
13 KiB
C++
/* ------------------------------------------------------------------------ */
|
|
/* Copyright 2002-2026, 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 "Cluster.h"
|
|
#include "GroupPool.h"
|
|
#include "Nebula.h"
|
|
#include "OneDB.h"
|
|
#include "DatastorePool.h"
|
|
#include "PlanPool.h"
|
|
#include "HostPool.h"
|
|
|
|
#include <sstream>
|
|
|
|
using namespace std;
|
|
|
|
/* ************************************************************************** */
|
|
/* Cluster :: Constructor/Destructor */
|
|
/* ************************************************************************** */
|
|
|
|
Cluster::Cluster(
|
|
int id,
|
|
const string& name,
|
|
std::unique_ptr<ClusterTemplate> cl_template,
|
|
const VectorAttribute& vnc_conf):
|
|
PoolObjectSQL(id, CLUSTER, name, -1, -1, "", "", one_db::cluster_table),
|
|
hosts("HOSTS"),
|
|
datastores("DATASTORES"),
|
|
vnets("VNETS"),
|
|
vnc_bitmap(vnc_conf, id, one_db::cluster_bitmap_table)
|
|
{
|
|
if (cl_template)
|
|
{
|
|
obj_template = std::move(cl_template);
|
|
}
|
|
else
|
|
{
|
|
obj_template = make_unique<ClusterTemplate>();
|
|
}
|
|
|
|
add_template_attribute("RESERVED_CPU", "");
|
|
add_template_attribute("RESERVED_MEM", "");
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
int Cluster::check_drop(string& error_msg)
|
|
{
|
|
ostringstream oss;
|
|
|
|
if ( hosts.size() > 0 )
|
|
{
|
|
oss << "Cluster " << oid << " is not empty, it contains "
|
|
<< hosts.size() << " hosts.";
|
|
|
|
goto error_common;
|
|
}
|
|
|
|
if ( datastores.size() > 0 )
|
|
{
|
|
oss << "Cluster " << oid << " is not empty, it contains "
|
|
<< datastores.size() << " datastores.";
|
|
|
|
goto error_common;
|
|
}
|
|
|
|
if ( vnets.size() > 0 )
|
|
{
|
|
oss << "Cluster " << oid << " is not empty, it contains "
|
|
<< vnets.size() << " vnets.";
|
|
|
|
goto error_common;
|
|
}
|
|
|
|
return 0;
|
|
|
|
error_common:
|
|
error_msg = oss.str();
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
int Cluster::get_default_system_ds(const set<int>& ds_set)
|
|
{
|
|
Nebula& nd = Nebula::instance();
|
|
|
|
DatastorePool* dspool = nd.get_dspool();
|
|
|
|
for (auto dsid : ds_set)
|
|
{
|
|
if ( auto ds = dspool->get_ro(dsid) )
|
|
{
|
|
if (ds->get_type() == Datastore::SYSTEM_DS)
|
|
{
|
|
return dsid;
|
|
}
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* ************************************************************************** */
|
|
/* Cluster :: Database Access Functions */
|
|
/* ************************************************************************** */
|
|
|
|
|
|
int Cluster::post_update_template(std::string& error, Template *_old_tmpl)
|
|
{
|
|
std::vector<VectorAttribute*> one_drs_attrs;
|
|
|
|
const auto one_drs_num = obj_template->get("ONE_DRS", one_drs_attrs);
|
|
|
|
if (one_drs_num <= 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
auto* one_drs = one_drs_attrs.front();
|
|
|
|
const auto validate_field = [&](const std::string& field_name, const std::regex& pattern)
|
|
{
|
|
std::string value = one_util::trim(one_drs->vector_value(field_name));
|
|
|
|
one_util::tolower(value);
|
|
|
|
if (!std::regex_match(value, pattern))
|
|
{
|
|
error = "Error cluster template contains invalid " + field_name ;
|
|
return false;
|
|
}
|
|
|
|
if (!value.empty())
|
|
{
|
|
// Store normalized value
|
|
one_drs->replace(field_name, value);
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
if (!validate_field("POLICY", std::regex("^(pack|balance)$")))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (!validate_field("AUTOMATION", std::regex("^(manual|partial|full)$")))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (!validate_field("MIGRATION_THRESHOLD", std::regex(R"(^(-1||\d+(\.\d+)?)$)")))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
static std::vector<std::string> numeric_attr = {
|
|
"CPU_USAGE_WEIGHT",
|
|
"CPU_WEIGHT",
|
|
"MEMORY_WEIGHT",
|
|
"NET_WEIGHT",
|
|
"DISK_WEIGHT",
|
|
"PREDICTIVE"
|
|
};
|
|
|
|
for (const auto &i : numeric_attr)
|
|
{
|
|
if (!validate_field(i, std::regex(R"(^(\d+(\.\d+)?|)$)")))
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// Check Cluster doesn't contains pinned hosts
|
|
auto hpool = Nebula::instance().get_hpool();
|
|
for (const auto& host_id : hosts.get_collection())
|
|
{
|
|
if (auto host = hpool->get_ro(host_id))
|
|
{
|
|
if (host->is_pinned())
|
|
{
|
|
error = "Error cluster contains pinned host " + std::to_string(host_id);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Cluster::insert_replace(SqlDB *db, bool replace, string& error_str)
|
|
{
|
|
ostringstream oss;
|
|
|
|
int rc;
|
|
string xml_body;
|
|
|
|
char * sql_name;
|
|
char * sql_xml;
|
|
|
|
// Set the owner and group to oneadmin
|
|
set_user(0, "");
|
|
set_group(GroupPool::ONEADMIN_ID, GroupPool::ONEADMIN_NAME);
|
|
|
|
// Update the Cluster
|
|
|
|
sql_name = db->escape_str(name);
|
|
|
|
if ( sql_name == 0 )
|
|
{
|
|
goto error_name;
|
|
}
|
|
|
|
sql_xml = db->escape_str(to_xml(xml_body));
|
|
|
|
if ( sql_xml == 0 )
|
|
{
|
|
goto error_body;
|
|
}
|
|
|
|
if ( validate_xml(sql_xml) != 0 )
|
|
{
|
|
goto error_xml;
|
|
}
|
|
|
|
if ( replace )
|
|
{
|
|
oss << "UPDATE " << one_db::cluster_table << " SET "
|
|
<< "name = '" << sql_name << "', "
|
|
<< "body = '" << sql_xml << "', "
|
|
<< "uid = " << uid << ", "
|
|
<< "gid = " << gid << ", "
|
|
<< "owner_u = " << owner_u << ", "
|
|
<< "group_u = " << group_u << ", "
|
|
<< "other_u = " << other_u
|
|
<< " WHERE oid = " << oid;
|
|
|
|
}
|
|
else
|
|
{
|
|
oss << "INSERT INTO " << one_db::cluster_table
|
|
<< " (" << one_db::cluster_db_names << ") VALUES ("
|
|
<< oid << ","
|
|
<< "'" << sql_name << "',"
|
|
<< "'" << sql_xml << "',"
|
|
<< uid << ","
|
|
<< gid << ","
|
|
<< owner_u << ","
|
|
<< group_u << ","
|
|
<< other_u << ")";
|
|
}
|
|
|
|
rc = db->exec_wr(oss);
|
|
|
|
db->free_str(sql_name);
|
|
db->free_str(sql_xml);
|
|
|
|
return rc;
|
|
|
|
error_xml:
|
|
db->free_str(sql_name);
|
|
db->free_str(sql_xml);
|
|
|
|
error_str = "Error transforming the Cluster to XML.";
|
|
|
|
goto error_common;
|
|
|
|
error_body:
|
|
db->free_str(sql_name);
|
|
goto error_generic;
|
|
|
|
error_name:
|
|
goto error_generic;
|
|
|
|
error_generic:
|
|
error_str = "Error inserting Cluster in DB.";
|
|
error_common:
|
|
return -1;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
int Cluster::bootstrap(SqlDB * db)
|
|
{
|
|
int rc;
|
|
ostringstream oss;
|
|
|
|
oss.str(one_db::cluster_db_bootstrap);
|
|
rc = db->exec_local_wr(oss);
|
|
|
|
oss.str(one_db::cluster_datastore_db_bootstrap);
|
|
rc += db->exec_local_wr(oss);
|
|
|
|
oss.str(one_db::cluster_network_db_bootstrap);
|
|
rc += db->exec_local_wr(oss);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
int Cluster::insert(SqlDB *db, std::string& error_str)
|
|
{
|
|
int rc;
|
|
|
|
rc = insert_replace(db, false, error_str);
|
|
|
|
if ( rc != 0 )
|
|
{
|
|
return rc;
|
|
}
|
|
|
|
PlanPool * plpool = Nebula::instance().get_planpool();
|
|
|
|
Plan plan(oid);
|
|
|
|
if (plpool->insert(&plan) != 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return vnc_bitmap.insert(oid, db);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
string& Cluster::to_xml(string& xml) const
|
|
{
|
|
ostringstream oss;
|
|
string host_collection_xml;
|
|
string ds_collection_xml;
|
|
string vnet_collection_xml;
|
|
string template_xml;
|
|
|
|
oss <<
|
|
"<CLUSTER>" <<
|
|
"<ID>" << oid << "</ID>" <<
|
|
"<NAME>" << name << "</NAME>" <<
|
|
hosts.to_xml(host_collection_xml) <<
|
|
datastores.to_xml(ds_collection_xml) <<
|
|
vnets.to_xml(vnet_collection_xml) <<
|
|
obj_template->to_xml(template_xml) <<
|
|
plan_xml <<
|
|
"</CLUSTER>";
|
|
|
|
xml = oss.str();
|
|
|
|
return xml;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
std::string& Cluster::template_xml(std::string& xml) const
|
|
{
|
|
ostringstream oss;
|
|
|
|
obj_template->each_attribute([&oss](const Attribute * a){
|
|
a->to_xml(oss);
|
|
});
|
|
|
|
xml = oss.str();
|
|
|
|
return xml;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
int Cluster::from_xml(const string& xml)
|
|
{
|
|
int rc = 0;
|
|
vector<xmlNodePtr> content;
|
|
|
|
// Initialize the internal XML object
|
|
update_from_str(xml);
|
|
|
|
// Get class base attributes
|
|
rc += xpath(oid, "/CLUSTER/ID", -1);
|
|
rc += xpath(name, "/CLUSTER/NAME", "not_found");
|
|
|
|
// Set oneadmin as the owner
|
|
set_user(0, "");
|
|
|
|
// Set the Cluster ID as the cluster it belongs to
|
|
set_group(oid, name);
|
|
|
|
// Get associated classes
|
|
rc += hosts.from_xml(this, "/CLUSTER/");
|
|
rc += datastores.from_xml(this, "/CLUSTER/");
|
|
rc += vnets.from_xml(this, "/CLUSTER/");
|
|
|
|
ObjectXML::get_nodes("/CLUSTER/TEMPLATE", content);
|
|
|
|
if (content.empty())
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
rc += obj_template->from_xml_node(content[0]);
|
|
|
|
ObjectXML::free_nodes(content);
|
|
|
|
if (rc != 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
void Cluster::set_error_message(const char *mod, const string& name, const string& message)
|
|
{
|
|
static const int MAX_ERROR_MSG_LENGTH = 100;
|
|
|
|
SingleAttribute * attr;
|
|
ostringstream error_value;
|
|
|
|
error_value << one_util::log_time() << ": " << message.substr(0, MAX_ERROR_MSG_LENGTH);
|
|
|
|
if (message.length() >= MAX_ERROR_MSG_LENGTH)
|
|
{
|
|
error_value << "... see more details in oned.log";
|
|
|
|
NebulaLog::error(mod, message);
|
|
}
|
|
|
|
attr = new SingleAttribute(name, error_value.str());
|
|
|
|
obj_template->erase(name);
|
|
obj_template->set(attr);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
void Cluster::load_plan()
|
|
{
|
|
auto plpool = Nebula::instance().get_planpool();
|
|
|
|
if (auto plan = plpool->get_ro(oid))
|
|
{
|
|
if (plan->state() != PlanState::NONE)
|
|
{
|
|
plan_xml = plan->to_xml();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
Cluster::DrsAutomation Cluster::automation() const
|
|
{
|
|
auto *vattr = obj_template->get("ONE_DRS");
|
|
|
|
if (!vattr)
|
|
{
|
|
return DrsAutomation::MANUAL;
|
|
}
|
|
|
|
string str = vattr->vector_value("AUTOMATION");
|
|
|
|
one_util::tolower(str);
|
|
|
|
if ( str == "partial" )
|
|
{
|
|
return Cluster::PARTIAL;
|
|
}
|
|
else if ( str == "full" )
|
|
{
|
|
return Cluster::FULL;
|
|
}
|
|
|
|
return Cluster::MANUAL;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
int Cluster::drop(SqlDB *db)
|
|
{
|
|
int rc;
|
|
|
|
rc = vnc_bitmap.drop(db);
|
|
|
|
if (rc != 0)
|
|
{
|
|
return rc;
|
|
}
|
|
|
|
return PoolObjectSQL::drop(db);
|
|
}
|