Merge pull request #5242 from Icinga/feature/object-decl-expr

Allow expressions for the type in object/template declarations

fixes #5242
This commit is contained in:
Gunnar Beutner 2017-05-11 14:30:29 +02:00 committed by GitHub
commit 1c255140b5
18 changed files with 72 additions and 71 deletions

View File

@ -143,7 +143,7 @@ bool DaemonUtility::ValidateConfigFiles(const std::vector<std::string>& configs,
if (!success)
return false;
String appType = ScriptGlobal::Get("ApplicationType", &Empty);
Type::Ptr appType = Type::GetByName(ScriptGlobal::Get("ApplicationType", &Empty));
if (ConfigItem::GetItems(appType).empty()) {
ConfigItemBuilder::Ptr builder = new ConfigItemBuilder();

View File

@ -372,7 +372,7 @@ object:
context->m_Assign.push(0);
context->m_Ignore.push(0);
}
object_declaration identifier optional_rterm use_specifier default_specifier ignore_specifier
object_declaration rterm optional_rterm use_specifier default_specifier ignore_specifier
{
BeginFlowControlBlock(context, FlowControlReturn, false);
}
@ -388,9 +388,6 @@ object:
if (!abstract && defaultTmpl)
BOOST_THROW_EXCEPTION(ScriptError("'default' keyword is invalid for object definitions", DebugInfoRange(@2, @4)));
String type = *$3;
delete $3;
bool seen_assign = context->m_SeenAssign.top();
context->m_SeenAssign.pop();
@ -406,9 +403,6 @@ object:
Expression *filter = NULL;
if (seen_assign) {
if (!ObjectRule::IsValidSourceType(type))
BOOST_THROW_EXCEPTION(ScriptError("object rule 'assign' cannot be used for type '" + type + "'", DebugInfoRange(@2, @4)));
if (ignore) {
Expression *rex = new LogicalNegateExpression(ignore, DebugInfoRange(@2, @5));
@ -416,13 +410,10 @@ object:
} else
filter = assign;
} else if (seen_ignore) {
if (!ObjectRule::IsValidSourceType(type))
BOOST_THROW_EXCEPTION(ScriptError("object rule 'ignore' cannot be used for type '" + type + "'", DebugInfoRange(@2, @4)));
else
BOOST_THROW_EXCEPTION(ScriptError("object rule 'ignore' is missing 'assign' for type '" + type + "'", DebugInfoRange(@2, @4)));
BOOST_THROW_EXCEPTION(ScriptError("object rule 'ignore where' cannot be used without 'assign where'", DebugInfoRange(@2, @4)));
}
$$ = new ObjectExpression(abstract, type, $4, filter, context->GetZone(), context->GetPackage(), $5, $6, $7, $9, DebugInfoRange(@2, @7));
$$ = new ObjectExpression(abstract, $3, $4, filter, context->GetZone(), context->GetPackage(), $5, $6, $7, $9, DebugInfoRange(@2, @7));
}
;

View File

@ -59,7 +59,7 @@ REGISTER_SCRIPTFUNCTION_NS(Internal, run_with_activation_context, &ConfigItem::R
* @param exprl Expression list for the item.
* @param debuginfo Debug information.
*/
ConfigItem::ConfigItem(const String& type, const String& name,
ConfigItem::ConfigItem(const Type::Ptr& type, const String& name,
bool abstract, const boost::shared_ptr<Expression>& exprl,
const boost::shared_ptr<Expression>& filter, bool defaultTmpl, bool ignoreOnError,
const DebugInfo& debuginfo, const Dictionary::Ptr& scope,
@ -77,7 +77,7 @@ ConfigItem::ConfigItem(const String& type, const String& name,
*
* @returns The type.
*/
String ConfigItem::GetType(void) const
Type::Ptr ConfigItem::GetType(void) const
{
return m_Type;
}
@ -157,7 +157,7 @@ class DefaultValidationUtils : public ValidationUtils
public:
virtual bool ValidateName(const String& type, const String& name) const override
{
ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(type, name);
ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(Type::GetByName(type), name);
if (!item || (item && item->IsAbstract()))
return false;
@ -180,7 +180,7 @@ ConfigObject::Ptr ConfigItem::Commit(bool discard)
#endif /* I2_DEBUG */
/* Make sure the type is valid. */
Type::Ptr type = Type::GetByName(GetType());
Type::Ptr type = GetType();
if (!type || !ConfigObject::TypeInstance->IsAssignableFrom(type))
BOOST_THROW_EXCEPTION(ScriptError("Type '" + GetType() + "' does not exist.", m_DebugInfo));
@ -320,15 +320,13 @@ ConfigObject::Ptr ConfigItem::Commit(bool discard)
*/
void ConfigItem::Register(void)
{
Type::Ptr type = Type::GetByName(m_Type);
m_ActivationContext = ActivationContext::GetCurrentContext();
boost::mutex::scoped_lock lock(m_Mutex);
/* If this is a non-abstract object with a composite name
* we register it in m_UnnamedItems instead of m_Items. */
if (!m_Abstract && dynamic_cast<NameComposer *>(type.get()))
if (!m_Abstract && dynamic_cast<NameComposer *>(m_Type.get()))
m_UnnamedItems.push_back(this);
else {
auto& items = m_Items[m_Type];
@ -337,7 +335,7 @@ void ConfigItem::Register(void)
if (it != items.end()) {
std::ostringstream msgbuf;
msgbuf << "A configuration item of type '" << GetType()
msgbuf << "A configuration item of type '" << m_Type->GetName()
<< "' and name '" << GetName() << "' already exists ("
<< it->second->GetDebugInfo() << "), new declaration: " << GetDebugInfo();
BOOST_THROW_EXCEPTION(ScriptError(msgbuf.str()));
@ -373,7 +371,7 @@ void ConfigItem::Unregister(void)
* @param name The name of the ConfigItem that is to be looked up.
* @returns The configuration item.
*/
ConfigItem::Ptr ConfigItem::GetByTypeAndName(const String& type, const String& name)
ConfigItem::Ptr ConfigItem::GetByTypeAndName(const Type::Ptr& type, const String& name)
{
boost::mutex::scoped_lock lock(m_Mutex);
@ -442,26 +440,26 @@ bool ConfigItem::CommitNewItems(const ActivationContext::Ptr& context, WorkQueue
if (upq.HasExceptions())
return false;
std::set<String> types;
std::set<Type::Ptr> types;
for (const Type::Ptr& type : Type::GetAllTypes()) {
if (ConfigObject::TypeInstance->IsAssignableFrom(type))
types.insert(type->GetName());
types.insert(type);
}
std::set<String> completed_types;
std::set<Type::Ptr> completed_types;
while (types.size() != completed_types.size()) {
for (const String& type : types) {
for (const Type::Ptr& type : types) {
if (completed_types.find(type) != completed_types.end())
continue;
Type::Ptr ptype = Type::GetByName(type);
bool unresolved_dep = false;
/* skip this type (for now) if there are unresolved load dependencies */
for (const String& loadDep : ptype->GetLoadDependencies()) {
if (types.find(loadDep) != types.end() && completed_types.find(loadDep) == completed_types.end()) {
for (const String& loadDep : type->GetLoadDependencies()) {
Type::Ptr pLoadDep = Type::GetByName(loadDep);
if (types.find(pLoadDep) != types.end() && completed_types.find(pLoadDep) == completed_types.end()) {
unresolved_dep = true;
break;
}
@ -508,17 +506,17 @@ bool ConfigItem::CommitNewItems(const ActivationContext::Ptr& context, WorkQueue
if (upq.HasExceptions())
return false;
for (const String& loadDep : ptype->GetLoadDependencies()) {
for (const String& loadDep : type->GetLoadDependencies()) {
for (const ItemPair& ip : items) {
const ConfigItem::Ptr& item = ip.first;
if (!item->m_Object)
continue;
if (item->m_Type == loadDep) {
if (item->m_Type->GetName() == loadDep) {
upq.Enqueue([&]() {
ActivationScope ascope(item->m_ActivationContext);
item->m_Object->CreateChildObjects(ptype);
item->m_Object->CreateChildObjects(type);
});
}
}
@ -683,7 +681,7 @@ bool ConfigItem::RunWithActivationContext(const Function::Ptr& function)
return true;
}
std::vector<ConfigItem::Ptr> ConfigItem::GetItems(const String& type)
std::vector<ConfigItem::Ptr> ConfigItem::GetItems(const Type::Ptr& type)
{
std::vector<ConfigItem::Ptr> items;
@ -703,7 +701,7 @@ std::vector<ConfigItem::Ptr> ConfigItem::GetItems(const String& type)
return items;
}
std::vector<ConfigItem::Ptr> ConfigItem::GetDefaultTemplates(const String& type)
std::vector<ConfigItem::Ptr> ConfigItem::GetDefaultTemplates(const Type::Ptr& type)
{
std::vector<ConfigItem::Ptr> items;

View File

@ -40,14 +40,14 @@ class I2_CONFIG_API ConfigItem : public Object {
public:
DECLARE_PTR_TYPEDEFS(ConfigItem);
ConfigItem(const String& type, const String& name, bool abstract,
ConfigItem(const Type::Ptr& type, const String& name, bool abstract,
const boost::shared_ptr<Expression>& exprl,
const boost::shared_ptr<Expression>& filter,
bool defaultTmpl, bool ignoreOnError, const DebugInfo& debuginfo,
const Dictionary::Ptr& scope, const String& zone,
const String& package);
String GetType(void) const;
Type::Ptr GetType(void) const;
String GetName(void) const;
bool IsAbstract(void) const;
bool IsDefaultTemplate(void) const;
@ -66,7 +66,7 @@ public:
ConfigObject::Ptr GetObject(void) const;
static ConfigItem::Ptr GetByTypeAndName(const String& type,
static ConfigItem::Ptr GetByTypeAndName(const Type::Ptr& type,
const String& name);
static bool CommitItems(const ActivationContext::Ptr& context, WorkQueue& upq, std::vector<ConfigItem::Ptr>& newItems, bool silent = false);
@ -74,13 +74,13 @@ public:
static bool RunWithActivationContext(const Function::Ptr& function);
static std::vector<ConfigItem::Ptr> GetItems(const String& type);
static std::vector<ConfigItem::Ptr> GetDefaultTemplates(const String& type);
static std::vector<ConfigItem::Ptr> GetItems(const Type::Ptr& type);
static std::vector<ConfigItem::Ptr> GetDefaultTemplates(const Type::Ptr& type);
static void RemoveIgnoredItems(const String& allowedConfigPath);
private:
String m_Type; /**< The object type. */
Type::Ptr m_Type; /**< The object type. */
String m_Name; /**< The name. */
bool m_Abstract; /**< Whether this is a template. */
@ -99,7 +99,7 @@ private:
static boost::mutex m_Mutex;
typedef std::map<String, ConfigItem::Ptr> ItemMap;
typedef std::map<String, ItemMap> TypeMap;
typedef std::map<Type::Ptr, ItemMap> TypeMap;
static TypeMap m_Items; /**< All registered configuration items. */
static TypeMap m_DefaultTemplates;

View File

@ -39,7 +39,7 @@ ConfigItemBuilder::ConfigItemBuilder(const DebugInfo& debugInfo)
m_DebugInfo = debugInfo;
}
void ConfigItemBuilder::SetType(const String& type)
void ConfigItemBuilder::SetType(const Type::Ptr& type)
{
m_Type = type;
}
@ -91,24 +91,23 @@ void ConfigItemBuilder::SetIgnoreOnError(bool ignoreOnError)
ConfigItem::Ptr ConfigItemBuilder::Compile(void)
{
if (m_Type.IsEmpty()) {
if (!m_Type) {
std::ostringstream msgbuf;
msgbuf << "The type name of an object may not be empty";
msgbuf << "The type of an object must be specified";
BOOST_THROW_EXCEPTION(ScriptError(msgbuf.str(), m_DebugInfo));
}
Type::Ptr ptype = Type::GetByName(m_Type);
ConfigType *ctype = dynamic_cast<ConfigType *>(ptype.get());
ConfigType *ctype = dynamic_cast<ConfigType *>(m_Type.get());
if (!ctype) {
std::ostringstream msgbuf;
msgbuf << "The type '" + m_Type + "' is unknown";
msgbuf << "The type '" + m_Type->GetName() + "' cannot be used for config objects";
BOOST_THROW_EXCEPTION(ScriptError(msgbuf.str(), m_DebugInfo));
}
if (m_Name.FindFirstOf("!") != String::NPos) {
std::ostringstream msgbuf;
msgbuf << "Name for object '" << m_Name << "' of type '" << m_Type << "' is invalid: Object names may not contain '!'";
msgbuf << "Name for object '" << m_Name << "' of type '" << m_Type->GetName() << "' is invalid: Object names may not contain '!'";
BOOST_THROW_EXCEPTION(ScriptError(msgbuf.str(), m_DebugInfo));
}

View File

@ -42,7 +42,7 @@ public:
ConfigItemBuilder(void);
explicit ConfigItemBuilder(const DebugInfo& debugInfo);
void SetType(const String& type);
void SetType(const Type::Ptr& type);
void SetName(const String& name);
void SetAbstract(bool abstract);
void SetScope(const Dictionary::Ptr& scope);
@ -57,7 +57,7 @@ public:
ConfigItem::Ptr Compile(void);
private:
String m_Type; /**< The object type. */
Type::Ptr m_Type; /**< The object type. */
String m_Name; /**< The name. */
bool m_Abstract; /**< Whether the item is abstract. */
std::vector<Expression *> m_Expressions; /**< Expressions for this item. */

View File

@ -745,7 +745,7 @@ ExpressionResult ImportExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhi
if (!name.IsString())
BOOST_THROW_EXCEPTION(ScriptError("Template/object name must be a string", m_DebugInfo));
ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(type, name);
ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(Type::GetByName(type), name);
if (!item)
BOOST_THROW_EXCEPTION(ScriptError("Import references unknown template: '" + name + "'", m_DebugInfo));
@ -767,8 +767,9 @@ ExpressionResult ImportDefaultTemplatesExpression::DoEvaluate(ScriptFrame& frame
BOOST_THROW_EXCEPTION(ScriptError("Imports are not allowed in sandbox mode.", m_DebugInfo));
String type = VMOps::GetField(frame.Self, "type", frame.Sandboxed, m_DebugInfo);
Type::Ptr ptype = Type::GetByName(type);
for (const ConfigItem::Ptr& item : ConfigItem::GetDefaultTemplates(type)) {
for (const ConfigItem::Ptr& item : ConfigItem::GetDefaultTemplates(ptype)) {
Dictionary::Ptr scope = item->GetScope();
if (scope)
@ -803,6 +804,10 @@ ExpressionResult ObjectExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhi
if (frame.Sandboxed)
BOOST_THROW_EXCEPTION(ScriptError("Object definitions are not allowed in sandbox mode.", m_DebugInfo));
ExpressionResult typeres = m_Type->Evaluate(frame, dhint);
CHECK_RESULT(typeres);
Type::Ptr type = typeres.GetValue();
String name;
if (m_Name) {
@ -812,7 +817,7 @@ ExpressionResult ObjectExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhi
name = nameres.GetValue();
}
return VMOps::NewObject(frame, m_Abstract, m_Type, name, m_Filter, m_Zone,
return VMOps::NewObject(frame, m_Abstract, type, name, m_Filter, m_Zone,
m_Package, m_DefaultTmpl, m_IgnoreOnError, m_ClosedVars, m_Expression, m_DebugInfo);
}

View File

@ -888,7 +888,7 @@ private:
class I2_CONFIG_API ObjectExpression : public DebuggableExpression
{
public:
ObjectExpression(bool abstract, const String& type, Expression *name, Expression *filter,
ObjectExpression(bool abstract, Expression *type, Expression *name, Expression *filter,
const String& zone, const String& package, std::map<String, Expression *> *closedVars,
bool defaultTmpl, bool ignoreOnError, Expression *expression, const DebugInfo& debugInfo = DebugInfo())
: DebuggableExpression(debugInfo), m_Abstract(abstract), m_Type(type),
@ -915,7 +915,7 @@ protected:
private:
bool m_Abstract;
String m_Type;
Expression *m_Type;
Expression *m_Name;
boost::shared_ptr<Expression> m_Filter;
String m_Zone;

View File

@ -126,7 +126,7 @@ public:
return Empty;
}
static inline Value NewObject(ScriptFrame& frame, bool abstract, const String& type, const String& name, const boost::shared_ptr<Expression>& filter,
static inline Value NewObject(ScriptFrame& frame, bool abstract, const Type::Ptr& type, const String& name, const boost::shared_ptr<Expression>& filter,
const String& zone, const String& package, bool defaultTmpl, bool ignoreOnError, std::map<String, Expression *> *closedVars, const boost::shared_ptr<Expression>& expression, const DebugInfo& debugInfo = DebugInfo())
{
ConfigItemBuilder::Ptr item = new ConfigItemBuilder(debugInfo);
@ -134,9 +134,7 @@ public:
String checkName = name;
if (!abstract) {
Type::Ptr ptype = Type::GetByName(type);
NameComposer *nc = dynamic_cast<NameComposer *>(ptype.get());
NameComposer *nc = dynamic_cast<NameComposer *>(type.get());
if (nc)
checkName = nc->MakeName(name, Dictionary::Ptr());
@ -147,11 +145,17 @@ public:
if (oldItem) {
std::ostringstream msgbuf;
msgbuf << "Object '" << name << "' of type '" << type << "' re-defined: " << debugInfo << "; previous definition: " << oldItem->GetDebugInfo();
msgbuf << "Object '" << name << "' of type '" << type->GetName() << "' re-defined: " << debugInfo << "; previous definition: " << oldItem->GetDebugInfo();
BOOST_THROW_EXCEPTION(ScriptError(msgbuf.str(), debugInfo));
}
}
if (filter && !ObjectRule::IsValidSourceType(type->GetName())) {
std::ostringstream msgbuf;
msgbuf << "Object '" << name << "' of type '" << type->GetName() << "' must not have 'assign where' and 'ignore where' rules: " << debugInfo;
BOOST_THROW_EXCEPTION(ScriptError(msgbuf.str(), debugInfo));
}
item->SetType(type);
item->SetName(name);

View File

@ -50,7 +50,7 @@ bool Dependency::EvaluateApplyRuleInstance(const Checkable::Ptr& checkable, cons
#endif /* _DEBUG */
ConfigItemBuilder::Ptr builder = new ConfigItemBuilder(di);
builder->SetType("Dependency");
builder->SetType(Dependency::TypeInstance);
builder->SetName(name);
builder->SetScope(frame.Locals->ShallowClone());
builder->SetIgnoreOnError(rule.GetIgnoreOnError());

View File

@ -62,7 +62,7 @@ void HostGroup::EvaluateObjectRules(const Host::Ptr& host)
{
CONTEXT("Evaluating group memberships for host '" + host->GetName() + "'");
for (const ConfigItem::Ptr& group : ConfigItem::GetItems("HostGroup"))
for (const ConfigItem::Ptr& group : ConfigItem::GetItems(HostGroup::TypeInstance))
{
if (!group->GetFilter())
continue;

View File

@ -50,7 +50,7 @@ bool Notification::EvaluateApplyRuleInstance(const Checkable::Ptr& checkable, co
#endif /* _DEBUG */
ConfigItemBuilder::Ptr builder = new ConfigItemBuilder(di);
builder->SetType("Notification");
builder->SetType(Notification::TypeInstance);
builder->SetName(name);
builder->SetScope(frame.Locals->ShallowClone());
builder->SetIgnoreOnError(rule.GetIgnoreOnError());

View File

@ -49,7 +49,7 @@ bool ScheduledDowntime::EvaluateApplyRuleInstance(const Checkable::Ptr& checkabl
#endif /* _DEBUG */
ConfigItemBuilder::Ptr builder = new ConfigItemBuilder(di);
builder->SetType("ScheduledDowntime");
builder->SetType(ScheduledDowntime::TypeInstance);
builder->SetName(name);
builder->SetScope(frame.Locals->ShallowClone());
builder->SetIgnoreOnError(rule.GetIgnoreOnError());

View File

@ -48,7 +48,7 @@ bool Service::EvaluateApplyRuleInstance(const Host::Ptr& host, const String& nam
#endif /* _DEBUG */
ConfigItemBuilder::Ptr builder = new ConfigItemBuilder(di);
builder->SetType("Service");
builder->SetType(Service::TypeInstance);
builder->SetName(name);
builder->SetScope(frame.Locals->ShallowClone());
builder->SetIgnoreOnError(rule.GetIgnoreOnError());

View File

@ -65,7 +65,7 @@ void ServiceGroup::EvaluateObjectRules(const Service::Ptr& service)
{
CONTEXT("Evaluating group membership for service '" + service->GetName() + "'");
for (const ConfigItem::Ptr& group : ConfigItem::GetItems("ServiceGroup"))
for (const ConfigItem::Ptr& group : ConfigItem::GetItems(ServiceGroup::TypeInstance))
{
if (!group->GetFilter())
continue;

View File

@ -62,7 +62,7 @@ void UserGroup::EvaluateObjectRules(const User::Ptr& user)
{
CONTEXT("Evaluating group membership for user '" + user->GetName() + "'");
for (const ConfigItem::Ptr& group : ConfigItem::GetItems("UserGroup"))
for (const ConfigItem::Ptr& group : ConfigItem::GetItems(UserGroup::TypeInstance))
{
if (!group->GetFilter())
continue;

View File

@ -195,7 +195,7 @@ bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bo
DeleteObjectHelper(parentObj, cascade, errors);
}
ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(type->GetName(), object->GetName());
ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(type, object->GetName());
try {
/* mark this object for cluster delete event */

View File

@ -40,7 +40,7 @@ public:
{
Dictionary::Ptr target = new Dictionary();
target->Set("name", item->GetName());
target->Set("type", item->GetType());
target->Set("type", item->GetType()->GetName());
DebugInfo di = item->GetDebugInfo();
Dictionary::Ptr dinfo = new Dictionary();
@ -57,7 +57,9 @@ public:
virtual void FindTargets(const String& type,
const boost::function<void (const Value&)>& addTarget) const override
{
for (const ConfigItem::Ptr& item : ConfigItem::GetItems(type)) {
Type::Ptr ptype = Type::GetByName(type);
for (const ConfigItem::Ptr& item : ConfigItem::GetItems(ptype)) {
if (item->IsAbstract())
addTarget(GetTargetForTemplate(item));
}
@ -65,7 +67,9 @@ public:
virtual Value GetTargetByName(const String& type, const String& name) const override
{
ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(type, name);
Type::Ptr ptype = Type::GetByName(type);
ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(ptype, name);
if (!item || !item->IsAbstract())
BOOST_THROW_EXCEPTION(std::invalid_argument("Template does not exist."));