Implement support for arrays in custom variables

fixes #6544
This commit is contained in:
Gunnar Beutner 2014-11-02 07:22:00 +01:00
parent 9844a2c990
commit 4677014b6d
13 changed files with 368 additions and 176 deletions

View File

@ -25,12 +25,14 @@
#include "base/socket.hpp"
#include "base/utility.hpp"
#include "base/json.hpp"
#include "base/objectlock.hpp"
#include <mmatch.h>
#include <boost/lexical_cast.hpp>
#include <boost/foreach.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <ios>
#include <fstream>
#include <iostream>
@ -772,6 +774,35 @@ String Utility::NaturalJoin(const std::vector<String>& tokens)
return result;
}
String Utility::Join(const Array::Ptr& tokens, char separator)
{
String result;
bool first = true;
ObjectLock olock(tokens);
BOOST_FOREACH(const Value& vtoken, tokens) {
String token = Convert::ToString(vtoken);
boost::algorithm::replace_all(token, "\\", "\\\\");
char sep_before[2], sep_after[3];
sep_before[0] = separator;
sep_before[1] = '\0';
sep_after[0] = '\\';
sep_after[1] = separator;
sep_after[2] = '\0';
boost::algorithm::replace_all(token, sep_before, sep_after);
if (first)
first = false;
else
result += String(1, separator);
result += token;
}
return result;
}
String Utility::FormatDuration(double duration)
{
std::vector<String> tokens;

View File

@ -22,6 +22,7 @@
#include "base/i2-base.hpp"
#include "base/string.hpp"
#include "base/array.hpp"
#include <typeinfo>
#include <boost/function.hpp>
#include <boost/thread/tss.hpp>
@ -96,6 +97,7 @@ public:
static void QueueAsyncCallback(const boost::function<void (void)>& callback, SchedulerPolicy policy = DefaultScheduler);
static String NaturalJoin(const std::vector<String>& tokens);
static String Join(const Array::Ptr& tokens, char separator);
static String FormatDuration(double duration);
static String FormatDateTime(const char *format, double ts);

View File

@ -516,14 +516,22 @@ void StatusDataWriter::DumpCustomAttributes(std::ostream& fp, const CustomVarObj
ObjectLock olock(vars);
BOOST_FOREACH(const Dictionary::Pair& kv, vars) {
if (!kv.first.IsEmpty()) {
if (kv.first.IsEmpty())
continue;
String value;
if (kv.second.IsObjectType<Array>())
value = Utility::Join(kv.second, ';');
else
value = kv.second;
fp << "\t";
if (!CompatUtility::IsLegacyAttribute(object, kv.first))
fp << "_";
fp << kv.first << "\t" << kv.second << "\n";
}
fp << kv.first << "\t" << value << "\n";
}
}

View File

@ -28,8 +28,10 @@ ApplyRule::RuleMap ApplyRule::m_Rules;
ApplyRule::CallbackMap ApplyRule::m_Callbacks;
ApplyRule::ApplyRule(const String& targetType, const String& name, const Expression::Ptr& expression,
const Expression::Ptr& filter, const DebugInfo& di, const Dictionary::Ptr& scope)
: m_TargetType(targetType), m_Name(name), m_Expression(expression), m_Filter(filter), m_DebugInfo(di), m_Scope(scope)
const Expression::Ptr& filter, const String& fvar, const Expression::Ptr& fterm,
const DebugInfo& di, const Dictionary::Ptr& scope)
: m_TargetType(targetType), m_Name(name), m_Expression(expression), m_Filter(filter), m_FVar(fvar),
m_FTerm(fterm), m_DebugInfo(di), m_Scope(scope)
{ }
String ApplyRule::GetTargetType(void) const
@ -52,6 +54,16 @@ Expression::Ptr ApplyRule::GetFilter(void) const
return m_Filter;
}
String ApplyRule::GetFVar(void) const
{
return m_FVar;
}
Expression::Ptr ApplyRule::GetFTerm(void) const
{
return m_FTerm;
}
DebugInfo ApplyRule::GetDebugInfo(void) const
{
return m_DebugInfo;
@ -63,10 +75,10 @@ Dictionary::Ptr ApplyRule::GetScope(void) const
}
void ApplyRule::AddRule(const String& sourceType, const String& targetType, const String& name,
const Expression::Ptr& expression, const Expression::Ptr& filter,
const DebugInfo& di, const Dictionary::Ptr& scope)
const Expression::Ptr& expression, const Expression::Ptr& filter, const String& fvar,
const Expression::Ptr& fterm, const DebugInfo& di, const Dictionary::Ptr& scope)
{
m_Rules[sourceType].push_back(ApplyRule(targetType, name, expression, filter, di, scope));
m_Rules[sourceType].push_back(ApplyRule(targetType, name, expression, filter, fvar, fterm, di, scope));
}
bool ApplyRule::EvaluateFilter(const Dictionary::Ptr& scope) const

View File

@ -42,13 +42,15 @@ public:
String GetName(void) const;
Expression::Ptr GetExpression(void) const;
Expression::Ptr GetFilter(void) const;
String GetFVar(void) const;
Expression::Ptr GetFTerm(void) const;
DebugInfo GetDebugInfo(void) const;
Dictionary::Ptr GetScope(void) const;
bool EvaluateFilter(const Dictionary::Ptr& scope) const;
static void AddRule(const String& sourceType, const String& targetType, const String& name, const Expression::Ptr& expression,
const Expression::Ptr& filter, const DebugInfo& di, const Dictionary::Ptr& scope);
const Expression::Ptr& filter, const String& fvar, const Expression::Ptr& fterm, const DebugInfo& di, const Dictionary::Ptr& scope);
static void EvaluateRules(bool clear);
static void RegisterType(const String& sourceType, const std::vector<String>& targetTypes, const ApplyRule::Callback& callback);
@ -61,6 +63,8 @@ private:
String m_Name;
Expression::Ptr m_Expression;
Expression::Ptr m_Filter;
String m_FVar;
Expression::Ptr m_FTerm;
DebugInfo m_DebugInfo;
Dictionary::Ptr m_Scope;
@ -68,7 +72,8 @@ private:
static RuleMap m_Rules;
ApplyRule(const String& targetType, const String& name, const Expression::Ptr& expression,
const Expression::Ptr& filter, const DebugInfo& di, const Dictionary::Ptr& scope);
const Expression::Ptr& filter, const String& fvar, const Expression::Ptr& fterm,
const DebugInfo& di, const Dictionary::Ptr& scope);
};
}

View File

@ -33,7 +33,10 @@
%attribute %dictionary "methods",
%attribute %dictionary "vars" {
%attribute %string "*",
%attribute %array "*" {
%attribute %string "*"
}
},
}

View File

@ -218,6 +218,8 @@ static std::stack<bool> m_ObjectAssign;
static std::stack<bool> m_SeenAssign;
static std::stack<Expression::Ptr> m_Assign;
static std::stack<Expression::Ptr> m_Ignore;
static std::stack<String> m_FVar;
static std::stack<Expression::Ptr> m_FTerm;
void ConfigCompiler::Compile(void)
{
@ -231,6 +233,8 @@ void ConfigCompiler::Compile(void)
m_SeenAssign = std::stack<bool>();
m_Assign = std::stack<Expression::Ptr>();
m_Ignore = std::stack<Expression::Ptr>();
m_FVar = std::stack<String>();
m_FTerm = std::stack<Expression::Ptr>();
try {
yyparse(this);
@ -836,14 +840,27 @@ target_type_specifier: /* empty */
}
;
apply_for_specifier: /* empty */
| T_FOR '(' identifier T_IN rterm ')'
{
m_FVar.top() = $3;
free($3);
m_FTerm.top() = *$5;
delete $5;
}
;
apply:
{
m_Apply.push(true);
m_SeenAssign.push(false);
m_Assign.push(make_shared<Expression>(&Expression::OpLiteral, false, DebugInfo()));
m_Ignore.push(make_shared<Expression>(&Expression::OpLiteral, false, DebugInfo()));
m_FVar.push("");
m_FTerm.push(Expression::Ptr());
}
T_APPLY identifier rterm target_type_specifier rterm
T_APPLY identifier rterm apply_for_specifier target_type_specifier rterm
{
m_Apply.pop();
@ -851,8 +868,8 @@ apply:
free($3);
Expression::Ptr aname = *$4;
delete $4;
String target = $5;
free($5);
String target = $6;
free($6);
if (!ApplyRule::IsValidSourceType(type))
BOOST_THROW_EXCEPTION(ConfigError("'apply' cannot be used with type '" + type + "'") << errinfo_debuginfo(DebugInfoRange(@2, @3)));
@ -878,8 +895,8 @@ apply:
BOOST_THROW_EXCEPTION(ConfigError("'apply' target type '" + target + "' is invalid") << errinfo_debuginfo(DebugInfoRange(@2, @5)));
}
Expression::Ptr exprl = *$6;
delete $6;
Expression::Ptr exprl = *$7;
delete $7;
exprl->MakeInline();
@ -895,11 +912,19 @@ apply:
Expression::Ptr filter = make_shared<Expression>(&Expression::OpLogicalAnd, m_Assign.top(), rex, DebugInfoRange(@2, @5));
m_Assign.pop();
String fvar = m_FVar.top();
m_FVar.pop();
Expression::Ptr fterm = m_FTerm.top();
m_FTerm.pop();
Array::Ptr args = make_shared<Array>();
args->Add(type);
args->Add(target);
args->Add(aname);
args->Add(filter);
args->Add(fvar);
args->Add(fterm);
$$ = new Value(make_shared<Expression>(&Expression::OpApply, args, exprl, DebugInfoRange(@2, @5)));
}

View File

@ -551,10 +551,12 @@ Value Expression::OpApply(const Expression* expr, const Dictionary::Ptr& locals,
String target = left->Get(1);
Expression::Ptr aname = left->Get(2);
Expression::Ptr filter = left->Get(3);
String fvar = left->Get(4);
Expression::Ptr fterm = left->Get(5);
String name = aname->Evaluate(locals, dhint);
ApplyRule::AddRule(type, target, name, exprl, filter, expr->m_DebugInfo, locals);
ApplyRule::AddRule(type, target, name, exprl, filter, fvar, fterm, expr->m_DebugInfo, locals);
return Empty;
}

View File

@ -167,7 +167,16 @@ void DbObject::SendVarsConfigUpdate(void)
ObjectLock olock (vars);
BOOST_FOREACH(const Dictionary::Pair& kv, vars) {
if (!kv.first.IsEmpty()) {
if (kv.first.IsEmpty())
continue;
String value;
if (kv.second.IsObjectType<Array>())
value = Utility::Join(kv.second, ';');
else
value = kv.second;
int overridden = custom_var_object->IsVarOverridden(kv.first) ? 1 : 0;
Log(LogDebug, "DbObject")
@ -175,8 +184,8 @@ void DbObject::SendVarsConfigUpdate(void)
<< "' overridden: " << overridden;
Dictionary::Ptr fields = make_shared<Dictionary>();
fields->Set("varname", Convert::ToString(kv.first));
fields->Set("varvalue", Convert::ToString(kv.second));
fields->Set("varname", kv.first);
fields->Set("varvalue", value);
fields->Set("config_type", 1);
fields->Set("has_been_modified", overridden);
fields->Set("object_id", obj);
@ -191,7 +200,6 @@ void DbObject::SendVarsConfigUpdate(void)
}
}
}
}
void DbObject::SendVarsStatusUpdate(void)
{
@ -211,7 +219,9 @@ void DbObject::SendVarsStatusUpdate(void)
ObjectLock olock (vars);
BOOST_FOREACH(const Dictionary::Pair& kv, vars) {
if (!kv.first.IsEmpty()) {
if (kv.first.IsEmpty() || kv.second.IsObject())
continue;
int overridden = custom_var_object->IsVarOverridden(kv.first) ? 1 : 0;
Log(LogDebug, "DbObject")
@ -240,8 +250,6 @@ void DbObject::SendVarsStatusUpdate(void)
OnQuery(query);
}
}
}
}
double DbObject::GetLastConfigUpdate(void) const

View File

@ -63,12 +63,35 @@ bool Dependency::EvaluateApplyRuleOne(const Checkable::Ptr& checkable, const App
if (!rule.EvaluateFilter(locals))
return false;
Array::Ptr instances;
if (rule.GetFTerm()) {
Value vinstances = rule.GetFTerm()->Evaluate(locals);
if (!vinstances.IsObjectType<Array>())
BOOST_THROW_EXCEPTION(std::invalid_argument("for expression must be an array"));
instances = vinstances;
} else {
instances = make_shared<Array>();
instances->Add("");
}
ObjectLock olock(instances);
BOOST_FOREACH(const String& instance, instances) {
String objName = rule.GetName();
if (!rule.GetFVar().IsEmpty()) {
locals->Set(rule.GetFVar(), instance);
objName += "-" + instance;
}
Log(LogDebug, "Dependency")
<< "Applying dependency '" << rule.GetName() << "' to object '" << checkable->GetName() << "' for rule " << di;
ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(di);
builder->SetType("Dependency");
builder->SetName(rule.GetName());
builder->SetName(objName);
builder->SetScope(locals);
builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
@ -103,6 +126,7 @@ bool Dependency::EvaluateApplyRuleOne(const Checkable::Ptr& checkable, const App
dependencyItem->Register();
DynamicObject::Ptr dobj = dependencyItem->Commit();
dobj->OnConfigLoaded();
}
return true;
}

View File

@ -63,12 +63,35 @@ bool Notification::EvaluateApplyRuleOne(const Checkable::Ptr& checkable, const A
if (!rule.EvaluateFilter(locals))
return false;
Array::Ptr instances;
if (rule.GetFTerm()) {
Value vinstances = rule.GetFTerm()->Evaluate(locals);
if (!vinstances.IsObjectType<Array>())
BOOST_THROW_EXCEPTION(std::invalid_argument("for expression must be an array"));
instances = vinstances;
} else {
instances = make_shared<Array>();
instances->Add("");
}
ObjectLock olock(instances);
BOOST_FOREACH(const String& instance, instances) {
String objName = rule.GetName();
if (!rule.GetFVar().IsEmpty()) {
locals->Set(rule.GetFVar(), instance);
objName += "-" + instance;
}
Log(LogDebug, "Notification")
<< "Applying notification '" << rule.GetName() << "' to object '" << checkable->GetName() << "' for rule " << di;
ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(di);
builder->SetType("Notification");
builder->SetName(rule.GetName());
builder->SetName(objName);
builder->SetScope(locals);
builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
@ -98,6 +121,7 @@ bool Notification::EvaluateApplyRuleOne(const Checkable::Ptr& checkable, const A
notificationItem->Register();
DynamicObject::Ptr dobj = notificationItem->Commit();
dobj->OnConfigLoaded();
}
return true;
}

View File

@ -62,12 +62,35 @@ bool ScheduledDowntime::EvaluateApplyRule(const Checkable::Ptr& checkable, const
if (!rule.EvaluateFilter(locals))
return false;
Array::Ptr instances;
if (rule.GetFTerm()) {
Value vinstances = rule.GetFTerm()->Evaluate(locals);
if (!vinstances.IsObjectType<Array>())
BOOST_THROW_EXCEPTION(std::invalid_argument("for expression must be an array"));
instances = vinstances;
} else {
instances = make_shared<Array>();
instances->Add("");
}
ObjectLock olock(instances);
BOOST_FOREACH(const String& instance, instances) {
String objName = rule.GetName();
if (!rule.GetFVar().IsEmpty()) {
locals->Set(rule.GetFVar(), instance);
objName += "-" + instance;
}
Log(LogDebug, "ScheduledDowntime")
<< "Applying scheduled downtime '" << rule.GetName() << "' to object '" << checkable->GetName() << "' for rule " << di;
ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(di);
builder->SetType("ScheduledDowntime");
builder->SetName(rule.GetName());
builder->SetName(objName);
builder->SetScope(locals);
builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
@ -97,6 +120,7 @@ bool ScheduledDowntime::EvaluateApplyRule(const Checkable::Ptr& checkable, const
downtimeItem->Register();
DynamicObject::Ptr dobj = downtimeItem->Commit();
dobj->OnConfigLoaded();
}
return true;
}

View File

@ -55,12 +55,35 @@ bool Service::EvaluateApplyRuleOne(const Host::Ptr& host, const ApplyRule& rule)
if (!rule.EvaluateFilter(locals))
return false;
Array::Ptr instances;
if (rule.GetFTerm()) {
Value vinstances = rule.GetFTerm()->Evaluate(locals);
if (!vinstances.IsObjectType<Array>())
BOOST_THROW_EXCEPTION(std::invalid_argument("for expression must be an array"));
instances = vinstances;
} else {
instances = make_shared<Array>();
instances->Add("");
}
ObjectLock olock(instances);
BOOST_FOREACH(const String& instance, instances) {
String objName = rule.GetName();
if (!rule.GetFVar().IsEmpty()) {
locals->Set(rule.GetFVar(), instance);
objName += "-" + instance;
}
Log(LogDebug, "Service")
<< "Applying service '" << rule.GetName() << "' to host '" << host->GetName() << "' for rule " << di;
<< "Applying service '" << objName << "' to host '" << host->GetName() << "' for rule " << di;
ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(di);
builder->SetType("Service");
builder->SetName(rule.GetName());
builder->SetName(objName);
builder->SetScope(locals);
builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
@ -70,7 +93,7 @@ bool Service::EvaluateApplyRuleOne(const Host::Ptr& host, const ApplyRule& rule)
builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
make_shared<Expression>(&Expression::OpLiteral, "name", di),
make_shared<Expression>(&Expression::OpLiteral, rule.GetName(), di),
make_shared<Expression>(&Expression::OpLiteral, objName, di),
di));
String zone = host->GetZone();
@ -88,6 +111,7 @@ bool Service::EvaluateApplyRuleOne(const Host::Ptr& host, const ApplyRule& rule)
serviceItem->Register();
DynamicObject::Ptr dobj = serviceItem->Commit();
dobj->OnConfigLoaded();
}
return true;
}