Module: check_mk
Branch: master
Commit: 051d9b006faf0ef8589902c078f2755f94249925
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=051d9b006faf0e…
Author: Lars Michelsen <lm(a)mathias-kettner.de>
Date: Wed Jan 9 12:06:41 2013 +0100
userdb: changed login API to return the logged-in username on success; usernames are now case insensitiv on ldap login
---
web/htdocs/login.py | 7 ++++++-
web/htdocs/userdb.py | 13 +++++++------
web/htdocs/wato.py | 2 +-
web/plugins/userdb/htpasswd.py | 12 ++++++++----
web/plugins/userdb/ldap.py | 25 ++++++++++++++++---------
5 files changed, 38 insertions(+), 21 deletions(-)
diff --git a/web/htdocs/login.py b/web/htdocs/login.py
index f8d9ba7..d3ff0ce 100644
--- a/web/htdocs/login.py
+++ b/web/htdocs/login.py
@@ -161,7 +161,12 @@ def do_login():
if not origtarget or "logout.py" in origtarget:
origtarget = defaults.url_prefix + 'check_mk/'
- if userdb.hook_login(username, password):
+ # None -> User unknown, means continue with other connectors
+ # '<user_id>' -> success
+ # False -> failed
+ result = userdb.hook_login(username, password)
+ if result:
+ username = result
# The login succeeded! Now:
# a) Set the auth cookie
# b) Unset the login vars in further processing
diff --git a/web/htdocs/userdb.py b/web/htdocs/userdb.py
index 51ca6b6..843994c 100644
--- a/web/htdocs/userdb.py
+++ b/web/htdocs/userdb.py
@@ -467,10 +467,11 @@ def hook_login(username, password):
continue
result = handler(username, password)
- # None -> User unknown, means continue with other connectors
- # True -> success
- # False -> failed
- if result == True:
+ # None -> User unknown, means continue with other connectors
+ # '<user_id>' -> success
+ # False -> failed
+ if result not in [ False, None ]:
+ username = result
# Check wether or not the user exists (and maybe create it)
create_non_existing_user(connector['id'], username)
@@ -481,8 +482,8 @@ def hook_login(username, password):
# a "!". But when using other conectors it might be neccessary
# to validate the user "locked" attribute.
lock_handler = connector.get('locked', None)
- if lock_handler:
- result = not lock_handler(username) # returns True if locked
+ if lock_handler and lock_handler(username):
+ return False # The account is locked
return result
diff --git a/web/htdocs/wato.py b/web/htdocs/wato.py
index 334ea6d..943922a 100644
--- a/web/htdocs/wato.py
+++ b/web/htdocs/wato.py
@@ -7786,7 +7786,7 @@ def mode_users(phase):
if cgs:
html.write(", ".join(
[ '<a href="%s">%s</a>' % (make_link([("mode", "edit_contact_group"), ("edit", c)]),
- contact_groups[c] and contact_groups[c] or c) for c in cgs]))
+ c in contact_groups and contact_groups[c] or c) for c in cgs]))
else:
html.write("<i>" + _("none") + "</i>")
diff --git a/web/plugins/userdb/htpasswd.py b/web/plugins/userdb/htpasswd.py
index 2977b89..d1b386b 100644
--- a/web/plugins/userdb/htpasswd.py
+++ b/web/plugins/userdb/htpasswd.py
@@ -37,9 +37,9 @@
# to validate a login issued by a user.
# Gets parameters: username, password
# Has to return either:
-# True -> Login succeeded
-# False -> Login failed
-# None -> Unknown user
+# '<user_id>' -> Login succeeded
+# False -> Login failed
+# None -> Unknown user
# sync
# Optional: Hook function can be registered here to be executed
# to synchronize all users.
@@ -94,7 +94,11 @@ def htpasswd_login(username, password):
users = load_htpasswd()
if username not in users:
return None # not existing user, skip over
- return password_valid(users[username], password)
+
+ if password_valid(users[username], password):
+ return username
+ else:
+ return False
# Saves htpasswd connector managed users
def htpasswd_save(users):
diff --git a/web/plugins/userdb/ldap.py b/web/plugins/userdb/ldap.py
index 1f9775f..3c99af3 100644
--- a/web/plugins/userdb/ldap.py
+++ b/web/plugins/userdb/ldap.py
@@ -239,10 +239,10 @@ def ldap_replace_macros(tmpl):
def ldap_user_id_attr():
return config.ldap_userspec.get('user_id', ldap_attr('user_id'))
-def ldap_get_user_dn(username, no_escape = False):
+def ldap_get_user(username, no_escape = False):
# Check wether or not the user exists in the directory
# It's only ok when exactly one entry is found.
- # Returns the DN in this case.
+ # Returns the DN and user_id as tuple in this case.
result = ldap_search(
ldap_replace_macros(config.ldap_userspec['dn']),
'(%s=%s)' % (ldap_user_id_attr(), ldap.filter.escape_filter_chars(username)),
@@ -250,10 +250,12 @@ def ldap_get_user_dn(username, no_escape = False):
)
if result:
+ dn = result[0][0]
+ user_id = result[0][1][ldap_user_id_attr()][0]
if no_escape:
- return result[0][0]
+ return (dn, user_id)
else:
- return result[0][0].replace('\\', '\\\\')
+ return (dn.replace('\\', '\\\\'), user_id)
def ldap_get_users(add_filter = None):
columns = [
@@ -273,7 +275,9 @@ def ldap_get_users(add_filter = None):
return result
def ldap_user_groups(username, attr = 'cn'):
- user_dn = ldap_get_user_dn(username)
+ # The given username might be wrong case. The ldap search is case insensitive,
+ # so the username read from ldap might differ. Fix it here.
+ user_dn, username = ldap_get_user(username)
# Apply configured group ldap filter and only reply with groups
# having the current user as member
@@ -525,16 +529,19 @@ ldap_attribute_plugins['groups_to_roles'] = {
def ldap_login(username, password):
ldap_connect()
# Returns None when the user is not found or not uniq, else returns the
- # distinguished name of the user as string which is needed for the login.
- user_dn = ldap_get_user_dn(username, True)
- if not user_dn:
+ # distinguished name and the username as tuple which are both needed for
+ # the further login process.
+ result = ldap_get_user(username, True)
+ if not result:
return None # The user does not exist. Skip this connector.
+ user_dn, username = result
+
# Try to bind with the user provided credentials. This unbinds the default
# authentication which should be rebound again after trying this.
try:
ldap_bind(user_dn, password)
- result = True
+ result = username
except:
result = False
Module: check_mk
Branch: master
Commit: 112c770a7cc6a1f5c70b11c4dce0f29a11fbe5e6
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=112c770a7cc6a1…
Author: Lars Michelsen <lm(a)mathias-kettner.de>
Date: Wed Jan 9 11:11:56 2013 +0100
ldap: Detecting and handling size limit errors for better understanding
---
web/plugins/userdb/ldap.py | 17 ++++++++++++-----
1 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/web/plugins/userdb/ldap.py b/web/plugins/userdb/ldap.py
index fb441f2..1f9775f 100644
--- a/web/plugins/userdb/ldap.py
+++ b/web/plugins/userdb/ldap.py
@@ -192,11 +192,18 @@ def ldap_search(base, filt = '(objectclass=*)', columns = [], scope = None):
# Convert all keys to lower case!
result = []
- for dn, obj in ldap_connection.search_s(base, scope, filt, columns):
- new_obj = {}
- for key, val in obj.iteritems():
- new_obj[key.lower().decode('utf-8')] = [ i.decode('utf-8') for i in val ]
- result.append((dn, new_obj))
+ try:
+ for dn, obj in ldap_connection.search_s(base, scope, filt, columns):
+ new_obj = {}
+ for key, val in obj.iteritems():
+ new_obj[key.lower().decode('utf-8')] = [ i.decode('utf-8') for i in val ]
+ result.append((dn, new_obj))
+ except ldap.SIZELIMIT_EXCEEDED:
+ raise MKLDAPException(_('The response reached a size limit. This could be due to '
+ 'a sizelimit configuration on the LDAP server.<br />Throwing away the '
+ 'incomplete results. You should change the scope of operation '
+ 'within the ldap or adapt the limit settings of the LDAP server.'))
+
return result
#return ldap_connection.search_s(base, scope, filter, columns)
#for dn, obj in ldap_connection.search_s(base, scope, filter, columns):
Module: check_mk
Branch: master
Commit: 3dd6d868b02311720a546211b9c8e9039916d10b
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=3dd6d868b02311…
Author: Mathias Kettner <mk(a)mathias-kettner.de>
Date: Tue Jan 8 17:50:26 2013 +0100
New script multisite_to_mrpe for exporting services from a remote system
---
ChangeLog | 1 +
doc/treasures/.gitignore | 1 +
.../{multisite_to_local => multisite_to_mrpe} | 60 ++++++++++++++++----
web/plugins/views/builtin.py | 28 +++++++++
4 files changed, 78 insertions(+), 12 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index e161bc0..5988096 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -44,6 +44,7 @@
* FIX: zpool_status: fixed crash when spare devices were available
* FIX: hr_fs: handle negative values in order to larger disks (thanks to Christof Musik)
* FIX: mssql_backup: Fixed wrong calculation of backup age in seconds
+ * New script multisite_to_mrpe for exporting services from a remote system
Multisite:
diff --git a/doc/treasures/.gitignore b/doc/treasures/.gitignore
new file mode 100644
index 0000000..c332f1d
--- /dev/null
+++ b/doc/treasures/.gitignore
@@ -0,0 +1 @@
+.f12
diff --git a/doc/treasures/multisite_to_local b/doc/treasures/multisite_to_mrpe
similarity index 62%
rename from doc/treasures/multisite_to_local
rename to doc/treasures/multisite_to_mrpe
index b2a0fde..9274880 100755
--- a/doc/treasures/multisite_to_local
+++ b/doc/treasures/multisite_to_mrpe
@@ -2,12 +2,35 @@
# encoding: utf-8
# Fetches the current state of all services of a host via Multisite and
-# outputs a <<<local>>> section reflecting these states. This script is
+# outputs an <<<mrpe>>> section reflecting these states. This script is
# intended to be used as a datasource program.
#
-# Still missing:
-# - Output of performance data (not included in standard view)
-# - Assignment of corrent PNP template (need original check command)
+# Why? This allows you to "import" monitoring data from another
+# Check_MK system via HTTP without having livestatus access. The
+# administrator of the remote system has full control over this
+# channel and can also select a subset of hosts/services to export
+# by simply using standard contact <-> object relation for that
+# user.
+#
+# How to setup:
+# 1. On the remote system create a user for the export and
+# set its authentication method to 'Automation'. In this
+# step a secret is being created.
+# 2. In your local monitoring system create one host for
+# each remote host that you want to import (sorry, this
+# has really to be done manually)
+# 3. Configure this script as a datasource program for those
+# hosts. Call me with --help for details.
+# 4. Do Inventory and activate your configuration
+#
+# Note: Whenever the list of services of an imported host
+# changes on the remote system you have to do a re-inventory
+# of that host.
+#
+# Here is an example for a datasource_programs-definition (assuming
+# that the user is "automation"):
+# "$OMD_ROOT/local/lib/multisite_to_mrpe --secret dasistgeheim --url http://localhost/remote/check_mk/ <HOST>"
+#
import os, sys, getopt, time, urllib
@@ -27,11 +50,11 @@ def verbose(text):
sys.stdout.write(text + "\n")
def usage():
- sys.stdout.write("""Usage: multisite_to_local [OPTIONS] HOST
+ sys.stdout.write("""Usage: multisite_to_mrpe [OPTIONS] HOST
This program is intended to be used as a datasource program.
It fetches the current state of all services of a host
-from a remote Multisite and create a <<<local>>> section
+from a remote Multisite and create a <<<mrpe>>> section
from it. That way all those services can be added to the
monitoring of another OMD site.
@@ -44,16 +67,19 @@ Options:
-u, --user U Name of automation user (default: "automation")
-U, --url U Multisite URL of the remote server including
-S, --secret S Automation secret (default: public)
+ --debug Let Python exceptions come through
""")
short_options = 'hvu:U:S:'
-long_options = [ "help", "verbose", "user=", "url=", "secret=" ]
+long_options = [ "help", "verbose", "user=", "url=", "secret=", "debug" ]
opt_verbose = False
opt_user = "automation"
opt_secret = None
opt_url = None
+opt_debug = False
+
if omd_site:
opt_url = "http://localhost/" + omd_site + "/check_mk/"
@@ -73,6 +99,8 @@ for o,a in opts:
# Modifiers
elif o in [ '-v', '--verbose']:
opt_verbose = True
+ elif o == '--debug':
+ opt_debug = True
elif o in [ '-u', '--user']:
opt_user = a
elif o in [ '-S', '--secret']:
@@ -85,6 +113,8 @@ if omd_site and not opt_secret:
opt_secret = file(omd_root + "/var/check_mk/web/" + opt_user
+ "/automation.secret").read().strip()
except Exception, e:
+ if opt_debug:
+ raise
bail_out("Cannot read automation secret from user %s: %s" %
(opt_user, e))
@@ -112,7 +142,7 @@ variables = [
( "_username", opt_user ),
( "_secret", opt_secret ),
( "host", arg_host ),
- ( "view_name", "host" ),
+ ( "view_name", "host_export" ),
( "output_format", "python" ),
]
@@ -122,16 +152,22 @@ verbose("URL: " + url)
try:
pipe = urllib.urlopen(url)
answer = pipe.read()
+ if answer.startswith("ERROR:"):
+ bail_out("Error response from remote Multisite: %s" % answer[7:])
services = convert_from_multisite(eval(answer))
except Exception, e:
+ if opt_debug:
+ raise
bail_out("Cannot call Multisite URL: %s" % e)
-sys.stdout.write('<<<local>>>\n')
+sys.stdout.write('<<<mrpe>>>\n')
for service in services:
state = { "OK" : 0, "WARN" : 1, "CRIT" : 2, "UNKNOWN" : 3}.get(service['service_state'])
if state != None: # skip pending services
- sys.stdout.write("%d %s - %s\n" % (
- state,
+ sys.stdout.write("(%s) %s %d %s|%s\n" % (
+ service["svc_check_command"],
service["service_description"].replace(" ", "_"),
- service["svc_plugin_output"]))
+ state,
+ service["svc_plugin_output"],
+ service["svc_perf_data"]))
diff --git a/web/plugins/views/builtin.py b/web/plugins/views/builtin.py
index 49ea300..0d701ed 100644
--- a/web/plugins/views/builtin.py
+++ b/web/plugins/views/builtin.py
@@ -361,6 +361,34 @@ multisite_builtin_views.update({
'sorters': [('svcdescr', False)],
'linktitle': _('Services'),
'title': _('Services of Host')},
+ 'host_export': {'browser_reload': 30,
+ 'column_headers': 'pergroup',
+ 'datasource': 'services',
+ 'description': 'All services of a given host. The host and site must be set via HTML variables.',
+ 'group_painters': [('host_with_state', 'hoststatus')],
+ 'hard_filters': [],
+ 'hard_filtervars': [('st0', 'on'),
+ ('st1', 'on'),
+ ('st2', 'on'),
+ ('st3', 'on'),
+ ('stp', 'on')],
+ 'hidden': True,
+ 'hide_filters': ['site', 'host'],
+ 'icon': 'services',
+ 'layout': 'boxed',
+ 'mustsearch': False,
+ 'num_columns': 1,
+ 'painters': [('service_state', None),
+ ('service_description', 'service'),
+ ('svc_plugin_output', None),
+ ('svc_perf_data', None),
+ ('svc_check_command', None)],
+ 'play_sounds': False,
+ 'public': True,
+ 'show_filters': ['svcstate', 'serviceregex'],
+ 'sorters': [('svcdescr', False)],
+ 'linktitle': _('Services'),
+ 'title': _('Services of Host')},
'hosts': {'browser_reload': 30,
'column_headers': 'off',
'datasource': 'services',