Module: check_mk
Branch: master
Commit: 58b4cc18797a348feea27031900b30bf736ad2bc
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=58b4cc18797a34…
Author: Mathias Kettner <mk(a)mathias-kettner.de>
Date: Wed Jun 29 14:42:30 2016 +0200
CascadingDropdown: allow list as an alternative encoding over tuple
This is neccessary when working with backends that do no preserve
tuples since they use JSON as an intermediate encoding
---
web/htdocs/valuespec.py | 23 ++++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/web/htdocs/valuespec.py b/web/htdocs/valuespec.py
index 22063ed..a6929ef 100644
--- a/web/htdocs/valuespec.py
+++ b/web/htdocs/valuespec.py
@@ -1370,6 +1370,11 @@ class CascadingDropdown(ValueSpec):
self._separator = kwargs.get("separator", ", ")
self._sorted = kwargs.get("sorted", True)
self._orientation = kwargs.get("orientation", "vertical") # or horizontal
+ if kwargs.get("encoding", "tuple") == "list":
+ self._encoding_type = list
+ else:
+ self._encoding_type = tuple
+
def normalize_choices(self, choices):
new_choices = []
@@ -1388,7 +1393,7 @@ class CascadingDropdown(ValueSpec):
def canonical_value(self):
choices = self.choices()
if choices[0][2]:
- return (choices[0][0], choices[0][2].canonical_value())
+ return self._encoding_type((choices[0][0], choices[0][2].canonical_value()))
else:
return choices[0][0]
@@ -1398,7 +1403,7 @@ class CascadingDropdown(ValueSpec):
except:
choices = self.choices()
if choices[0][2]:
- return (choices[0][0], choices[0][2].default_value())
+ return self._encoding_type((choices[0][0], choices[0][2].default_value()))
else:
return choices[0][0]
@@ -1414,7 +1419,7 @@ class CascadingDropdown(ValueSpec):
# selection, if the HTML variable varprefix_sel aleady
# exists.
if value == val or (
- type(value) == tuple and value[0] == val):
+ type(value) == self._encoding_type and value[0] == val):
def_val = str(nr)
vp = varprefix + "_sel"
@@ -1449,7 +1454,7 @@ class CascadingDropdown(ValueSpec):
disp = "none"
else: # form painted the first time
if value == val \
- or (type(value) == tuple and value[0] == val):
+ or (type(value) == self._encoding_type and value[0] == val):
def_val_2 = value[1]
disp = ""
else:
@@ -1480,17 +1485,17 @@ class CascadingDropdown(ValueSpec):
sel = 0
val, title, vs = choices[sel]
if vs:
- val = (val, vs.from_html_vars(varprefix + "_%d" % sel))
+ val = self._encoding_type((val, vs.from_html_vars(varprefix + "_%d" % sel)))
return val
def validate_datatype(self, value, varprefix):
choices = self.choices()
for nr, (val, title, vs) in enumerate(choices):
if value == val or (
- type(value) == tuple and value[0] == val):
+ type(value) == self._encoding_type and value[0] == val):
if vs:
- if type(value) != tuple or len(value) != 2:
- raise MKUserError(varprefix + "_sel", _("Value must a tuple with two elements."))
+ if type(value) != self._encoding_type or len(value) != 2:
+ raise MKUserError(varprefix + "_sel", _("Value must a %s with two elements."), self._encoding_type.__name__)
vs.validate_datatype(value[1], varprefix + "_%d" % nr)
return
raise MKUserError(varprefix, _("Value %r is not allowed here.") % value)
@@ -1499,7 +1504,7 @@ class CascadingDropdown(ValueSpec):
choices = self.choices()
for nr, (val, title, vs) in enumerate(choices):
if value == val or (
- type(value) == tuple and value[0] == val):
+ type(value) == self._encoding_type and value[0] == val):
if vs:
vs.validate_value(value[1], varprefix + "_%d" % nr)
ValueSpec.custom_validate(self, value, varprefix)
Module: check_mk
Branch: master
Commit: 39125ad0fbcb65351ca348953b049f54c34d1926
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=39125ad0fbcb65…
Author: Mathias Kettner <mk(a)mathias-kettner.de>
Date: Wed Jun 29 12:30:39 2016 +0200
Updated bug entries #2497
---
.bugs/2497 | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/.bugs/2497 b/.bugs/2497
new file mode 100644
index 0000000..dc39b93
--- /dev/null
+++ b/.bugs/2497
@@ -0,0 +1,13 @@
+Title: Multisite cronjobs have config.user_id not set correcty
+Component: multisite
+State: open
+Date: 2016-06-29 12:29:30
+Targetversion: future
+Class: bug
+
+This value should be set to None (and maybe a buch of other user related
+settings in config). There should *no* user be logged in, but all permissions
+be active.
+
+Currently there seems to the randomly the user from the last login that
+that Apache process has made.
Module: check_mk
Branch: master
Commit: 737c0e8432d825c9aeaaa393c6153d878eb29b78
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=737c0e8432d825…
Author: Simon Betz <si(a)mathias-kettner.de>
Date: Wed Jun 29 10:32:11 2016 +0200
3687 Hostname translation for piggybacked host: now it's possible to enter any number of regular expressions within this ruleset
Please note: You can add any number of expressions which are executed succesively until the first match.
---
.werks/3687 | 9 ++++++
ChangeLog | 1 +
modules/check_mk_base.py | 11 ++++++--
web/htdocs/wato.py | 68 ++++++++++++++++++++++++++++------------------
4 files changed, 60 insertions(+), 29 deletions(-)
diff --git a/.werks/3687 b/.werks/3687
new file mode 100644
index 0000000..aa68fd2
--- /dev/null
+++ b/.werks/3687
@@ -0,0 +1,9 @@
+Title: Hostname translation for piggybacked host: now it's possible to enter any number of regular expressions within this ruleset
+Level: 1
+Component: wato
+Compatible: compat
+Version: 1.4.0i1
+Date: 1467188546
+Class: feature
+
+Please note: You can add any number of expressions which are executed succesively until the first match.
diff --git a/ChangeLog b/ChangeLog
index 97169e1..7c85fff 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -383,6 +383,7 @@
* 3538 Now hiding the activate changes button as long as another activation process is running
* 3618 Better layout for role & permission matrix, also allow sorting and searching
* 3544 The edit host and diagnostic page are now able to handle SNMPv3 credentials...
+ * 3687 Hostname translation for piggybacked host: now it's possible to enter any number of regular expressions within this ruleset...
* 3657 Custom host attributes can now be configured...
* 3060 FIX: Folder properties: Fixed exception when a user has no alias set...
* 3062 FIX: Git integration: Fixed not adding files in WATO folders to git control
diff --git a/modules/check_mk_base.py b/modules/check_mk_base.py
index 180767d..a7ba470 100644
--- a/modules/check_mk_base.py
+++ b/modules/check_mk_base.py
@@ -643,9 +643,13 @@ def do_hostname_translation(translation, hostname):
# We assume the incoming name is correctly encoded in UTF-8
hostname = decode_incoming_string(hostname)
- # 3. Regular expression conversion
- if "regex" in translation:
- expr, subst = translation.get("regex")
+ # 3. Multiple regular expression conversion
+ if type(translation.get("regex")) == tuple:
+ translations = [translation.get("regex")]
+ else:
+ translations = translation.get("regex", [])
+
+ for expr, subst in translations:
if not expr.endswith('$'):
expr += '$'
rcomp = regex(expr)
@@ -655,6 +659,7 @@ def do_hostname_translation(translation, hostname):
hostname = subst
for nr, text in enumerate(mo.groups("")):
hostname = hostname.replace("\\%d" % (nr+1), text)
+ break
# 4. Explicity mapping
for from_host, to_host in translation.get("mapping", []):
diff --git a/web/htdocs/wato.py b/web/htdocs/wato.py
index 2059d88..8ca319c 100644
--- a/web/htdocs/wato.py
+++ b/web/htdocs/wato.py
@@ -13719,29 +13719,40 @@ def HostnameTranslation(**kwargs):
totext = _("Drop domain part (<tt>host123.foobar.de</tt> → <tt>host123</tt>)"),
)),
( "regex",
- Tuple(
- title = _("Regular expression substitution"),
- help = _("Please specify a regular expression in the first field. This expression should at "
- "least contain one subexpression exclosed in brackets - for example <tt>vm_(.*)_prod</tt>. "
- "In the second field you specify the translated host name and can refer to the first matched "
- "group with <tt>\\1</tt>, the second with <tt>\\2</tt> and so on, for example <tt>\\1.example.org</tt>"),
- elements = [
- RegExpUnicode(
- title = _("Regular expression"),
- help = _("Must contain at least one subgroup <tt>(...)</tt>"),
- mingroups = 0,
- maxgroups = 9,
- size = 30,
- allow_empty = False,
- ),
- TextUnicode(
- title = _("Replacement"),
- help = _("Use <tt>\\1</tt>, <tt>\\2</tt> etc. to replace matched subgroups"),
- size = 30,
- allow_empty = False,
- )
- ]
- )),
+ Transform(
+ ListOf(
+ Tuple(
+ orientation = "horizontal",
+ elements = [
+ RegExpUnicode(
+ title = _("Regular expression"),
+ help = _("Must contain at least one subgroup <tt>(...)</tt>"),
+ mingroups = 0,
+ maxgroups = 9,
+ size = 30,
+ allow_empty = False,
+ ),
+ TextUnicode(
+ title = _("Replacement"),
+ help = _("Use <tt>\\1</tt>, <tt>\\2</tt> etc. to replace matched subgroups"),
+ size = 30,
+ allow_empty = False,
+ )
+ ],
+ ),
+ title = _("Multiple regular expressions"),
+ help = _("You can add any number of expressions here which are executed succesively until the first match. "
+ "Please specify a regular expression in the first field. This expression should at "
+ "least contain one subexpression exclosed in brackets - for example <tt>vm_(.*)_prod</tt>. "
+ "In the second field you specify the translated host name and can refer to the first matched "
+ "group with <tt>\\1</tt>, the second with <tt>\\2</tt> and so on, for example <tt>\\1.example.org</tt>. "
+ ""),
+ add_label = _("Add expression"),
+ movable = False,
+ ),
+ forth = lambda x: type(x) == tuple and [x] or x,
+ )
+ ),
( "mapping",
ListOf(
Tuple(
@@ -13788,9 +13799,13 @@ def do_hostname_translation(translation, hostname):
if translation.get("drop_domain") and not hostname[0].isdigit():
hostname = hostname.split(".", 1)[0]
- # 3. Regular expression conversion
- if "regex" in translation:
- expr, subst = translation.get("regex")
+ # 3. Multiple regular expression conversion
+ if type(translation.get("regex")) == tuple:
+ translations = [translation.get("regex")]
+ else:
+ translations = translation.get("regex", [])
+
+ for expr, subst in translations:
if not expr.endswith('$'):
expr += '$'
rcomp = regex(expr)
@@ -13800,6 +13815,7 @@ def do_hostname_translation(translation, hostname):
hostname = subst
for nr, text in enumerate(mo.groups("")):
hostname = hostname.replace("\\%d" % (nr+1), text)
+ break
# 4. Explicit mapping
for from_host, to_host in translation.get("mapping", []):
Module: check_mk
Branch: master
Commit: b16015a540a6ec7571fdf98179e59cddad288e96
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=b16015a540a6ec…
Author: Lars Michelsen <lm(a)mathias-kettner.de>
Date: Wed Jun 29 10:30:52 2016 +0200
3657 Custom host attributes can now be configured
It is now possible to declare custom host attributes (just like for users). By default the
custom attributes are just fields in the configuration GUI which you can use to store
host related values in.
Each of the attributes can be configured to be exported as custom variable to the
monitoring core. In case you do this for a custom attribute, the variable will be available
in the monitoring GUI, in notifications and via raw livestatus for your scripts or 3rd party
tools.
If you create a custom variable "my_custom_variable", it will result in a custom variable
"_MY_CUSTOM_VARIABLE" in the monitoring core.
---
.werks/3657 | 19 +++++++
ChangeLog | 1 +
web/htdocs/userdb.py | 15 +++---
web/htdocs/wato.py | 122 ++++++++++++++++++++++++++++++++++----------
web/htdocs/watolib.py | 18 +++++++
web/plugins/config/wato.py | 1 +
6 files changed, 142 insertions(+), 34 deletions(-)
Diff: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commitdiff;h=b16015a540…
Module: check_mk
Branch: master
Commit: 734a4992ed1442a5d5a92bf42aa618bbd9939228
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=734a4992ed1442…
Author: Sebastian Herbord <sh(a)mathias-kettner.de>
Date: Tue Jun 28 11:11:45 2016 +0200
3148 FIX fixed Edit View/Dashlet Dialogs offering non-sensical filter choices
This is actually two separate but similar bugfixes:
When editing a dashlet that represented a view, the search/context filter offered all possible
filter choices even when they made no sense for the data visualised.
Also, when editing a aggregation group view (and potentially other views), the filter list
offered the same filter option (aggregation group) twice, with different semantics, causing
exceptions if either one of these options was used.
---
.werks/3148 | 15 +++++++++++
ChangeLog | 1 +
web/htdocs/dashboard.py | 10 +++++--
web/htdocs/visuals.py | 69 ++++++++++++++++++++++++-----------------------
4 files changed, 60 insertions(+), 35 deletions(-)
diff --git a/.werks/3148 b/.werks/3148
new file mode 100644
index 0000000..d23da64
--- /dev/null
+++ b/.werks/3148
@@ -0,0 +1,15 @@
+Title: fixed Edit View/Dashlet Dialogs offering non-sensical filter choices
+Level: 1
+Component: multisite
+Class: fix
+Compatible: compat
+State: unknown
+Version: 1.4.0i1
+Date: 1467104831
+
+This is actually two separate but similar bugfixes:
+When editing a dashlet that represented a view, the search/context filter offered all possible
+filter choices even when they made no sense for the data visualised.
+Also, when editing a aggregation group view (and potentially other views), the filter list
+offered the same filter option (aggregation group) twice, with different semantics, causing
+exceptions if either one of these options was used.
diff --git a/ChangeLog b/ChangeLog
index a757976..aec3261 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -368,6 +368,7 @@
* 3627 FIX: fixed double graphs of database size
* 3586 FIX: Fixed file locking issues (rare and random errors that settings file could not be loaded)
* 2239 FIX: Fixed exception in WATO snapins Folders, Tree of folders and Virtual Host Tree...
+ * 3148 FIX: fixed Edit View/Dashlet Dialogs offering non-sensical filter choices...
WATO:
* 3244 WATO BI Module: swap order of aggregation function and child node selection...
diff --git a/web/htdocs/dashboard.py b/web/htdocs/dashboard.py
index 06f3fec..76a5628 100644
--- a/web/htdocs/dashboard.py
+++ b/web/htdocs/dashboard.py
@@ -1055,8 +1055,14 @@ def page_edit_dashlet():
],
)
- context_specs = visuals.get_context_specs(dashlet,
- info_handler=lambda dashlet: dashlet_types[dashlet['type']].get('infos'))
+ def dashlet_info_handler(dashlet):
+ if dashlet['type'] == 'view':
+ import views
+ return views.get_view_infos(dashlet)
+ else:
+ return dashlet_types[dashlet['type']].get('infos')
+
+ context_specs = visuals.get_context_specs(dashlet, info_handler=dashlet_info_handler)
vs_type = None
params = dashlet_type.get('parameters')
diff --git a/web/htdocs/visuals.py b/web/htdocs/visuals.py
index 911b110..b21e795 100644
--- a/web/htdocs/visuals.py
+++ b/web/htdocs/visuals.py
@@ -497,43 +497,44 @@ def page_create_visual(what, info_keys, next_url = None):
def get_context_specs(visual, info_handler):
context_specs = []
info_keys = info_handler and info_handler(visual) or infos.keys()
- for info_key in info_keys:
- info = infos[info_key]
- if info_key in visual['single_infos']:
- params = info['single_spec']
- optional = True
- isopen = True
- vs = Dictionary(
- title = info['title'],
- # render = 'form',
- form_isopen = isopen,
- optional_keys = optional,
- elements = params,
- )
- else:
- filter_list = VisualFilterList([info_key], title=info['title'])
- filter_names = filter_list.filter_names()
+ single_info_keys = [key for key in info_keys if key in visual['single_infos']]
+ multi_info_keys = [key for key in info_keys if key not in single_info_keys]
+
+ def visual_spec_single(info_key):
+ info = infos[info_key]
+ params = info['single_spec']
+ optional = True
+ isopen = True
+ return Dictionary(
+ title = info['title'],
+ # render = 'form',
+ form_isopen = isopen,
+ optional_keys = optional,
+ elements = params,
+ )
+
+ def visual_spec_multi(info_key):
+ info = infos[info_key]
+ filter_list = VisualFilterList([info_key], title=info['title'], ignore=set(single_info_keys))
+ filter_names = filter_list.filter_names()
- if not filter_names:
- continue # Skip infos which have no filters available
+ if not filter_names:
+ return [] # Skip infos which have no filters available
- params = [
- ('filters', filter_list),
- ]
- optional = None
- # Make it open by default when at least one filter is used
- isopen = bool([ fn for fn in visual.get('context', {}).keys()
- if fn in filter_names ])
- vs = filter_list
+ params = [
+ ('filters', filter_list),
+ ]
+ optional = None
+ # Make it open by default when at least one filter is used
+ isopen = bool([ fn for fn in visual.get('context', {}).keys()
+ if fn in filter_names ])
+ return filter_list
+ # single infos first, the rest afterwards
+ return [(info_key, visual_spec_single(info_key)) for info_key in single_info_keys] +\
+ [(info_key, visual_spec_multi(info_key)) for info_key in multi_info_keys]
- # Single info context specifications should be listed first
- if info_key in visual['single_infos']:
- context_specs.insert(0, (info_key, vs))
- else:
- context_specs.append((info_key, vs))
- return context_specs
def process_context_specs(context_specs):
context = {}
@@ -1116,13 +1117,15 @@ class VisualFilterList(ListOfMultiple):
def __init__(self, infos, **kwargs):
self._infos = infos
+ ignore = kwargs.get("ignore", set())
+
# First get all filters useful for the infos, then create VisualFilter
# valuespecs from them and then sort them
fspecs = {}
self._filters = {}
for info in self._infos:
for fname, filter in filters_allowed_for_info(info).items():
- if fname not in fspecs:
+ if fname not in fspecs and fname not in ignore:
fspecs[fname] = VisualFilter(fname,
title = filter.title,
)