Merge pull request #5264 from Icinga/feature/array-match

Implement new array match functionality

fixes #5264 
fixes #5263
This commit is contained in:
Gunnar Beutner 2017-05-16 14:22:35 +02:00 committed by GitHub
commit 7130e5e387
5 changed files with 223 additions and 17 deletions

View File

@ -16,7 +16,10 @@ Signature:
function regex(pattern, text)
Returns true if the regular expression matches the text, false otherwise.
Returns true if the regular expression matches the text, false otherwise. The mode argument is
optional and can be either MatchAll (in which case all elements for an array have to match) or MatchAny
(in which case at least one element has to match). The default mode is MatchAll.
**Tip**: In case you are looking for regular expression tests try [regex101](https://regex101.com).
Example:
@ -34,9 +37,11 @@ Example:
Signature:
function match(pattern, text)
function match(pattern, text, mode)
Returns true if the wildcard (`?*`) pattern matches the text, false otherwise.
Returns true if the wildcard (`?*`) pattern matches the text, false otherwise. The mode argument is
optional and can be either MatchAll (in which case all elements for an array have to match) or MatchAny
(in which case at least one element has to match). The default mode is MatchAll.
Example:
@ -59,7 +64,10 @@ Signature:
Returns true if the CIDR pattern matches the IP address, false otherwise.
IPv4 addresses are converted to IPv4-mapped IPv6 addresses before being
matched against the pattern.
matched against the pattern. The mode argument is optional and can be
either MatchAll (in which case all elements for an array have to match) or MatchAny
(in which case at least one element has to match). The default mode is MatchAll.
Example:
@ -1148,6 +1156,24 @@ Signature:
Returns a copy of the array containing only the elements for which `func(element)`
is true.
### <a id="array-any"></a> Array#any
Signature:
function any(func);
Returns true if the array contains at least one element for which `func(element)`
is true, false otherwise.
### <a id="array-all"></a> Array#all
Signature:
function all(func);
Returns true if the array contains only elements for which `func(element)`
is true, false otherwise.
### <a id="array-unique"></a> Array#unique
Signature:
@ -1220,6 +1246,14 @@ Signature:
Returns a list of keys for all items that are currently in the dictionary.
### <a id="dictionary-keys"></a> Dictionary#values
Signature:
function values();
Returns a list of values for all items that are currently in the dictionary.
## <a id="scriptfunction-type"></a> Function type
Inherits methods from the [Object type](18-library-reference.md#object-type).

View File

@ -207,6 +207,43 @@ static Array::Ptr ArrayFilter(const Function::Ptr& function)
return result;
}
static bool ArrayAny(const Function::Ptr& function)
{
ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
Array::Ptr self = static_cast<Array::Ptr>(vframe->Self);
if (vframe->Sandboxed && !function->IsSideEffectFree())
BOOST_THROW_EXCEPTION(ScriptError("Filter function must be side-effect free."));
ObjectLock olock(self);
for (const Value& item : self) {
std::vector<Value> args;
args.push_back(item);
if (function->Invoke(args))
return true;
}
return false;
}
static bool ArrayAll(const Function::Ptr& function)
{
ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
Array::Ptr self = static_cast<Array::Ptr>(vframe->Self);
if (vframe->Sandboxed && !function->IsSideEffectFree())
BOOST_THROW_EXCEPTION(ScriptError("Filter function must be side-effect free."));
ObjectLock olock(self);
for (const Value& item : self) {
std::vector<Value> args;
args.push_back(item);
if (!function->Invoke(args))
return false;
}
return true;
}
static Array::Ptr ArrayUnique(void)
{
ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
@ -242,6 +279,8 @@ Object::Ptr Array::GetPrototype(void)
prototype->Set("map", new Function("Array#map", WrapFunction(ArrayMap), { "func" }, true));
prototype->Set("reduce", new Function("Array#reduce", WrapFunction(ArrayReduce), { "reduce" }, true));
prototype->Set("filter", new Function("Array#filter", WrapFunction(ArrayFilter), { "func" }, true));
prototype->Set("any", new Function("Array#any", WrapFunction(ArrayAny), { "func" }, true));
prototype->Set("all", new Function("Array#all", WrapFunction(ArrayAll), { "func" }, true));
prototype->Set("unique", new Function("Array#unique", WrapFunction(ArrayUnique), {}, true));
}

View File

@ -79,6 +79,18 @@ static Array::Ptr DictionaryKeys(void)
return keys;
}
static Array::Ptr DictionaryValues(void)
{
ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
Dictionary::Ptr self = static_cast<Dictionary::Ptr>(vframe->Self);
Array::Ptr keys = new Array();
ObjectLock olock(self);
for (const Dictionary::Pair& kv : self) {
keys->Add(kv.second);
}
return keys;
}
Object::Ptr Dictionary::GetPrototype(void)
{
static Dictionary::Ptr prototype;
@ -92,6 +104,7 @@ Object::Ptr Dictionary::GetPrototype(void)
prototype->Set("contains", new Function("Dictionary#contains", WrapFunction(DictionaryContains), { "key" }, true));
prototype->Set("shallow_clone", new Function("Dictionary#shallow_clone", WrapFunction(DictionaryShallowClone), {}, true));
prototype->Set("keys", new Function("Dictionary#keys", WrapFunction(DictionaryKeys), {}, true));
prototype->Set("values", new Function("Dictionary#values", WrapFunction(DictionaryValues), {}, true));
}
return prototype;

View File

@ -27,6 +27,7 @@
#include "base/configtype.hpp"
#include "base/application.hpp"
#include "base/dependencygraph.hpp"
#include "base/initialize.hpp"
#include <boost/regex.hpp>
#include <algorithm>
#include <set>
@ -36,9 +37,9 @@
using namespace icinga;
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, regex, &ScriptUtils::Regex, "pattern:text");
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, match, &Utility::Match, "pattern:text");
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, cidr_match, &Utility::CidrMatch, "pattern:ip");
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, regex, &ScriptUtils::Regex, "pattern:text:mode");
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, match, &ScriptUtils::Match, "pattern:text:mode");
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, cidr_match, &ScriptUtils::CidrMatch, "pattern:ip:mode");
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, len, &ScriptUtils::Len, "value");
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, union, &ScriptUtils::Union, "");
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, intersection, &ScriptUtils::Intersection, "");
@ -67,6 +68,20 @@ REGISTER_SAFE_SCRIPTFUNCTION_NS(System, escape_create_process_arg, &Utility::Esc
REGISTER_SCRIPTFUNCTION_NS(System, ptr, &ScriptUtils::Ptr, "object");
REGISTER_SCRIPTFUNCTION_NS(System, sleep, &Utility::Sleep, "interval");
INITIALIZE_ONCE(&ScriptUtils::StaticInitialize);
enum MatchType
{
MatchAll,
MatchAny
};
void ScriptUtils::StaticInitialize(void)
{
ScriptGlobal::Set("MatchAll", MatchAll);
ScriptGlobal::Set("MatchAny", MatchAny);
}
String ScriptUtils::CastString(const Value& value)
{
return value;
@ -81,18 +96,120 @@ bool ScriptUtils::CastBool(const Value& value)
{
return value.ToBool();
}
bool ScriptUtils::Regex(const String& pattern, const String& text)
bool ScriptUtils::Regex(const std::vector<Value>& args)
{
bool res = false;
try {
boost::regex expr(pattern.GetData());
boost::smatch what;
res = boost::regex_search(text.GetData(), what, expr);
} catch (boost::exception&) {
res = false; /* exception means something went terribly wrong */
if (args.size() < 2)
BOOST_THROW_EXCEPTION(std::invalid_argument("Regular expression and text must be specified."));
Array::Ptr texts = new Array();
String pattern = args[0];
Value argTexts = args[1];
MatchType mode;
if (args.size() > 2)
mode = static_cast<MatchType>(static_cast<int>(args[2]));
else
mode = MatchAll;
if (argTexts.IsObjectType<Array>())
texts = argTexts;
else {
texts = new Array();
texts->Add(argTexts);
}
return res;
ObjectLock olock(texts);
for (const String& text : texts) {
bool res = false;
try {
boost::regex expr(pattern.GetData());
boost::smatch what;
res = boost::regex_search(text.GetData(), what, expr);
} catch (boost::exception&) {
res = false; /* exception means something went terribly wrong */
}
if (mode == MatchAny && res)
return true;
else if (mode == MatchAll && !res)
return false;
}
return mode == MatchAll;
}
bool ScriptUtils::Match(const std::vector<Value>& args)
{
if (args.size() < 2)
BOOST_THROW_EXCEPTION(std::invalid_argument("Pattern and text must be specified."));
Array::Ptr texts = new Array();
String pattern = args[0];
Value argTexts = args[1];
MatchType mode;
if (args.size() > 2)
mode = static_cast<MatchType>(static_cast<int>(args[2]));
else
mode = MatchAll;
if (argTexts.IsObjectType<Array>())
texts = argTexts;
else {
texts = new Array();
texts->Add(argTexts);
}
ObjectLock olock(texts);
for (const String& text : texts) {
bool res = Utility::Match(pattern, text);
if (mode == MatchAny && res)
return true;
else if (mode == MatchAll && !res)
return false;
}
return mode == MatchAll;
}
bool ScriptUtils::CidrMatch(const std::vector<Value>& args)
{
if (args.size() < 2)
BOOST_THROW_EXCEPTION(std::invalid_argument("CIDR and IP address must be specified."));
Array::Ptr ips = new Array();
String pattern = args[0];
Value argIps = args[1];
MatchType mode;
if (args.size() > 2)
mode = static_cast<MatchType>(static_cast<int>(args[2]));
else
mode = MatchAll;
if (argIps.IsObjectType<Array>())
ips = argIps;
else {
ips = new Array();
ips->Add(argIps);
}
ObjectLock olock(ips);
for (const String& ip : ips) {
bool res = Utility::CidrMatch(pattern, ip);
if (mode == MatchAny && res)
return true;
else if (mode == MatchAll && !res)
return false;
}
return mode == MatchAll;
}
double ScriptUtils::Len(const Value& value)

View File

@ -36,10 +36,13 @@ namespace icinga
class I2_BASE_API ScriptUtils
{
public:
static void StaticInitialize(void);
static String CastString(const Value& value);
static double CastNumber(const Value& value);
static bool CastBool(const Value& value);
static bool Regex(const String& pattern, const String& text);
static bool Regex(const std::vector<Value>& args);
static bool Match(const std::vector<Value>& args);
static bool CidrMatch(const std::vector<Value>& args);
static double Len(const Value& value);
static Array::Ptr Union(const std::vector<Value>& arguments);
static Array::Ptr Intersection(const std::vector<Value>& arguments);