Implement validation for "repository add"

fixes #7458
This commit is contained in:
Gunnar Beutner 2014-10-28 11:54:56 +01:00
parent 00652f603c
commit 3dc2f82345
9 changed files with 118 additions and 62 deletions

View File

@ -175,13 +175,6 @@ int RepositoryObjectCommand::Run(const boost::program_options::variables_map& vm
String name = attrs->Get("name");
if (m_Type == "Service") {
if (!attrs->Contains("host_name")) {
Log(LogCritical, "cli", "Service objects require the 'host_name' attribute.");
return 1;
}
}
if (vm.count("import")) {
Array::Ptr imports = make_shared<Array>();
@ -196,13 +189,12 @@ int RepositoryObjectCommand::Run(const boost::program_options::variables_map& vm
if (m_Command == RepositoryCommandAdd) {
Utility::LoadExtensionLibrary("icinga");
RepositoryUtility::AddObject(name, m_Type, attrs);
}
else if (m_Command == RepositoryCommandRemove) {
} else if (m_Command == RepositoryCommandRemove) {
/* pass attrs for service->host_name requirement */
RepositoryUtility::RemoveObject(name, m_Type, attrs);
}
else if (m_Command == RepositoryCommandSet) {
} else if (m_Command == RepositoryCommandSet) {
Log(LogWarning, "cli")
<< "Not supported yet. Please check the roadmap at https://dev.icinga.org\n";
return 1;

View File

@ -19,6 +19,9 @@
#include "cli/repositoryutility.hpp"
#include "cli/clicommand.hpp"
#include "config/configtype.hpp"
#include "config/configcompilercontext.hpp"
#include "config/configcompiler.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include "base/convert.hpp"
@ -177,6 +180,15 @@ void RepositoryUtility::PrintChangeLog(std::ostream& fp)
}
}
class RepositoryTypeRuleUtilities : public TypeRuleUtilities
{
public:
virtual bool ValidateName(const String& type, const String& name, String *hint) const
{
return true;
}
};
/* modify objects and write changelog */
bool RepositoryUtility::AddObject(const String& name, const String& type, const Dictionary::Ptr& attrs)
{
@ -191,6 +203,45 @@ bool RepositoryUtility::AddObject(const String& name, const String& type, const
change->Set("command", "add");
change->Set("attrs", attrs);
ConfigCompilerContext::GetInstance()->Reset();
String fname, fragment;
BOOST_FOREACH(boost::tie(fname, fragment), ConfigFragmentRegistry::GetInstance()->GetItems()) {
ConfigCompiler::CompileText(fname, fragment);
}
ConfigType::Ptr ctype = ConfigType::GetByName(type);
if (!ctype)
Log(LogCritical, "cli")
<< "No validation type available for '" << type << "'.";
else {
Dictionary::Ptr vattrs = attrs->ShallowClone();
vattrs->Set("__name", vattrs->Get("name"));
vattrs->Remove("name");
vattrs->Set("type", type);
RepositoryTypeRuleUtilities utils;
ctype->ValidateItem(name, vattrs, DebugInfo(), &utils);
int warnings = 0, errors = 0;
BOOST_FOREACH(const ConfigCompilerMessage& message, ConfigCompilerContext::GetInstance()->GetMessages()) {
String logmsg = String("Config ") + (message.Error ? "error" : "warning") + ": " + message.Text;
if (message.Error) {
Log(LogCritical, "config", logmsg);
errors++;
} else {
Log(LogWarning, "config", logmsg);
warnings++;
}
}
if (errors > 0)
return false;
}
return WriteObjectToRepositoryChangeLog(path, change);
}

View File

@ -270,8 +270,10 @@ void ConfigItem::ValidateItem(void)
return;
}
TypeRuleUtilities utils;
try {
ctype->ValidateItem(GetSelf());
ctype->ValidateItem(GetName(), GetProperties(), GetDebugInfo(), &utils);
} catch (const ConfigError& ex) {
const DebugInfo *di = boost::get_error_info<errinfo_debuginfo>(ex);
ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), di ? *di : DebugInfo());

View File

@ -74,33 +74,21 @@ void ConfigType::AddParentRules(std::vector<TypeRuleList::Ptr>& ruleLists, const
}
}
void ConfigType::ValidateItem(const ConfigItem::Ptr& item)
void ConfigType::ValidateItem(const String& name, const Dictionary::Ptr& attrs, const DebugInfo& debugInfo, const TypeRuleUtilities *utils)
{
/* Don't validate abstract items. */
if (item->IsAbstract())
return;
Dictionary::Ptr attrs;
DebugInfo debugInfo;
String type, name;
{
ObjectLock olock(item);
attrs = item->GetProperties();
debugInfo = item->GetDebugInfo();
type = item->GetType();
name = item->GetName();
}
String location = "Object '" + name + "' (Type: '" + GetName() + "')";
if (!debugInfo.Path.IsEmpty())
location += " at " + debugInfo.Path + ":" + Convert::ToString(debugInfo.FirstLine);
std::vector<String> locations;
locations.push_back("Object '" + name + "' (Type: '" + type + "') at " + debugInfo.Path + ":" + Convert::ToString(debugInfo.FirstLine));
locations.push_back(location);
std::vector<TypeRuleList::Ptr> ruleLists;
AddParentRules(ruleLists, GetSelf());
ruleLists.push_back(m_RuleList);
ValidateDictionary(attrs, ruleLists, locations);
ValidateDictionary(attrs, ruleLists, locations, utils);
}
String ConfigType::LocationToString(const std::vector<String>& locations)
@ -120,7 +108,8 @@ String ConfigType::LocationToString(const std::vector<String>& locations)
}
void ConfigType::ValidateDictionary(const Dictionary::Ptr& dictionary,
const std::vector<TypeRuleList::Ptr>& ruleLists, std::vector<String>& locations)
const std::vector<TypeRuleList::Ptr>& ruleLists, std::vector<String>& locations,
const TypeRuleUtilities *utils)
{
BOOST_FOREACH(const TypeRuleList::Ptr& ruleList, ruleLists) {
BOOST_FOREACH(const String& require, ruleList->GetRequires()) {
@ -163,7 +152,7 @@ void ConfigType::ValidateDictionary(const Dictionary::Ptr& dictionary,
BOOST_FOREACH(const TypeRuleList::Ptr& ruleList, ruleLists) {
TypeRuleList::Ptr subRuleList;
TypeValidationResult result = ruleList->ValidateAttribute(kv.first, kv.second, &subRuleList, &hint);
TypeValidationResult result = ruleList->ValidateAttribute(kv.first, kv.second, &subRuleList, &hint, utils);
if (subRuleList)
subRuleLists.push_back(subRuleList);
@ -192,16 +181,17 @@ void ConfigType::ValidateDictionary(const Dictionary::Ptr& dictionary,
}
if (!subRuleLists.empty() && kv.second.IsObjectType<Dictionary>())
ValidateDictionary(kv.second, subRuleLists, locations);
ValidateDictionary(kv.second, subRuleLists, locations, utils);
else if (!subRuleLists.empty() && kv.second.IsObjectType<Array>())
ValidateArray(kv.second, subRuleLists, locations);
ValidateArray(kv.second, subRuleLists, locations, utils);
locations.pop_back();
}
}
void ConfigType::ValidateArray(const Array::Ptr& array,
const std::vector<TypeRuleList::Ptr>& ruleLists, std::vector<String>& locations)
const std::vector<TypeRuleList::Ptr>& ruleLists, std::vector<String>& locations,
const TypeRuleUtilities *utils)
{
BOOST_FOREACH(const TypeRuleList::Ptr& ruleList, ruleLists) {
BOOST_FOREACH(const String& require, ruleList->GetRequires()) {
@ -249,7 +239,7 @@ void ConfigType::ValidateArray(const Array::Ptr& array,
BOOST_FOREACH(const TypeRuleList::Ptr& ruleList, ruleLists) {
TypeRuleList::Ptr subRuleList;
TypeValidationResult result = ruleList->ValidateAttribute(key, value, &subRuleList, &hint);
TypeValidationResult result = ruleList->ValidateAttribute(key, value, &subRuleList, &hint, utils);
if (subRuleList)
subRuleLists.push_back(subRuleList);
@ -267,7 +257,7 @@ void ConfigType::ValidateArray(const Array::Ptr& array,
}
if (overallResult == ValidationUnknownField)
ConfigCompilerContext::GetInstance()->AddMessage(false, "Unknown attribute: " + LocationToString(locations));
ConfigCompilerContext::GetInstance()->AddMessage(true, "Unknown attribute: " + LocationToString(locations));
else if (overallResult == ValidationInvalidType) {
String message = "Invalid value for array index: " + LocationToString(locations);
@ -278,9 +268,9 @@ void ConfigType::ValidateArray(const Array::Ptr& array,
}
if (!subRuleLists.empty() && value.IsObjectType<Dictionary>())
ValidateDictionary(value, subRuleLists, locations);
ValidateDictionary(value, subRuleLists, locations, utils);
else if (!subRuleLists.empty() && value.IsObjectType<Array>())
ValidateArray(value, subRuleLists, locations);
ValidateArray(value, subRuleLists, locations, utils);
locations.pop_back();
}
@ -310,4 +300,3 @@ ConfigTypeRegistry *ConfigTypeRegistry::GetInstance(void)
{
return Singleton<ConfigTypeRegistry>::GetInstance();
}

View File

@ -50,7 +50,8 @@ public:
DebugInfo GetDebugInfo(void) const;
void ValidateItem(const ConfigItem::Ptr& object);
void ValidateItem(const String& name, const Dictionary::Ptr& attrs,
const DebugInfo& debugInfo, const TypeRuleUtilities *utils);
void Register(void);
static ConfigType::Ptr GetByName(const String& name);
@ -65,9 +66,11 @@ private:
DebugInfo m_DebugInfo; /**< Debug information. */
static void ValidateDictionary(const Dictionary::Ptr& dictionary,
const std::vector<TypeRuleList::Ptr>& ruleLists, std::vector<String>& locations);
const std::vector<TypeRuleList::Ptr>& ruleLists, std::vector<String>& locations,
const TypeRuleUtilities *utils);
static void ValidateArray(const Array::Ptr& array,
const std::vector<TypeRuleList::Ptr>& ruleLists, std::vector<String>& locations);
const std::vector<TypeRuleList::Ptr>& ruleLists, std::vector<String>& locations,
const TypeRuleUtilities *utils);
static String LocationToString(const std::vector<String>& locations);

View File

@ -42,7 +42,7 @@ bool TypeRule::MatchName(const String& name) const
return (Utility::Match(m_NamePattern, name));
}
bool TypeRule::MatchValue(const Value& value, String *hint) const
bool TypeRule::MatchValue(const Value& value, String *hint, const TypeRuleUtilities *utils) const
{
ConfigItem::Ptr item;
@ -77,21 +77,26 @@ bool TypeRule::MatchValue(const Value& value, String *hint) const
if (!value.IsScalar())
return false;
item = ConfigItem::GetObject(m_NameType, value);
if (!item) {
*hint = "Object '" + value + "' of type '" + m_NameType + "' does not exist.";
return false;
}
if (item->IsAbstract()) {
*hint = "Object '" + value + "' of type '" + m_NameType + "' must not be a template.";
return false;
}
return true;
return utils->ValidateName(m_NameType, value, hint);
default:
return false;
}
}
bool TypeRuleUtilities::ValidateName(const String& type, const String& name, String *hint) const
{
ConfigItem::Ptr item = ConfigItem::GetObject(type, name);
if (!item) {
*hint = "Object '" + name + "' of type '" + type + "' does not exist.";
return false;
}
if (item->IsAbstract()) {
*hint = "Object '" + name + "' of type '" + type + "' must not be a template.";
return false;
}
return true;
}

View File

@ -27,6 +27,17 @@
namespace icinga
{
/**
* Utilities for type rules.
*
* @ingroup config
*/
class TypeRuleUtilities
{
public:
virtual bool ValidateName(const String& type, const String& name, String *hint) const;
};
/**
* The allowed type for a type rule.
*
@ -58,7 +69,7 @@ public:
TypeRuleList::Ptr GetSubRules(void) const;
bool MatchName(const String& name) const;
bool MatchValue(const Value& value, String *hint) const;
bool MatchValue(const Value& value, String *hint, const TypeRuleUtilities *utils) const;
private:
TypeSpecifier m_Type;

View File

@ -117,7 +117,8 @@ size_t TypeRuleList::GetLength(void) const
* @returns The validation result.
*/
TypeValidationResult TypeRuleList::ValidateAttribute(const String& name,
const Value& value, TypeRuleList::Ptr *subRules, String *hint) const
const Value& value, TypeRuleList::Ptr *subRules, String *hint,
const TypeRuleUtilities *utils) const
{
bool foundField = false;
BOOST_FOREACH(const TypeRule& rule, m_Rules) {
@ -126,7 +127,7 @@ TypeValidationResult TypeRuleList::ValidateAttribute(const String& name,
foundField = true;
if (rule.MatchValue(value, hint)) {
if (rule.MatchValue(value, hint, utils)) {
*subRules = rule.GetSubRules();
return ValidationOK;
}

View File

@ -28,6 +28,7 @@ namespace icinga
{
struct TypeRule;
class TypeRuleUtilities;
/**
* @ingroup config
@ -59,7 +60,8 @@ public:
void AddRule(const TypeRule& rule);
void AddRules(const TypeRuleList::Ptr& ruleList);
TypeValidationResult ValidateAttribute(const String& name, const Value& value, TypeRuleList::Ptr *subRules, String *hint) const;
TypeValidationResult ValidateAttribute(const String& name, const Value& value,
TypeRuleList::Ptr *subRules, String *hint, const TypeRuleUtilities *utils) const;
size_t GetLength(void) const;