r5396 - trunk/mint/python/mint/plumage
by croberts@fedoraproject.org
Author: croberts
Date: 2012-05-31 16:42:28 +0000 (Thu, 31 May 2012)
New Revision: 5396
Modified:
trunk/mint/python/mint/plumage/session.py
Log:
Making the cumin-report historical record loading thread wake-up once each day to handle the case of a backload of older records (database recovery, etc).
Modified: trunk/mint/python/mint/plumage/session.py
===================================================================
--- trunk/mint/python/mint/plumage/session.py 2012-05-31 14:49:15 UTC (rev 5395)
+++ trunk/mint/python/mint/plumage/session.py 2012-05-31 16:42:28 UTC (rev 5396)
@@ -209,37 +209,40 @@
return record
def run(self):
- self._check_connection()
- if self.stop_requested:
- return
-
- try:
- # Make sure we have a database
- self._init()
-
- # We create objects here. Tag them with the right class,
- # probably specified to us from a config option (with a corresponding
- # query specification in the xml)
- (oldest, newest) = self.app.update_thread.get_first_and_last_sample_timestamp(self.cls)
- if oldest is None:
- # if we have no oldest record (first run), start at "5 min ago" and start loading everything
- oldest = datetime.now() - timedelta(seconds=300)
- oldest = oldest + UTC_DIFF
-
- log.info("PlumageSessionThread--history: Loading records older than %s" % oldest)
- times = sorted(self.collection.find({"ts": {'$lt': oldest}}).distinct('ts'), reverse=True)
- sample_times = map(lambda i: times[i],filter(lambda i: i%5 == 0,range(len(times))))
-
- for time in sample_times:
- record = self.fillOSUtilStats(time, "history")
-
- obj = ObjectUpdate(self.app.model, record, self.cls)
- self.app.update_thread.enqueue(obj)
-
- log.info("PlumageSessionThread--history: run completed")
-
- except Exception, e:
- log.info("%s got exception %s, exiting" % (self.__class__.__name__, str(e)))
+ while True:
+ self._check_connection()
+ if self.stop_requested:
+ return
+
+ try:
+ # Make sure we have a database
+ self._init()
+
+ # We create objects here. Tag them with the right class,
+ # probably specified to us from a config option (with a corresponding
+ # query specification in the xml)
+ (oldest, newest) = self.app.update_thread.get_first_and_last_sample_timestamp(self.cls)
+ if oldest is None:
+ # if we have no oldest record (first run), start at "5 min ago" and start loading everything
+ oldest = datetime.now() - timedelta(seconds=300)
+ oldest = oldest + UTC_DIFF
+
+ log.info("PlumageSessionThread--history: Loading records older than %s" % oldest)
+ times = sorted(self.collection.find({"ts": {'$lt': oldest}}).distinct('ts'), reverse=True)
+ sample_times = map(lambda i: times[i],filter(lambda i: i%5 == 0,range(len(times))))
+
+ for time in sample_times:
+ record = self.fillOSUtilStats(time, "history")
+
+ obj = ObjectUpdate(self.app.model, record, self.cls)
+ self.app.update_thread.enqueue(obj)
+
+ log.info("PlumageSessionThread--history: run completed")
+
+ except Exception, e:
+ log.info("%s got exception %s, exiting" % (self.__class__.__name__, str(e)))
+ #wake up once a day just in case there has been additional historical items added
+ sleep(86400)
class CurrentPlumageSessionThread(PlumageSessionThread):
def run(self):
11 years, 11 months
r5395 - in branches/stability: cumin cumin/bin cumin/etc cumin/model cumin/python/cumin cumin/python/cumin/account cumin/python/cumin/grid cumin/python/cumin/inventory cumin/python/cumin/messaging cumin/python/cumin/usergrid mint/python/mint rosemary/python/rosemary wooly/python/wooly wooly/python/wooly/wsgiserver
by tmckay@fedoraproject.org
Author: tmckay
Date: 2012-05-31 14:49:15 +0000 (Thu, 31 May 2012)
New Revision: 5395
Added:
branches/stability/cumin/model/access/
branches/stability/cumin/python/cumin/authenticator.py
branches/stability/cumin/python/cumin/persona.py
Modified:
branches/stability/cumin/Makefile
branches/stability/cumin/bin/cumin-admin
branches/stability/cumin/bin/cumin-database
branches/stability/cumin/bin/cumin-web
branches/stability/cumin/etc/cumin.conf
branches/stability/cumin/python/cumin/account/widgets.py
branches/stability/cumin/python/cumin/account/widgets.strings
branches/stability/cumin/python/cumin/admin.py
branches/stability/cumin/python/cumin/config.py
branches/stability/cumin/python/cumin/database.py
branches/stability/cumin/python/cumin/grid/dashboard.py
branches/stability/cumin/python/cumin/grid/job.py
branches/stability/cumin/python/cumin/grid/limit.py
branches/stability/cumin/python/cumin/grid/main.py
branches/stability/cumin/python/cumin/grid/negotiator.py
branches/stability/cumin/python/cumin/grid/pool.py
branches/stability/cumin/python/cumin/grid/quota.py
branches/stability/cumin/python/cumin/grid/scheduler.py
branches/stability/cumin/python/cumin/grid/submission.py
branches/stability/cumin/python/cumin/grid/tags.py
branches/stability/cumin/python/cumin/inventory/main.py
branches/stability/cumin/python/cumin/inventory/system.py
branches/stability/cumin/python/cumin/main.py
branches/stability/cumin/python/cumin/messaging/binding.py
branches/stability/cumin/python/cumin/messaging/brokerlink.py
branches/stability/cumin/python/cumin/messaging/connection.py
branches/stability/cumin/python/cumin/messaging/exchange.py
branches/stability/cumin/python/cumin/messaging/main.py
branches/stability/cumin/python/cumin/messaging/queue.py
branches/stability/cumin/python/cumin/model.py
branches/stability/cumin/python/cumin/objectframe.py
branches/stability/cumin/python/cumin/objectselector.py
branches/stability/cumin/python/cumin/server.py
branches/stability/cumin/python/cumin/stat.py
branches/stability/cumin/python/cumin/usergrid/main.py
branches/stability/cumin/python/cumin/util.py
branches/stability/cumin/python/cumin/widgets.py
branches/stability/mint/python/mint/database.py
branches/stability/mint/python/mint/main.py
branches/stability/rosemary/python/rosemary/model.py
branches/stability/wooly/python/wooly/__init__.py
branches/stability/wooly/python/wooly/forms.py
branches/stability/wooly/python/wooly/pages.py
branches/stability/wooly/python/wooly/server.py
branches/stability/wooly/python/wooly/table.py
branches/stability/wooly/python/wooly/util.py
branches/stability/wooly/python/wooly/widgets.py
branches/stability/wooly/python/wooly/wsgiserver/ssl_builtin.py
branches/stability/wooly/python/wooly/wsgiserver/ssl_pyopenssl.py
Log:
Add support for LDAP, web server SSL, and role enforcement
BZ737979
BZ820861
BZ769573
Modified: branches/stability/cumin/Makefile
===================================================================
--- branches/stability/cumin/Makefile 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/Makefile 2012-05-31 14:49:15 UTC (rev 5395)
@@ -27,8 +27,10 @@
install -pm 0644 ../wooly/LICENSE-for-wsgiserver ${CUMIN_HOME}/doc
install -pm 0644 ../wooly/COPYING-for-wsgiserver ${CUMIN_HOME}/doc
install -d ${CUMIN_HOME}/model/upgrades
+ install -d ${CUMIN_HOME}/model/access
install -pm 0644 model/*.xml ${CUMIN_HOME}/model
- install -pm 0755 model/upgrades/* ${CUMIN_HOME}/model/upgrades/
+ install -pm 0644 model/access/* ${CUMIN_HOME}/model/access/
install -d ${CUMIN_HOME}/resources
install -pm 0644 ../wooly/resources/* ${CUMIN_HOME}/resources
install -pm 0644 resources/* ${CUMIN_HOME}/resources
Modified: branches/stability/cumin/bin/cumin-admin
===================================================================
--- branches/stability/cumin/bin/cumin-admin 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/bin/cumin-admin 2012-05-31 14:49:15 UTC (rev 5395)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
import os
import sys
@@ -6,7 +6,7 @@
import subprocess
from subprocess import PIPE
-from psycopg2 import IntegrityError
+from psycopg2 import IntegrityError, OperationalError
home = os.environ.get("CUMIN_HOME", os.path.normpath("/usr/share/cumin"))
sys.path.append(os.path.join(home, "python"))
@@ -16,6 +16,7 @@
from cumin import *
from cumin.config import *
from cumin.util import *
+from cumin.admin import SchemaMissing
def main():
uid = os.getuid()
@@ -65,18 +66,46 @@
sys.exit(1)
broker_uris = [x.strip() for x in opts.brokers.split(",")]
+ authmech = [x.strip() for x in values.common.auth.split(";") if x != ""]
- app = Cumin(config.get_home(), broker_uris, opts.database)
+ app = Cumin(config.get_home(), broker_uris, opts.database, authmech=authmech)
app.check()
- app.init()
+ # Normally, a schema version check during Cumin init will raise
+ # an exception if the schema version doesn't match. However,
+ # cumin-admin is the mechanism for fixing the schema so we
+ # have to avoid the check here!
+ try:
+ app.db_init(schema_version_check=False)
+ except OperationalError:
+ print "Can't talk to the database"
+ error("Run 'cumin-database check' as root for more information")
+
conn = app.database.get_connection()
cursor = conn.cursor()
+
+ # Okay, do the schema version check here and disallow
+ # all but schema commands if the version is wrong
+ if name not in ["check_schema",
+ "create_schema",
+ "drop_schema",
+ "print_schema",
+ "upgrade_schema"]:
+ err = True
+ try:
+ app.admin.check_schema(cursor)
+ err = False
+ except SchemaMissing:
+ print("The schema is missing, run 'cumin-admin create-schema'")
+ except SchemaVersion, e:
+ print str(e)
+ print("Run 'cumin-admin upgrade-schema'")
+ if err:
+ error("Only schema commands are allowed until the schema is repaired")
try:
handler(app, cursor, opts, args[1:])
-
conn.commit()
finally:
cursor.close()
@@ -96,6 +125,11 @@
lines.append("User commands:")
lines.append("")
lines.append(" add-user USER [PASSWORD] Add USER")
+ lines.append(" external-user USER Add USER with external authentication.")
+ lines.append(" Use this command when a role other than 'user'")
+ lines.append(" will be set for this external user. Otherwise,")
+ lines.append(" this command is not necessary.")
+ lines.append("")
lines.append(" remove-user USER Remove USER")
lines.append(" add-assignment USER ROLE Add USER to ROLE")
lines.append(" remove-assignment USER ROLE Remove USER from ROLE")
@@ -104,6 +138,8 @@
lines.append(" list-roles List roles")
lines.append(" export-users FILE Export user list to file")
lines.append(" import-users FILE Import user list from file")
+ lines.append(" expunge-users Remove all users. Use with caution")
+ lines.append(" You may want to run export-users first")
lines.append("")
lines.append("Schema commands:")
lines.append("")
@@ -184,7 +220,11 @@
print app.admin.get_schema(),
def handle_upgrade_schema(app, cursor, opts, args):
- curr = app.admin.get_schema_version(cursor)
+ try:
+ curr = app.admin.get_schema_version(cursor)
+ except SchemaMissing:
+ error("The schema is missing, run 'cumin-admin create-schema'")
+
target = app.admin.get_target_schema_version()
if curr == target:
print "The schema is already version %s, nothing to do" % target
@@ -243,16 +283,20 @@
def handle_check_schema(app, cursor, opts, args):
try:
schema_version = app.admin.check_schema(cursor)
+ except SchemaMissing:
+ error("The schema is missing, run 'cumin-admin create-schema'")
except Exception, e:
error(str(e))
print "The schema is OK (schema version %s)" % schema_version
def handle_drop_schema(app, cursor, opts, args):
- app.admin.drop_schema(cursor)
+ try:
+ app.admin.drop_schema(cursor)
+ print "The schema is dropped"
+ except SchemaMissing:
+ print "The schema has already been dropped"
- print "The schema is dropped"
-
def get_users(app, cursor):
user_cls = app.model.com_redhat_cumin.User
role_cls = app.model.com_redhat_cumin.Role
@@ -281,24 +325,102 @@
def handle_list_users(app, cursor, opts, args):
- print " ID Name Roles"
- print "---- -------------------- --------------------"
+ print " ID Name Roles"
+ print "---- -------------------- --------------------"
users, roles_by_user_id = get_users(app, cursor)
-
+ externals = 0
for user in users:
try:
roles = ", ".join(roles_by_user_id[user._id])
except KeyError:
roles = ""
+ if len(user.password) == 0:
+ ex = "*"
+ externals += 1
+ else:
+ ex = " "
+ print "%4i %s %-20s %-20s" % (user._id, ex, user.name, roles)
- print "%4i %-20s %-20s" % (user._id, user.name, roles)
-
count = len(users)
print
print "(%i user%s found)" % (count, ess(count))
+ if externals > 0:
+ print "(%i external user%s, indicated by *)" % (externals, ess(externals))
+def handle_external_user(app, cursor, opts, args):
+ try:
+ name = args[0]
+ except IndexError:
+ error("USER is required")
+ role = app.admin.get_role(cursor, "user")
+ try:
+ user = app.admin.add_user(cursor, name, "")
+ except IntegrityError:
+ error("A user called '%s' already exists" % name)
+
+ app.admin.add_assignment(cursor, user, role)
+ print "External user '%s' is added" % name
+
+def handle_external_sync(app, cursor, opts, args):
+ print "This command is not supported at this time."
+ return
+
+ try:
+ authenticatorname = args[0]
+ except IndexError:
+ error("AUTHENTICATOR name is required")
+
+ output_on = False
+ if len(args) > 1:
+ output_on = args[1].lower() in ("t", "true", "1")
+
+ users, conflicts, errors = \
+ app.authenticator.batch_import(authenticatorname, output_on)
+
+ print("Successfully imported %s users, %s conflicts" % (users, conflicts))
+ if errors:
+ print("Errors reported, check logs for details.")
+
+def handle_expunge_users(app, cursor, opts, args):
+
+ print("This will remove all users from Cumin's database.")
+ print("Hint: you may want to backup Cumin's user database "\
+ "with export-users first.\n")
+ try:
+ go = confirm( "Are you sure you want to do this?", resp=False)
+ except KeyboardInterrupt:
+ go = False
+
+ if go:
+ cls = app.model.com_redhat_cumin.User
+ cls.delete_selection(cursor)
+ print("\nAll users removed.")
+ else:
+ print("\nNo users removed.")
+
+
+def handle_add_role(app, cursor, opts, args):
+ print("\nNot implemented.")
+ return
+
+ # We may allow this later
+ try:
+ name = args[0]
+ except IndexError:
+ error("ROLE is required")
+
+ if app.admin.get_role(cursor, name):
+ error("A role called '%s' already exists" % name)
+
+ try:
+ app.admin.add_role(cursor, name)
+ except IntegrityError:
+ error("A role called '%s' already exists" % name)
+
+ print "Role '%s' is added" % name
+
def handle_add_user(app, cursor, opts, args):
try:
name = args[0]
@@ -314,18 +436,14 @@
password = args[1]
except IndexError:
password = prompt_password()
-
crypted = crypt_password(password)
-
role = app.admin.get_role(cursor, "user")
-
try:
user = app.admin.add_user(cursor, name, crypted)
except IntegrityError:
error("A user called '%s' already exists" % name)
app.admin.add_assignment(cursor, user, role)
-
print "User '%s' is added" % name
def handle_export_users(app, cursor, opts, args):
@@ -365,15 +483,19 @@
for info in user_data:
line += 1
+ count = 0
for field in info:
# Don't suppose it's possible for data to be anything other than
# strings from csv, but this is where such type checking goes
if type(field) != str:
error("Data error, line %u, fields must be strings" % line)
- if len(field) == 0:
- error("Data error, line %u, importer "\
- "does not allow empty fields" % line)
+ # Empty password field is external auth indicator, allow it
+ if len(field) == 0 and count != 1:
+ error("Data error, line %u, field %u, importer "\
+ "does not allow empty field" % (line, field+1))
+ count += 1
+
if len(info) < 2:
error("Data error, line %u, not enough fields. "\
" User and password are required" % (line))
@@ -388,7 +510,7 @@
# before we get an integrity error
warn("A user called '%s' already exists, skipping" % info[0])
continue
-
+
try:
user = app.admin.add_user(cursor, info[0], info[1])
except IntegrityError:
@@ -439,7 +561,6 @@
def handle_remove_assignment(app, cursor, opts, args):
user, role = get_user_and_role(app, cursor, args)
-
assignment = app.admin.get_assignment(cursor, user, role)
if not assignment:
@@ -464,6 +585,11 @@
user = app.admin.get_user(cursor, user_name)
role = app.admin.get_role(cursor, role_name)
+ if not user:
+ error("Specified user %s does not exist" % user_name)
+ if not role:
+ error("Specified role %s does not exist" % role_name)
+
return user, role
def handle_change_password(app, cursor, opts, args):
@@ -476,10 +602,11 @@
if not user:
error("User '%s' is not found" % user_name)
-
+ if user.password == "":
+ error("User '%s' is an external user, password cannot be changed "\
+ "through this mechanism" % user_name)
user.password = crypt_password(prompt_password())
user.save(cursor)
-
print "Password of user '%s' is changed" % user.name
def handle_load_demo_data(app, cursor, opts, args):
Modified: branches/stability/cumin/bin/cumin-database
===================================================================
--- branches/stability/cumin/bin/cumin-database 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/bin/cumin-database 2012-05-31 14:49:15 UTC (rev 5395)
@@ -73,7 +73,7 @@
if [[ ! -f "$pghbaconf" ]]; then
echo "Error: The database is not initialized"
if [ "$1" != noadvice ]; then
- echo "Hint: Run 'cumin-database initialize'"
+ echo "Hint: Run 'cumin-database install'"
fi
return 1
fi
@@ -83,7 +83,7 @@
grep "$dbname" "$pghbaconf" &> /dev/null || {
echo "Error: The database is not configured"
if [ "$1" != noadvice ]; then
- echo "Hint: Run 'cumin-database configure'"
+ echo "Hint: Run 'cumin-database install'"
fi
return 1
}
@@ -93,7 +93,7 @@
psql -d cumin -U cumin -h localhost -c '\q' &> /dev/null || {
echo "Error: The database is not created"
if [ "$1" != noadvice ]; then
- echo "Hint: Run 'cumin-database create'"
+ echo "Hint: Run 'cumin-database install'"
fi
return 1
}
@@ -117,7 +117,7 @@
*)
echo "Error: The database is not created"
if [ "$1" != noadvice ]; then
- echo "Hint: Run 'cumin-database create'"
+ echo "Hint: Run 'cumin-database install'"
fi
return 1
;;
@@ -132,7 +132,7 @@
WARNING
This script installs a cumin database into the system postgresql
-instance.
+instance. It will take the following actions as necessary.
* It will stop and start the postgresql service.
Modified: branches/stability/cumin/bin/cumin-web
===================================================================
--- branches/stability/cumin/bin/cumin-web 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/bin/cumin-web 2012-05-31 14:49:15 UTC (rev 5395)
@@ -9,6 +9,8 @@
from cumin.config import *
from cumin.util import *
from parsley.loggingex import PipeLogThread
+from psycopg2 import OperationalError
+from cumin.admin import SchemaVersion, SchemaMissing
def restore_IO():
sys.stderr = sys.__stderr__
@@ -38,7 +40,61 @@
cumin.wallaby_refresh = values.wallaby_refresh
if cumin.wallaby_refresh == 0:
cumin.wallaby_refresh = None
-
+
+def set_authorize_config(cumin, values, access_root):
+
+ # Allow this to be turned on/off.
+ # Default will be off.
+ cumin.do_authorize = values.authorize
+
+ # This is the path to the xml file defining authorization.
+ # Allow this to be disabled via "None"
+ if values.auth_path == "None":
+ cumin.access_path = None
+ else:
+ # If there is no initial dir, prepend home
+ dir_name = os.path.split(values.auth_path)[0]
+ if len(dir_name) == 0:
+ cumin.access_path = os.path.join(access_root, values.auth_path)
+ else:
+ cumin.access_path = values.auth_path
+
+def set_ldap_configs(cumin, values):
+ cumin.ldap_timeout = values.ldap_timeout
+ cumin.ldap_tls_cacertdir = values.ldap_tls_cacertdir
+ cumin.ldap_tls_cacertfile = values.ldap_tls_cacertfile
+
+def check_cert_files(opts):
+ # If a certificate or key file has been specified,
+ # check to make sure that both exist and that the
+ # app has permission to read them.
+ if opts.server_cert or opts.server_key:
+ for attr in ("server_cert", "server_key"):
+ attr_name = attr.replace("_", "-")
+ f = getattr(opts, attr)
+ if f == "":
+ log.error("parameter %s must have a value" % \
+ attr_name)
+ return False
+
+ if not os.path.isfile(f):
+ log.error("%s is not a file." % f)
+ return False
+
+ try:
+ open(f, "r")
+ except Exception, e:
+ log.error("Exception reading %s, %s" % (attr_name, e))
+ return False
+ return True
+
+def adjust_return(passed_init, ret):
+ # Shift non-zer0 return codes left 1 bit
+ # and OR in whether or not init passed
+ if ret != 0:
+ ret = ret << 1 | passed_init
+ return ret
+
def main():
# Do our own simple option check so we can redirect IO early
# without worrying about other options or the behavior of optParse
@@ -72,6 +128,13 @@
print_exc()
pipeThread = None
+ # Use ArgError to jump to the finally block and exit
+ class ArgError(Exception):
+ pass
+
+ class CertificateError(Exception):
+ pass
+
try:
try:
return_code = 0
@@ -84,6 +147,8 @@
# Add additional parameters for web
parser.add_option("--host")
parser.add_option("--port", type=int)
+ parser.add_option("--server-cert")
+ parser.add_option("--server-key")
parser.add_option("--section", default="web")
parser.add_option("--daemon", action="store_true", default=False)
@@ -106,15 +171,32 @@
values.log_max_archives)
broker_uris = [x.strip() for x in opts.brokers.split(",")]
+ authmech = [x.strip() for x in values.auth.split(";") if x != ""]
cumin = Cumin(config.get_home(), broker_uris, opts.database,
- opts.host, opts.port, values.persona)
+ opts.host, opts.port,
+ opts.server_cert, opts.server_key,
+ values.persona, authmech)
+
if type(values.sasl_mech_list) == str:
cumin.sasl_mech_list = values.sasl_mech_list.upper()
+ if not check_cert_files(opts):
+ raise CertificateError
+
+ if len(args) != 0:
+ log.error("Extra arguments:" + "".join([" "+arg for arg in args]))
+ raise ArgError
+
set_aviary_configs(cumin, values)
set_wallaby_configs(cumin, values, broker_uris)
+ set_ldap_configs(cumin, values)
+ # Someday we may let this be configurable, for now it will
+ # be hardwired
+ values.auth_path = "persona.xml"
+ set_authorize_config(cumin, values, config.get_access_root())
+
cumin.debug = opts.debug
cumin.user = values.user
cumin.update_interval = values.update_interval
Modified: branches/stability/cumin/etc/cumin.conf
===================================================================
--- branches/stability/cumin/etc/cumin.conf 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/etc/cumin.conf 2012-05-31 14:49:15 UTC (rev 5395)
@@ -30,11 +30,13 @@
# To use aviary job servers, uncomment the following line and edit as needed.
# The value is a comma separated list of URLs. The default port condor uses
# for an Aviary job server is 9090.
+# More details in 'Some parameter explanations by section' below.
# aviary-job-servers: http://localhost:9090
# To use aviary query servers, uncomment the following line and edit as needed.
# The value is a comma separated list of URLs. The default port condor uses
# for an Aviary query server is 9091.
+# More details in 'Some parameter explanations by section' below.
# aviary-query-servers: http://localhost:9091
# Full path to private key file used for ssl communication with aviary servers.
@@ -91,10 +93,17 @@
# log-file: $CUMIN_HOME/log/web.log
# host: localhost ('0.0.0.0' binds to all local interfaces)
# port: 45672
+# server-cert:
+# server-key:
# operator-email: [none]
# update-interval: 10
# max-qmf-table-sort: 1000
# persona: default
+# auth:
+# ldap_tls_cacertfile:
+# ldap_tls_cacertdir:
+# ldap_timeout: 30
+# authorize: False
[data]
# log-file: $CUMIN_HOME/log/data.log
@@ -172,107 +181,167 @@
# *************** Some parameter explanations by section ***************
-# [common]
+## [common]
-# wallaby-broker: [first item in 'brokers' list]
-# The broker to use for interaction with a Wallaby agent.
-# This is not necessarily the same broker used for grid
-# and messaging information. The default value is the
-# first item in the 'brokers' list if not specified. To
-# turn off interaction with Wallaby, set wallaby-broker to
-# the string 'None'.
+## sasl-mech-list:
+## A space separated list that restricts the SASL mechanisms
+## Cumin may use when authenticating to the broker. By default
+## Cumin will use ANONYMOUS authentication if the broker URL does
+## not contain user/password and will use PLAIN or DIGEST-MD5
+## if the broker URL does contain user/password. DIGEST-MD5 is
+## considered more secure than PLAIN by SASL and will be
+## used if is available. This value may be set explicitly
+## to make other choices.
+## Setting the value to 'AVAILABLE' will allow Cumin to use
+## any available mechanism including ANONYMOUS.
-# wallaby-refresh: 60
-# How often in seconds to contact the Wallaby agent
-# for updated information.
+## wallaby-broker: [first item in 'brokers' list]
+## The broker to use for interaction with a Wallaby agent.
+## This is not necessarily the same broker used for grid
+## and messaging information. The default value is the
+## first item in the 'brokers' list if not specified. To
+## turn off interaction with Wallaby, set wallaby-broker to
+## the string 'None'.
-# use-aviary: True
-# Whether or not to use the Aviary services for
-# remote procedure calls to condor. If this is
-# set to False, the QMF interface will be used
-# instead.
+## wallaby-refresh: 60
+## How often in seconds to contact the Wallaby agent
+## for updated information.
-# aviary-job-servers: http://localhost:9090
-# Specifies the URIs for aviary job servers. The value
-# is a comma separated list of URIs. A full URI has the
-# form 'scheme://user/password@host:port/path". The scheme
-# will default to http if not specified, the port will
-# default to 9090, and the path will default to
-# /services/job/. User and password will be empty by
-# default. As a convenience, a URI that explicitly
-# sets a port number may be followed by one or more
-# port numbers separated by commas to specify
-# mulitple job servers whose URIs differ only
-# by port number.
+## aviary-job-servers: http://localhost:9090
+## Specifies the URIs for aviary job servers. The value
+## is a comma separated list of URIs. A full URI has the
+## form 'scheme://user/password@host:port/path". The scheme
+## will default to http if not specified, the port will
+## default to 9090, and the path will default to
+## /services/job/. User and password will be empty by
+## default. As a convenience, a URI that explicitly
+## sets a port number may be followed by one or more
+## port numbers separated by commas to specify
+## mulitple job servers whose URIs differ only
+## by port number.
-# aviary-query-servers: http://localhost:9091
-# Like aviary-job-servers but specifies URIs for aviary
-# query servers. The port value defaults to 9091 and the
-# path defaults to /services/query/. Other
-# defaults are as noted for aviary-job-servers.
+## aviary-query-servers: http://localhost:9091
+## Like aviary-job-servers but specifies URIs for aviary
+## query servers. The port value defaults to 9091 and the
+## path defaults to /services/query/. Other
+## defaults are as noted for aviary-job-servers.
-# log-max-mb: 10
-# Maximum size in MB of *.log files created by cumin.
-# A log file reaching maximum size will be rolled over.
-# A value of 0 leaves the log file size unlimited.
+## log-max-mb: 10
+## Maximum size in MB of *.log files created by cumin.
+## A log file reaching maximum size will be rolled over.
+## A value of 0 leaves the log file size unlimited.
-# log-max-archives: 1
-# Number of rolled over log files to retain. A retained
-# log file will have a "." and a number added to its name.
-# A value of 0 will cause a log file to be truncated
-# rather than renamed if it reaches maximum size.
+## log-max-archives: 1
+## Number of rolled over log files to retain. A retained
+## log file will have a "." and a number added to its name.
+## A value of 0 will cause a log file to be truncated
+## rather than renamed if it reaches maximum size.
-# [web]
+## [web]
-# persona: default
-# Controls content of the top level page.
-# Valid values are "default", "grid", and "messaging".
-# The default persona contains content for both grid and
-# messaging.
+## server-cert:
+## This is a server certificate file in PEM format.
+## If server-cert and server-key are both set and valid,
+## Cumin will use ssl. The scheme in the Cumin URL will
+## be "https://" instead of "http://".
-# [data]
+## server-key:
+## This is a key file in PEM format that corresponds to
+## the server-cert. If server-cert and server-key are both
+## set and valid, Cumin will use ssl. The scheme in the
+## Cumin URL will be "https://" instead of "http://".
-# expire-enabled: True
-# Whether or not the instance runs a thread which periodically expires samples.
-# Running the expire thread from more than one instance is okay but unnecessary
+## persona: grid
+## Controls content of the top level page.
+## Valid values are "default", "grid", and "messaging".
+## The "default" persona contains content for both grid and
+## messaging.
-# expire-interval: 3600
-# How often the expire thread runs, in seconds
+## authorize: False
+## Controls whether or not roles are enforced.
+## Roles are assigned to users with cumin-admin.
+## If roles are enforced, users with only the 'user'
+## role will not see the 'Administrator' tab.
+## The default role for a new user is 'user'.
-# expire-threshold: 86400
-# The maximum age of a sample in seconds. If it's older, it will be deleted when
-# the expire thread runs.
+## auth:
+## Allows specification of LDAP servers for external authentication of
+## users during login. A server must support simple bind for authentication.
+## This is a semicolon separated list of URLs of the following form:
+##
+## ldap://host:port/dn??scope?filter
+##
+## Scope defaults to 'sub', filter defaults to 'uid=usnername'.
+## If a filter is specified Cumin will replace occurrences of %%s
+## with username. Example:
+## ldap://alpha.exmple.com/ou=users,dc=example,dc=com;
+## ldap://beta.example.com/ou=people,dc=example,dc=com??one?mail=%%s
-# vacuum-enabled: True
-# Whether or not the instance runs a thread which periodically vacuums the
-# database. Running the vacuum thread from more than one instance is okay but
-# unnecessary.
+## ldap_tls_cacertfile:
+## Setting this option turns on SSL communication to all LDAP servers.
+## Specifies a file containing certificates for all the Certificate
+## Authorities that Cumin will recognize. An LDAP server certificate
+## must be issued by a recognized Certificate Authority or Cumin will
+## refuse to communicate with the server.
+## If this option is set, Cumin will look at the specified file before
+## looking in the directory specified by ldap_tls_cacertdir.
-# vacuum-interval: 3600
-# How often the vacuum thread runs, in seconds
+## ldap_tls_cacertdir:
+## Setting this option turns on SSL communication to all LDAP servers.
+## Specifies a directory containing certificates for all the Certificate
+## Authorities that Cumin will recognize. Certificates may be in individual
+## files or in a certificate database created with certutil.
+## An LDAP server certificate must be issued by a recognized Certificate
+## Authority or Cumin will refuse to communicate with the server.
+## If ldap_tls_cacertfile is set, Cumin will look in that file before
+## looking in this directory.
-# include-classes: [all]
-# A comma separated list of QMF classes. The default value is all
-# classes in all packages. The cumin-data instance will be bound to
-# classes which appear in 'include-classes' but do not appear in
-# 'exclude-classes'. Classes are specified as package:class for a single
-# class or package:* for all classes in a package.
+## ldap_timeout: 30
+## Timeout in seconds for connections to an LDAP server
-# exclude-classes: [empty list]
-# A comma separated list of QMF classes. The default value is
-# an empty list. The cumin-data instance will be bound to
-# classes which appear in 'include-classes' but do not appear in
-# 'exclude-classes'. Classes are specified as package:class for a single
-# class or package:* for all classes in a package.
+## [data]
-# [master]
+## expire-enabled: True
+## Whether or not the instance runs a thread which periodically expires samples.
+## Running the expire thread from more than one instance is okay but unnecessary
-# webs: web
-# Comma separated list of sections in this file.
-# Each section specifies the configuration for a separate
-# instance of cumin-web.
+## expire-interval: 3600
+## How often the expire thread runs, in seconds
-# datas: data
-# Comma separated list of sections in this file.
-# Each section specifies the configuration for a separate
-# instance of cumin-data.
+## expire-threshold: 86400
+## The maximum age of a sample in seconds. If it's older, it will be deleted when
+## the expire thread runs.
+
+## vacuum-enabled: True
+## Whether or not the instance runs a thread which periodically vacuums the
+## database. Running the vacuum thread from more than one instance is okay but
+## unnecessary.
+
+## vacuum-interval: 3600
+## How often the vacuum thread runs, in seconds
+
+## include-classes: [all]
+## A comma separated list of QMF classes. The default value is all
+## classes in all packages. The cumin-data instance will be bound to
+## classes which appear in 'include-classes' but do not appear in
+## 'exclude-classes'. Classes are specified as package:class for a single
+## class or package:* for all classes in a package.
+
+## exclude-classes: [empty list]
+## A comma separated list of QMF classes. The default value is
+## an empty list. The cumin-data instance will be bound to
+## classes which appear in 'include-classes' but do not appear in
+## 'exclude-classes'. Classes are specified as package:class for a single
+## class or package:* for all classes in a package.
+
+## [master]
+
+## webs: web
+## Comma separated list of sections in this file.
+## Each section specifies the configuration for a separate
+## instance of cumin-web.
+
+## datas: data
+## Comma separated list of sections in this file.
+## Each section specifies the configuration for a separate
+## instance of cumin-data.
Modified: branches/stability/cumin/python/cumin/account/widgets.py
===================================================================
--- branches/stability/cumin/python/cumin/account/widgets.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/account/widgets.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -61,7 +61,6 @@
def render_content(self, sessino):
return "Change password"
-
class LoginPage(HtmlPage):
def __init__(self, app, name):
super(LoginPage, self).__init__(app, name)
@@ -119,28 +118,36 @@
self.validate(session)
if not self.errors.get(session):
- cursor = self.app.database.get_read_cursor()
+ user, ok = self.app.authenticator.authenticate(name, password)
+ if ok:
+ # You're in! almost...
+ # Check for a valid group if group authorization is on
+ roles = []
+ if self.app.authorizator.is_enforcing():
+ if user is None:
+ # This was an external user with no role
+ # entry in the Cumin database. We default the
+ # role to 'user'. In the future we may store
+ # roles externally as well.
+ roles = ['user']
+ else:
+ cursor = self.app.database.get_read_cursor()
+ roles = self.app.admin.get_roles_for_user(
+ cursor, user)
+ ok = self.app.authorizator.contains_valid_group(roles)
+ if not ok:
+ self.login_invalid.set(session, "roles")
- cls = self.app.model.com_redhat_cumin.User
- user = cls.get_object(cursor, name=name)
-
- if not user:
- self.login_invalid.set(session, True)
- return
-
- crypted = user.password
-
- if crypted and crypt(password, crypted) == crypted:
- # You're in!
-
- login = LoginSession(self.app, user)
- session.client_session.attributes["login_session"] = login
-
- url = self.page.origin.get(session)
-
- self.page.redirect.set(session, url)
+ # If we're still okay, set up the login
+ if ok:
+ if user is None:
+ user = name
+ login = LoginSession(self.app, user, roles)
+ session.client_session.attributes["login_session"] = login
+ url = self.page.origin.get(session)
+ self.page.redirect.set(session, url)
else:
- self.login_invalid.set(session, True)
+ self.login_invalid.set(session, "credentials")
def render_operator_link(self, session):
email = self.app.operator_email
@@ -151,7 +158,10 @@
return "site operator"
def render_login_invalid(self, session):
- if self.login_invalid.get(session):
+ reason = self.login_invalid.get(session)
+ if reason == "roles":
+ return self.get_string("roles_invalid")
+ elif reason == "credentials":
return self.get_string("login_invalid")
class Submit(FormButton):
@@ -178,53 +188,52 @@
self.add_field(self.new1)
def validate(self, session):
- super(ChangePasswordForm, self).validate(session)
-
- current = self.current.get(session)
- new0 = self.new0.get(session)
- new1 = self.new1.get(session)
-
user = session.client_session.attributes["login_session"].user
-
# In case a different login session for this user has made
# changes, refresh the user object
+ if hasattr(user, "load"):
+ user.load(session.cursor)
+
+ new0 = self.new0.get(session)
+ new1 = self.new1.get(session)
- user.load(session.cursor)
-
- crypted = user.password
-
- if crypt_password(current, crypted) != crypted:
- error = FormError("The password is incorrect")
- self.errors.add(session, error)
-
if new0 != new1:
error = FormError("The new passwords do not match")
self.errors.add(session, error)
+ return
+ super(ChangePasswordForm, self).validate(session)
+
+ # authenticator.update_password does authentication
+ # before allowing the password change, no need to authenticate here
+
def process_submit(self, session):
self.validate(session)
if not self.errors.get(session):
- password = self.new0.get(session)
-
- conn = self.app.database.get_connection()
-
+ newpassword = self.new0.get(session)
+ oldpassword = self.current.get(session)
+ user = session.client_session.attributes["login_session"].user
try:
- cursor = conn.cursor()
+ status, message = self.app.authenticator.update_password(user.name,
+ oldpassword,
+ newpassword)
+ if not status:
+ if not message:
+ message = "Update failed."
+ error = FormError(message)
+ self.errors.add(session, error)
- user = session.client_session.attributes["login_session"].user
- user.password = crypt_password(password)
- user.save(cursor)
+ except Exception,e:
+ log.debug("Error in external authenticator: %s" , e )
+ error = FormError("Password change error. Please contact your site administrator.")
+ self.errors.add(session,error)
- conn.commit()
- finally:
- conn.close()
-
+ if not self.errors.get(session):
task = ObjectTask(self.app)
invoc = task.start(session, None)
invoc.description = "Password changed"
invoc.end()
-
url = self.return_url.get(session)
self.page.redirect.set(session, url)
Modified: branches/stability/cumin/python/cumin/account/widgets.strings
===================================================================
--- branches/stability/cumin/python/cumin/account/widgets.strings 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/account/widgets.strings 2012-05-31 14:49:15 UTC (rev 5395)
@@ -77,3 +77,7 @@
<p class="login_invalid">The user name and password you entered do not
match any account.</p>
+[LoginForm.roles_invalid]
+<p class="login_invalid">There are no valid roles
+configured for this account.</p>
+
Modified: branches/stability/cumin/python/cumin/admin.py
===================================================================
--- branches/stability/cumin/python/cumin/admin.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/admin.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -1,12 +1,20 @@
from StringIO import StringIO
from util import *
-from psycopg2 import IntegrityError
+from rosemary.sqlquery import SqlQuery, SqlInnerJoin
+from rosemary.sqlfilter import SqlComparisonFilter
+from psycopg2 import IntegrityError, ProgrammingError
log = logging.getLogger("cumin.admin")
schema_version = "1.1"
+class SchemaMissing(Exception):
+ pass
+
+class SchemaVersion(Exception):
+ pass
+
class CuminAdmin(object):
def __init__(self, app):
self.app = app
@@ -34,9 +42,13 @@
def get_schema_version(self, cursor):
cls = self.app.model.com_redhat_cumin.Info
- info = cls.get_object(cursor)
+ try:
+ info = cls.get_object(cursor)
+ except ProgrammingError:
+ info = None
+
if not info:
- raise Exception("The schema isn't there")
+ raise SchemaMissing("The schema isn't there")
return info.schema_version
def get_target_schema_version(self):
@@ -44,19 +56,28 @@
def check_schema(self, cursor):
cls = self.app.model.com_redhat_cumin.Info
- info = cls.get_object(cursor)
+ try:
+ info = cls.get_object(cursor)
+ except ProgrammingError:
+ info = None
if not info:
- raise Exception("The schema isn't there")
+ raise SchemaMissing("The schema isn't there")
if info.schema_version != schema_version:
args = (schema_version, info.schema_version)
msg = "Expected schema version %s; found version %s" % args
- raise Exception(msg)
+ raise SchemaVersion(msg)
return info.schema_version
def drop_schema(self, cursor):
+ cls = self.app.model.com_redhat_cumin.Info
+ try:
+ info = cls.get_object(cursor)
+ except ProgrammingError:
+ raise SchemaMissing("The schema isn't there")
+
writer = StringIO()
self.app.model.sql_model.write_drop_ddl(writer)
sql = writer.getvalue()
@@ -116,3 +137,26 @@
return mapping
+ def get_roles_for_user(self, cursor, user):
+ user_cls = self.app.model.com_redhat_cumin.User
+ role_cls = self.app.model.com_redhat_cumin.Role
+ mapping_cls = self.app.model.com_redhat_cumin.UserRoleMapping
+
+ query = SqlQuery(role_cls.sql_table)
+
+ SqlInnerJoin(query,
+ mapping_cls.sql_table,
+ mapping_cls.role.sql_column,
+ role_cls._id.sql_column)
+
+ SqlInnerJoin(query,
+ user_cls.sql_table,
+ user_cls._id.sql_column,
+ mapping_cls.user.sql_column)
+ #TODO: one column ?
+ cols = [ role_cls.name.sql_column ]
+ filt = SqlComparisonFilter(user_cls._id.sql_column, user._id, '=')
+ query.add_filter(filt)
+ sql = query.emit(cols)
+ cursor.execute(sql)
+ return [x[0] for x in cursor.fetchall()]
Copied: branches/stability/cumin/python/cumin/authenticator.py (from rev 4971, trunk/cumin/python/cumin/authenticator.py)
===================================================================
--- branches/stability/cumin/python/cumin/authenticator.py (rev 0)
+++ branches/stability/cumin/python/cumin/authenticator.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -0,0 +1,558 @@
+from util import *
+try:
+ import ldap
+ import ldapurl
+ have_ldap = True
+except:
+ have_ldap = False
+
+import subprocess
+from subprocess import PIPE
+from psycopg2 import IntegrityError
+import syslog
+
+# Send sensitive authentication logs to syslog
+# but use this for other stuff. We want to keep
+# all user and password information out of the
+# publically readable log file.
+log = logging.getLogger("cumin.authenticator")
+
+
+class _syslog(object):
+ log_auth = False
+ @classmethod
+ def log(cls, msg):
+ if _syslog.log_auth:
+ syslog.syslog(msg)
+
+class CuminAuthenticatorLDAP(object):
+ def __init__(self, url, app):
+ self.valid = False
+ log.info("Initializing %s", self)
+ scheme, myurl = url.split("=",1)
+ err_msg = ""
+
+ self.tls_cacertdir = app.ldap_tls_cacertdir
+ self.tls_cacertfile = app.ldap_tls_cacertfile
+ self.ldap_timeout = app.ldap_timeout
+ has_cert = self.tls_cacertdir or self.tls_cacertfile
+
+ if has_cert and not ldap.TLS_AVAIL:
+ err_msg = "Authenticator: SSL is not supported by this python-ldap module."
+
+ elif not has_cert and scheme == "ldaps":
+ err_msg = "Authenticator: scheme is ldaps but no tls_cacertdir set."
+
+ else:
+ if self.tls_cacertdir:
+ if not os.path.isdir(self.tls_cacertdir):
+ err_msg = "Authenticator: '%s' is not a valid directory." \
+ % self.tls_cacertdir
+ else:
+ try:
+ os.listdir(self.tls_cacertdir)
+ except Exception, e:
+ err_msg = "Authenticator, exception reading " \
+ "tls_cacertdir, %s" % e
+
+ if not err_msg and self.tls_cacertfile:
+ if not os.path.isfile(self.tls_cacertfile):
+ err_msg = "Authenticator: '%s' is not a valid file." \
+ % self.tls_cacertfile
+ else:
+ try:
+ open(self.tls_cacertfile, "r")
+ except Exception, e:
+ err_msg = "Authenticator, exception reading " \
+ "tls_cacertfile, %s" % e
+ if not err_msg:
+ # If the scheme is just "ldap" but cert dir is set then we will use
+ # the StartTLS mechanism for password binds
+ self.start_tls = has_cert and scheme == "ldap"
+ if scheme == "ldap":
+ log.info("Authenticator: using StartTLS for ldap: %s" % \
+ self.start_tls)
+ try:
+ self.url = ldapurl.LDAPUrl(myurl,
+ scope=ldapurl.LDAP_SCOPE_SUBTREE,
+ filterstr="uid=%s")
+ log.info("Authenticator: parsed LDAP url successfully, " \
+ "scheme is %s" % scheme)
+ self.valid = True
+ except Exception,e:
+ err_msg = "Authenticator: cannot parse LDAP url %s" % e
+
+ if err_msg:
+ log.error(err_msg)
+
+ def _set_ldap_options(self):
+ if self.tls_cacertdir:
+ ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, self.tls_cacertdir)
+
+ if self.tls_cacertfile:
+ ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, self.tls_cacertfile)
+
+ if self.tls_cacertfile or self.tls_cacertdir:
+ # Always demand a certificate if the cacert is set
+ ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND)
+
+ if self.ldap_timeout is not None:
+ ldap.set_option(ldap.OPT_TIMEOUT, self.ldap_timeout)
+
+ def _start_tls(self, conn):
+ if self.start_tls and not conn.start_tls_called:
+ conn.start_tls_called = True
+ log.debug("Authenticator: calling conn.start_tls_s() on ldap connection")
+ try:
+ conn.start_tls_s()
+ except Exception, e:
+ log.error("Authenticator: StartTLS failed, %s" % e)
+ return False
+ return True
+
+ def __prep_con(self):
+ try:
+ self._set_ldap_options()
+ conn = ldap.initialize(self.url.urlscheme + "://" +self.url.hostport)
+ conn.start_tls_called = False
+ except Exception,e:
+ log.error("Authenticator: cannot contact ldap server %s",e)
+ return False
+
+ if self.url.who and self.url.cred:
+ if not self._start_tls(conn):
+ return False
+ try:
+ conn.simple_bind_s(self.url.who, self.url.cred)
+ except:
+ log.error("Authenticator: cannot authenticate as service %s",
+ self.url.who)
+ return False
+ return conn
+
+ def _gen_filter(self, filterstr, value):
+ count = filterstr.count("%s")
+ if count > 0:
+ vals = (value,) * count
+ return filterstr % vals
+ return filterstr
+
+ def _find_user(self, user):
+ res = False
+ conn = self.__prep_con()
+ if conn:
+ filter = self._gen_filter(self.url.filterstr, user)
+ try:
+ res = conn.search_s(self.url.dn, self.url.scope, filter)
+ except Exception, e:
+ msg = str(e)
+ if not msg:
+ msg = e.__repr__()
+ log.error("Authenticator: find_user, "\
+ "query returned exception %s" % msg)
+ return conn, res
+
+ def find_user(self, user):
+ conn, res = self._find_user(user)
+ if res:
+ return True
+ return False
+
+ def change_pw(self, user, oldpass, newpass):
+ conn, res = self._find_user(user)
+ if not res:
+ log.info("Authenticator: update password, "\
+ "query returned no results in %s", self.__class__.__name__)
+ else:
+ res = False
+ if self._start_tls(conn):
+ dn,ent = res[0]
+ conn.simple_bind_s(dn,oldpass)
+ conn.passwd_s(dn,oldpass,newpass)
+ log.info("Authenticator: update password succeeded in %s",
+ self.__class__.__name__)
+ _syslog.log("cumin: updated password via LDAP for user %s" % dn)
+ res = True
+ return res
+
+ def batch_import(self):
+ log.debug("Authenticator: batch import in %s", self.__class__.__name__)
+ userlist = res = []
+ conn = self.__prep_con()
+ error = not conn
+ if not error:
+ filter = self._gen_filter(self.url.filterstr, "*")
+ try:
+ res = conn.search_s(self.url.dn, self.url.scope, filter)
+ except Exception, e:
+ log.error("Authenticator:, batch import, "\
+ "query returned exception %s", e)
+ error = True
+ if not res:
+ log.info("Authenticator: batch import failed, "\
+ "query returned no results in %s", self.__class__.__name__)
+ else:
+ for dn,ent in res:
+ dn_arr = ldap.dn.explode_rdn(dn)
+ naming_attr = dn_arr[0].split('=')[0]
+ userlist.append(ent[naming_attr][0])
+ return userlist, error
+
+ def authenticate(self, username, password):
+ log.debug("Authenticating against %s", self.__class__.__name__)
+ conn, res = self._find_user(username)
+ if not res:
+ log.info("Authenticator: authentication failed, "\
+ "query returned no results in %s", self.__class__.__name__)
+ else:
+ # We need to check for zero length passwords here and disallow.
+ # This is because an LDAP directory server MAY allow unauthenticated
+ # binds, which are indicated by a zero length password. Red Hat Directory
+ # Services turns unauthenticated binds off by default, for example,
+ # but who knows how this particular server may be configured?
+
+ # If unauthenticated binds are allowed by the server, then simple_bind_s
+ # below will succeed with a zero lengh password and cumin will think that
+ # password authentication has succeeded when it really hasn't.
+ if password == "":
+ log.error("Authenticator: zero length password is not allowed for "\
+ "ldap users")
+ return False
+
+ if self._start_tls(conn):
+ for dn,ent in res:
+ try:
+ conn.simple_bind_s(dn,password)
+ _syslog.log("cumin: authenticated user %s via LDAP" % dn)
+ return True
+ except Exception, e:
+ log.info("Authenticator: LDAP authentication "\
+ "returned exception %s" % e)
+ return False
+
+class CuminAuthenticatorScript(object):
+ def __init__(self, commandline, *args):
+ self.valid = False
+ self.params = {}
+ self.cap = []
+ me = self.__class__.__name__
+ for a in commandline.split(','):
+ # Allow capability with a null value
+ args = a.split('=')
+ if len(args) == 1:
+ args.append("")
+ param,val = args
+ self.params[param] = val
+
+ if self.params.has_key('script') and \
+ os.access(self.params['script'], os.X_OK):
+
+ # Allow default with all operations and no specific
+ # parameters per operation. The convention here is
+ # to store the first letter in self.cap, but anything
+ # unambiguous would do.
+ if len(self.params) == 1:
+ self.cap = ['a','c','l','f']
+ log.debug("Authenticator: adding all capabilities to %s", me)
+ else:
+ msg = "Authenticator: adding %s capability to %s"
+ for cap in ("auth", "change", "list", "find"):
+ if self.params.has_key(cap):
+ log.debug(msg % (cap, me))
+ self.cap.append(cap[0])
+ self.valid = True
+ else:
+ log.error('Authenticator: script parameter missing or target file '\
+ 'not executable in %s', me)
+
+ def _gen_input(self, op, *args):
+ if op in self.params and len(self.params[op]) > 0:
+ args = (self.params[op],)+args
+ return ",".join(["%s" % x for x in args])
+
+ def authenticate(self, username, password):
+ me = self.__class__.__name__
+ if 'a' in self.cap:
+ log.debug("Authenticator: authenticate in %s" % me)
+ # Use nameless pipes to pass parameters.
+ # If they are passed as arguments they show up in the output
+ # of ps, etc. This way is secure.
+ # Note, the call to communicate causes an EOF on stdin
+ try:
+ cmd = [self.params['script']]
+ args = self._gen_input("auth", username, password)
+ res = subprocess.Popen(cmd,
+ stdin=PIPE, stdout=PIPE, stderr=PIPE)
+ out, err = res.communicate(input=args)
+ log.debug("Authenticator: authenticate, script returned "\
+ "%s, %s, exitcode %s", out, err, res.returncode)
+ except:
+ log.error("Authenticator: authenticate, "\
+ "exception running script, return False")
+ return False
+
+ if res.returncode == 0:
+ _syslog.log("cumin: authenticated user %s via script" % username)
+ return True
+ else:
+ log.debug("Authenticator: 'auth' not supported by script")
+ return False
+
+ def find_user(self, username):
+ me = self.__class__.__name__
+ if 'f' in self.cap:
+ log.debug("Authenticator: executing find_user in %s" % me)
+ # Use nameless pipes to pass parameters.
+ # If they are passed as arguments they show up in the output
+ # of ps, etc. This way is secure.
+ # Note, the call to communicate causes an EOF on stdin
+ try:
+ cmd = [self.params['script']]
+ args = self._gen_input("find", username)
+ res = subprocess.Popen(cmd,
+ stdin=PIPE, stdout=PIPE, stderr=PIPE)
+ out, err = res.communicate(input=args)
+ log.debug("Authenticator: find_user, script returned "\
+ "%s, %s, exitcode %s", out, err, res.returncode)
+ except:
+ log.error("Authenticator: find_user, "\
+ "exception running script, return False")
+ return False
+
+ if res.returncode == 0:
+ return True
+ else:
+ log.debug("Authenticator: 'find' not supported by script")
+ return False
+
+ def batch_import(self):
+ if 'l' in self.cap:
+ log.debug("Authenticator: batch import in %s",
+ self.__class__.__name__)
+ # Use nameless pipes to pass parameters.
+ # If they are passed as arguments they show up in the output
+ # of ps, etc. This way is secure.
+ # Note, the call to communicate causes an EOF on stdin
+ try:
+ cmd = [self.params['script']]
+ args = self._gen_input("list")
+ res = subprocess.Popen(cmd,
+ stdin=PIPE, stdout=PIPE, stderr=PIPE)
+ out, err = res.communicate(input=args)
+ log.debug("Authenticator: batch import, script returned "\
+ "%s, %s, exitcode %s", out, err, res.returncode)
+ except:
+ log.error("Authenticator: batch_import, "\
+ "exception running script, return False")
+ return []. False
+
+ return out.split(','), False
+ else:
+ log.debug("Authenticator: 'list' not supported by script")
+ return [], False
+
+ def change_pw(self, user, newpass, oldpass):
+ if 'c' in self.cap:
+ me = self.__class__.__name__
+ log.debug("Authenticator: change password in %s" % me)
+
+ # Use nameless pipes to pass parameters.
+ # If they are passed as arguments they show up in the output
+ # of ps, etc. This way is secure.
+ # Note, the call to communicate causes an EOF on stdin
+ try:
+ cmd = [self.params['script']]
+ args = self._gen_input("change", user, newpass, oldpass)
+ res = subprocess.Popen(cmd,
+ stdin=PIPE, stdout=PIPE, stderr=PIPE)
+ out, err = res.communicate(args)
+ log.debug("Authenticator: change password, script returned "\
+ "%s, %s, exitcode %s", out, err, res.returncode)
+ except:
+ log.error("Authenticator: change_pw, "\
+ "exception running script, return False")
+ return False
+
+ if res.returncode == 0:
+ _syslog.log("cumin: change password via script for user %s" % user)
+ return True
+ else:
+ log.debug("Authenticator: 'change' not supported by script")
+ return False
+
+if have_ldap:
+ authen_map = {'ldap': CuminAuthenticatorLDAP,
+ 'ldaps': CuminAuthenticatorLDAP,
+ 'script': CuminAuthenticatorScript}
+else:
+ authen_map = {'script': CuminAuthenticatorScript}
+
+class CuminAuthenticator(object):
+ def __init__(self, app, log_authentication=True):
+ self.authenticators=[]
+ self.app = app
+ _syslog.log_auth = log_authentication
+ log.info("Initializing %s", self)
+ if not have_ldap:
+ log.info("Authenticator: import of ldap modules failed, "\
+ "ldap mechanism not available")
+ for authmech in app.authmech:
+ # let the 'internal' word be used here since it has been
+ # on the trunk for a while. It means nothing, since the
+ # internal database will always be consulted first.
+ if authmech not in ("internal", ""):
+ # Allow initial ldap= to be skipped
+ try:
+ if authmech.startswith("ldap://"):
+ mechname = "ldap"
+ authmech = "ldap=" + authmech
+ elif authmech.startswith("ldaps://"):
+ mechname = "ldaps"
+ authmech = "ldaps=" + authmech
+ else:
+ mechname = authmech.split('=',1)[0]
+ classname = authen_map[mechname]
+ except KeyError:
+ log.error("Authenticator: unsupported authenticator type %s",
+ mechname)
+ continue
+
+ c = classname(authmech, self.app)
+ if c.valid:
+ self.authenticators.append(c)
+ log.info("Authenticator: adding auth mechanism %s %s" \
+ % (mechname, c.valid and "succeeded" or "failed"))
+
+ def batch_import(self, backend, output_on=False):
+ numusers = 0
+ numconflicts = 0
+ errors = False
+ import_list = []
+ log.debug("Authenticator: calling batch import")
+ try:
+ classname = authen_map[backend]
+ except KeyError:
+ log.error("Authenticator: batch import, unsupported authenticator "\
+ "type '%s'", backend)
+ errors = True
+
+ if not errors:
+ try:
+ idx = [x.__class__.__name__ for x in self.authenticators]
+ idx = idx.index(classname.__name__)
+ except ValueError:
+ log.error("Authenticator: batch import, authenticator "\
+ "not configured '%s'", backend)
+ errors = True
+
+ if not errors:
+ users, errors = self.authenticators[idx].batch_import()
+ for imp in users:
+ import_list.append(imp)
+
+ if len(import_list) > 0:
+ try:
+ conn = self.app.database.get_connection()
+ cursor = conn.cursor()
+ role = self.app.admin.get_role(cursor, "user")
+ for importuser in import_list:
+ try:
+ user = self.app.admin.add_user(cursor, importuser, "")
+ if output_on:
+ print("Importing user %s" % importuser)
+ self.app.admin.add_assignment(cursor, user, role)
+ conn.commit()
+ numusers += 1
+ except IntegrityError:
+ if output_on:
+ print("Import failed, a user called '%s' "\
+ "already exists" % importuser)
+ numconflicts += 1
+ conn.rollback()
+ finally:
+ conn.close()
+ return numusers, numconflicts, errors
+
+ def authenticate(self, username, password):
+ cursor = self.app.database.get_read_cursor()
+ cls = self.app.model.com_redhat_cumin.User
+ user = cls.get_object(cursor, name=username)
+ if user is None or len(user.password) == 0:
+ res = False
+ log.debug("Authenticator: authenticating external user")
+ if len(self.authenticators) == 0:
+ log.debug("Authenticator: no external mechanisms available")
+ for authenticator in self.authenticators:
+ log.debug("Authenticator: authenticate, try %s", authenticator)
+ res = authenticator.authenticate(username, password)
+ if res:
+ break
+ log.debug("Authenticator: authentication %s for external user" \
+ % (res and "succeeded" or "failed"))
+ msg = "cumin: authentication %s for external user %s" \
+ % (res and "succeeded" or "failed", username)
+ else:
+ res = crypt(password, user.password) == user.password
+ log.debug("Authenticator: authentication %s for user" \
+ % (res and "succeeded" or "failed"))
+ msg = "cumin: authentication %s for user %s" \
+ % (res and "succeeded" or "failed", username)
+ _syslog.log(msg)
+ return user, res
+
+ def find_user(self, username):
+ # Like authenticate, but find_user only confirms the
+ # existence of a user. We are not concerned with password here.
+ log.debug("Authenticator: calling find_user")
+ cursor = self.app.database.get_read_cursor()
+ cls = self.app.model.com_redhat_cumin.User
+ user = cls.get_object(cursor, name=username)
+ res = user and len(user.password) > 0
+ if not res:
+ # Try to find the user externally
+ log.debug("Authenticator: finding external user")
+ if len(self.authenticators) == 0:
+ log.debug("Authenticator: no external mechanisms available")
+ for authenticator in self.authenticators:
+ log.debug("Authenticator: find_user, try %s", authenticator)
+ res = authenticator.find_user(username)
+ if res:
+ break
+ return user, res
+
+ def update_password(self, username, oldpassword, newpassword):
+ status = False
+ message = ""
+ log.debug("Authenticator: calling update_password")
+ conn = self.app.database.get_connection()
+ try:
+ cursor = conn.cursor()
+ cls = self.app.model.com_redhat_cumin.User
+ user = cls.get_object(cursor, name=username)
+ if user is None or user.password == "":
+ # Disable change password for external users
+ message = "Change password is not allowed for external users"
+
+ #for authenticator in self.authenticators:
+ # status = authenticator.authenticate(username, oldpassword)
+ # if status:
+ # status = authenticator.change_pw(username,
+ # oldpassword, newpassword)
+ # if not status:
+ # message = "Could not change password"
+ # break
+ #if not status and message == "":
+ # message = "The password is incorrect"
+
+ else:
+ status = crypt(oldpassword, user.password) == user.password
+ if status:
+ user.password = crypt_password(newpassword)
+ user.save(cursor)
+ conn.commit()
+ _syslog.log("cumin: updated password for user %s" % username)
+ else:
+ message = "The password is incorrect"
+ finally:
+ conn.close()
+ return status, message
Modified: branches/stability/cumin/python/cumin/config.py
===================================================================
--- branches/stability/cumin/python/cumin/config.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/config.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -51,10 +51,15 @@
param = ConfigParameter(web, "persona", str)
param.default = "default"
-
+
param = ConfigParameter(web, "fast-view-attributes", str)
param.default = "JobStatus,Cmd,Args,ExitStatus,JobStartDate,LastRemoteHost,LastJobStatus,Owner"
+ # Turn this into a boolean, default is off for introduction.
+ # Hardwire the path to persona.xml unless/until we allow overriding
+ param = ConfigParameter(web, "authorize", bool)
+ param.default = False
+
def create_data_section(self, name, strict_section):
data = CuminConfigSection(self, name, strict_section)
data.log_file.default = os.path.join(self.home, "log", name + ".log")
@@ -94,6 +99,9 @@
def get_home(self):
return self.home
+ def get_access_root(self):
+ return os.path.join(self.home, "model/access/")
+
def parse(self):
paths = list()
@@ -145,6 +153,20 @@
param = ConfigParameter(self, "brokers", str)
param.default = "amqp://localhost"
+ # Put this here, because authentication is something that
+ # might need to be done commonly
+ param = ConfigParameter(self, "auth", str)
+ param.default = ""
+
+ param = ConfigParameter(self, "ldap_tls_cacertfile", str)
+ param.default = ""
+
+ param = ConfigParameter(self, "ldap_tls_cacertdir", str)
+ param.default = ""
+
+ param = ConfigParameter(self, "ldap_timeout", int)
+ param.default = 30
+
# Leave default set to None, which is equivalent to
# previous behavior
param = ConfigParameter(self, "sasl-mech-list", str)
@@ -161,6 +183,12 @@
param = ConfigParameter(self, "aviary-query-servers", str)
param.default = ""
+ param = ConfigParameter(self, "server-cert", str)
+ param.default = ""
+
+ param = ConfigParameter(self, "server-key", str)
+ param.default = ""
+
param = ConfigParameter(self, "aviary-key", str)
param.default = ""
Modified: branches/stability/cumin/python/cumin/database.py
===================================================================
--- branches/stability/cumin/python/cumin/database.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/database.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -16,11 +16,37 @@
self.connection_args = dict()
self.thread_local = threading.local()
- def init(self):
+ def init(self, schema_version_check):
log.info("Initializing %s", self)
#m = re.match(r"^([^:]+)://([^@]+)@([^/]+)/(.+)$", self.uri)
+ conn = None
+ try:
+ conn = self.get_connection()
+ cursor = conn.cursor()
+ self.check_connection(cursor)
+ if schema_version_check:
+ self.app.admin.check_schema(cursor)
+ conn.close()
+ except psycopg2.OperationalError:
+ conn and conn.close()
+ log.error("Connection to database failed, is the database running?")
+ raise
+ except Exception, e:
+ conn and conn.close()
+ log.error(str(e))
+ raise
+ def check(self):
+ # checks moved to init because the model has to be
+ # initialized before we can check the schema. And
+ # there is no point connecting to the database twice...
+ log.info("Checking %s", self)
+
+ def check_connection(self, cursor):
+ cursor.execute("select now()")
+ log.debug("Database is talking at '%s'", self.dsn)
+
def get_connection(self):
return psycopg2.connect(self.dsn)
Modified: branches/stability/cumin/python/cumin/grid/dashboard.py
===================================================================
--- branches/stability/cumin/python/cumin/grid/dashboard.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/grid/dashboard.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -15,7 +15,7 @@
from cumin.objectselector import ObjectSelector, MonitorSelfStatColumn, ObjectTableColumn, ObjectTable,\
ObjectLinkColumn, CsvStatsExporter, ExportButton, MonitorSelfAgeColumn
from cumin.parameters import YoungestAttribute
-from cumin.util import rgb_to_string
+from cumin.util import rgb_to_string, xml_escape
strings = StringCatalog(__file__)
log = logging.getLogger("cumin.dashboard")
@@ -539,10 +539,11 @@
def get_item_unit(self, session, item):
return item.unit
- def render_formatted_value(self, session, item, value):
+ def render_formatted_value(self, session, item, value, escape=False):
if item is self.PERCENT_COLUMN:
return "%0.02f%%" % value
- return super(DashboardCapacitySlotSummary, self).render_formatted_value(session, item, value)
+ return super(DashboardCapacitySlotSummary,
+ self).render_formatted_value(session, item, value, escape)
def render_html_title(self, session, item):
if item is self.PERCENT_COLUMN:
@@ -689,7 +690,7 @@
return record[1]
def render_item_value(self, session, item):
- return self.get_item_value(session, item)
+ return xml_escape(self.get_item_value(session, item))
def render_html_title(self, session, item):
(_, (name, value)) = item
Modified: branches/stability/cumin/python/cumin/grid/job.py
===================================================================
--- branches/stability/cumin/python/cumin/grid/job.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/grid/job.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -14,8 +14,9 @@
from cumin.formats import fmt_datetime, fmt_link
from wooly import Widget, Parameter, Attribute
-from wooly.util import StringCatalog, Writer, escape_amp, escape_entity
-from wooly.forms import Form, FormButton, StringField
+from wooly.util import StringCatalog, Writer, escape_amp, escape_entity,\
+ xml_escape
+from wooly.forms import Form, FormButton, StringField, NoXMLStringField
from wooly.widgets import ModeSet, PropertySet, TemplateRenderer
from wooly.template import WidgetTemplate
from wooly.parameters import ListParameter, IntegerParameter, DictParameter
@@ -313,7 +314,7 @@
def get_item_content(self, session, item):
# item here is the unicode job id
- return item
+ return xml_escape(item)
class JobSelectionHold(JobObjectSelectorTask):
def __init__(self, app, selector, verb):
@@ -1109,7 +1110,7 @@
self.task.invoke(session, scheduler, job_id, reason, submission)
self.task.exit_with_redirect(session)
-class ReasonField(StringField):
+class ReasonField(NoXMLStringField):
def render_title(self, session):
return "Reason"
Modified: branches/stability/cumin/python/cumin/grid/limit.py
===================================================================
--- branches/stability/cumin/python/cumin/grid/limit.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/grid/limit.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -79,7 +79,11 @@
def render_text_align(self, session):
return "right"
- class MaxColumn(ObjectTableColumn):
+ class MaxColumn(ObjectTableColumn):
+ def __init__(self, app, name, attr):
+ super(LimitTable.MaxColumn, self).__init__(app, name, attr)
+ self.do_escape = False
+
def render_text_align(self, session):
return "right"
Modified: branches/stability/cumin/python/cumin/grid/main.py
===================================================================
--- branches/stability/cumin/python/cumin/grid/main.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/grid/main.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -26,6 +26,8 @@
super(Module, self).init()
self.frame = PoolFrame(self.app, "grid")
+ self.frame.cumin_module = self.name
+
self.app.main_page.main.grid = self.frame
self.app.main_page.main.add_tab(self.frame)
Modified: branches/stability/cumin/python/cumin/grid/negotiator.py
===================================================================
--- branches/stability/cumin/python/cumin/grid/negotiator.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/grid/negotiator.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -186,8 +186,11 @@
info = self.get_config_for_groups(session, config, [group])
return info[group][config]
except:
- return "<em>loading</em>"
+ return self.loading_indicator()
+ def loading_indicator(self):
+ return "<em>loading</em>"
+
def get_unclaimed_dyn_quota(self, session, groups):
info = self.get_config_info(session)
total = 0.0
Modified: branches/stability/cumin/python/cumin/grid/pool.py
===================================================================
--- branches/stability/cumin/python/cumin/grid/pool.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/grid/pool.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -78,6 +78,7 @@
self.tag = TagsFrame(app, "tag")
self.add_mode(self.tag)
+ self.tag.cumin_module = "configuration"
dashboard = PoolDashboard(app, "dashboard", self.object)
self.view.add_tab(dashboard)
Modified: branches/stability/cumin/python/cumin/grid/quota.py
===================================================================
--- branches/stability/cumin/python/cumin/grid/quota.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/grid/quota.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -7,6 +7,7 @@
ObjectQmfTable
from cumin.qmfadapter import ObjectQmfAdapter
from cumin.widgets import StaticColumnHeader
+from cumin.util import xml_escape
from wooly.template import WidgetTemplate
from wooly.util import StringCatalog, Writer
@@ -29,6 +30,7 @@
self.negotiator = negotiator
self.task = task
self.user_task = None
+ self.do_escape = False
def render_title(self, session, *args):
return self.title
@@ -176,6 +178,7 @@
self.expand = expand
self.group_helper = group_helper
+ self.do_escape = False
def get_class_list(self, session):
return ["expand"]
@@ -225,6 +228,10 @@
self.header = StaticColumnHeader(app, "header")
self.replace_child(self.header)
+ # Turn off escape in the parent because although we
+ # have to escape here, it's based on context
+ self.do_escape = False
+
def render_cell_content(self, session, group):
if session.page == self.app.export_page:
return group
@@ -234,16 +241,17 @@
parts = group.split('.')
indent = len(parts) - 1
user = parts[indent]
- return "<span style='padding-left: %dem;'>%s</span>" % (indent, user)
+ return "<span style='padding-left: %dem;'>%s</span>" %\
+ (indent, xml_escape(user))
- return group
+ return xml_escape(group)
class DynamicColumn(QmfGroupColumn):
def __init__(self, app, name, attr, getter, negotiator, task, group_helper):
super(QuotaSelector.DynamicColumn, self).__init__(app, name, attr, getter, negotiator, task)
self.group_helper = group_helper
-
+
def render_cell_content(self, session, group):
value = self.group_helper.get_config_value(session, group, "GROUP_QUOTA_DYNAMIC")
self.task.form.group_leader.set(session, group)
@@ -251,11 +259,13 @@
try:
fval = float(value)
except ValueError:
- return value
+ if value == self.group_helper.loading_indicator():
+ return value
+ return xml_escape(value)
except:
if isinstance(value, Exception):
content = "<span class='QuotaError'>error</span>"
- val = len(value.args) > 0 and value.args[0] or ""
+ val = len(value.args) > 0 and xml_escape(value.args[0]) or ""
if session.page == self.app.export_page:
return val
return fmt_link("#", content, "", "", val)
@@ -282,18 +292,19 @@
try:
fval = float(value)
except ValueError:
- return value
+ if value == self.group_helper.loading_indicator():
+ return value
+ return xml_escape(value)
except:
if isinstance(value, Exception):
content = "<span class='QuotaError'>error</span>"
- val = len(value.args) > 0 and value.args[0] or ""
+ val = len(value.args) > 0 and xml_escape(value.args[0]) or ""
if session.page == self.app.export_page:
return val
return fmt_link("#", content, "", "", val)
if session.page == self.app.export_page:
return value
-
content = "%s" % str(round(fval, 2))
return content
Modified: branches/stability/cumin/python/cumin/grid/scheduler.py
===================================================================
--- branches/stability/cumin/python/cumin/grid/scheduler.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/grid/scheduler.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -123,7 +123,7 @@
"MonitorSelfImageSize","MonitorSelfRegisteredSocketCount",
"MonitorSelfResidentSetSize","MonitorSelfTime")
- def render_formatted_value(self, session, item, value):
+ def render_formatted_value(self, session, item, value, escape=False):
def fmt_b(value):
return value is not None and fmt_bytes(value) or None
@@ -147,14 +147,14 @@
elif format == "KiB":
return fmt_kb(value)
name = self.get_item_name(session, item)
- # Since condor.xml doesn't have a unit for this, we will improvise a bit.
- # In a perfect world, this might use the same Rosemary code that Negotiator uses
+ # Since condor.xml doesn't have a unit for this, we improvise a bit.
+ # This could perhaps use the same Rosemary code as Negotiator.
if(name in ["MonitorSelfAge"]):
return fmt_timestamp_ddhhmmss(value)
except:
pass
- return CuminStatistic.fmt_value(value)
+ return CuminStatistic.fmt_value(value, escape)
def get_item_name(self, session, item):
the_item, _ = item
Modified: branches/stability/cumin/python/cumin/grid/submission.py
===================================================================
--- branches/stability/cumin/python/cumin/grid/submission.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/grid/submission.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -10,13 +10,14 @@
ObjectTableColumn, ObjectTable
from cumin.task import TaskLink, Task, ObjectTaskForm
from cumin.parameters import ObjectAttribute
+from cumin.util import xml_escape
from rosemary.sqlfilter import SqlFilter
from rosemary.sqlquery import SqlInnerJoin
from wooly import Attribute, Parameter
from wooly.forms import StringField, ScalarField, FormError, OptionInputSet,\
- MultilineStringField, FormField, IntegerField
+ MultilineStringField, FormField, IntegerField, NoXMLStringField
from wooly.parameters import IntegerParameter
log = logging.getLogger("cumin.grid.submission")
@@ -60,7 +61,7 @@
def render_auxtitle(self, session):
sch = self.get_scheduler(session)
- return "Scheduler '%s'" % sch.Name
+ return "Scheduler '%s'" % xml_escape(sch.Name)
class SubmissionData(ObjectSqlAdapter):
def __init__(self, app):
@@ -203,7 +204,7 @@
self.columns.append(scheduler._id.sql_column)
self.columns.append(scheduler.Name.sql_column)
-class JobDescriptionField(StringField):
+class JobDescriptionField(NoXMLStringField):
def __init__(self, app, name):
super(JobDescriptionField, self).__init__(app, name)
@@ -273,7 +274,7 @@
return item._id
def render_item_content(self, session, item):
- return item.Name
+ return xml_escape(item.Name)
def render_item_selected_attr(self, session, item):
if item._id == self.param.get(session):
Modified: branches/stability/cumin/python/cumin/grid/tags.py
===================================================================
--- branches/stability/cumin/python/cumin/grid/tags.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/grid/tags.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -18,6 +18,7 @@
from cumin.task import TaskLink, Task, ObjectTaskForm
from cumin.widgets import *
from cumin.stat import *
+from cumin.util import xml_escape
from sage.wallaby.wallabyoperations import WallabyOperations, WBTypes
from sage.util import *
@@ -103,7 +104,10 @@
wallaby_features = self.app.wallaby.get_data(WBTypes.FEATURES)
for i, tag in enumerate(wallaby_tags):
- data.append({'Tag':str(escape_entity(tag.name)), 'Features':", ".join(tag.features), 'NumHosts':len(self.app.wallaby.get_node_names(tag)), 'Host':self.app.wallaby.get_node_names(tag)} )
+ data.append({'Tag':str(escape_entity(tag.name)),
+ 'Features':", ".join(tag.features),
+ 'NumHosts':len(self.app.wallaby.get_node_names(tag)),
+ 'Host':self.app.wallaby.get_node_names(tag)} )
except Exception, e:
log.exception(e)
@@ -117,7 +121,10 @@
if results:
for result in results:
#Tag goes in the 0 and 1 column, 0 for checkbox value, 1 for the column value
- records.append([result['Tag'], result['Tag'], result['Features'], result['NumHosts'], result['Host']])
+ records.append([result['Tag'],
+ result['Tag'],
+ result['Features'],
+ result['NumHosts'], result['Host']])
return records
@@ -139,7 +146,9 @@
wallaby_nodes = self.app.wallaby.get_data(WBTypes.NODES)
data = []
for i, node in enumerate(wallaby_nodes):
- data.append({'Host':node.name, 'Tags':self.app.wallaby.get_tag_names(node), 'Checkin':node.last_checkin})
+ data.append({'Host':node.name,
+ 'Tags':self.app.wallaby.get_tag_names(node),
+ 'Checkin':node.last_checkin})
return data
@@ -161,7 +170,7 @@
def render_title(self, session):
obj = self.object.get(session)
if obj is not None:
- return obj.name
+ return xml_escape(obj.name)
else:
return ""
@@ -243,7 +252,7 @@
retval = ""
try:
obj = self.tag.get(session)
- retval = ", ".join(self.app.wallaby.get_node_names(obj))
+ retval = xml_escape(", ".join(self.app.wallaby.get_node_names(obj)))
except Exception, e:
log.debug("Exception in rendering tag hosts, tags probably not loaded yet: %s", e.message)
return retval
@@ -252,7 +261,7 @@
retval = ""
try:
obj = self.tag.get(session)
- retval = ", ".join(obj.features)
+ retval = xml_escape(", ".join(obj.features))
except Exception, e:
log.debug("Exception in rendering tag features, tags probably not loaded yet: %s", e.message)
return retval
@@ -278,7 +287,7 @@
def get_title(self, session):
retval = ""
try:
- retval = "Tag '%s'" % self.object.get(session).name
+ retval = "Tag '%s'" % xml_escape(self.object.get(session).name)
except Exception, e:
pass
return retval
@@ -338,10 +347,10 @@
def render_cell_content(self, session, data):
feature_list = data[2]
features = truncate_text(feature_list, 70, True)
- return features
+ return features
def render_cell_title(self, session, data):
- return data[2]
+ return data[2]
class HostCountColumn(ObjectTableColumn):
def render_cell_content(self, session, data):
@@ -861,7 +870,7 @@
if not value or value =="":
value = "No hosts currently selected"
value = truncate_text(value, 50, True)
- return value
+ return xml_escape(value)
def render_title(self, session):
return "Current hosts"
@@ -980,7 +989,7 @@
value = ""
tag_obj = self.app.wallaby.get_tag_by_name(tag)
if tag_obj is not None:
- value = ", ".join(tag_obj.features)
+ value = xml_escape(", ".join(tag_obj.features))
if not value or value == "":
value = "No features currently selected"
value = truncate_text(value, 50, True)
@@ -1265,7 +1274,7 @@
log.exception(e)
def get_item_content(self, session, item):
- return item
+ return xml_escape(item)
def fetchItems(self, session, type):
'''
@@ -1275,7 +1284,7 @@
item_list = list()
for item in wallaby_items:
- item_list.append(str(item.name))
+ item_list.append(xml_escape(str(item.name)))
item_list.sort()
return item_list
\ No newline at end of file
Modified: branches/stability/cumin/python/cumin/inventory/main.py
===================================================================
--- branches/stability/cumin/python/cumin/inventory/main.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/inventory/main.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -16,6 +16,7 @@
self.frame = InventoryFrame(app, "inventory")
self.app = app
+ self.frame.cumin_module = name
def init(self):
self.app.main_page.main.inventory = self.frame
Modified: branches/stability/cumin/python/cumin/inventory/system.py
===================================================================
--- branches/stability/cumin/python/cumin/inventory/system.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/inventory/system.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -14,7 +14,7 @@
from wooly import Session, Widget
from wooly.datatable import *
-from wooly.util import StringCatalog
+from wooly.util import StringCatalog, xml_escape
strings = StringCatalog(__file__)
@@ -47,9 +47,11 @@
col = SystemTagsColumn(app, "Tags", app.model.com_redhat_cumin_grid.Node.Tags, "Tags")
self.add_column(col)
+ col.cumin_module = "configuration"
col = SystemCheckinColumn(app, "Checkin", app.model.com_redhat_cumin_grid.Node.Checkin, "Checkin")
self.add_column(col)
+ col.cumin_module = "configuration"
self.enable_csv_export()
@@ -74,6 +76,7 @@
configuration = SystemTags(app, "configuration", self.object)
self.view.add_tab(configuration)
+ configuration.cumin_module = "configuration"
def do_process(self, session):
try:
@@ -107,7 +110,7 @@
stat, value = item
if stat.name in self.fmt_as_bytes:
return fmt_bytes(value*1024)
- return CuminStatistic.fmt_value(value)
+ return CuminStatistic.fmt_value(value, escape=True)
class SystemStats(Widget):
def __init__(self, app, name, system):
@@ -194,7 +197,7 @@
tags = []
tags_string = ", ".join(tags)
- return tags_string
+ return xml_escape(tags_string)
def do_render(self, session, *args):
if self.defer_enabled and not getattr(session, "background", None):
@@ -448,6 +451,7 @@
self.attr = attr
self.title = title
self.format_method = getattr(attr, "unit", None)
+ self.cumin_module = "configuration"
def get_field(self):
field = CheckinField(self.table.adapter, self.name)
Modified: branches/stability/cumin/python/cumin/main.py
===================================================================
--- branches/stability/cumin/python/cumin/main.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/main.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -21,6 +21,8 @@
from user import *
from util import *
from widgets import *
+from authenticator import *
+from persona import CuminAuthorizator
from sage.catalog import Catalog
from sage.qmf.qmfoperations import QmfOperations
from sage.wallaby.wallabyoperations import WallabyOperations
@@ -33,18 +35,27 @@
class Cumin(Application):
def __init__(self, home, broker_uris, database_dsn,
- host="localhost", port=45672, persona="default"):
+ host="localhost", port=45672,
+ server_cert="", server_key="",
+ persona="default",
+ authmech=[]):
super(Cumin, self).__init__()
self.home = home
+ self.authmech = authmech
+ # Settings for LDAP authentication
+ self.ldap_timeout = None
+ self.ldap_tls_cacertdir = ""
+ self.ldap_tls_cacertfile = ""
+
model_dir = os.path.join(self.home, "model")
self.model = CuminModel(self, model_dir)
self.session = CuminSession(self, broker_uris)
self.database = CuminDatabase(self, database_dsn)
- self.server = CuminServer(self, host, port)
+ self.server = CuminServer(self, host, port, server_cert, server_key)
self.admin = CuminAdmin(self)
self.add_resource_dir(os.path.join(self.home, "resources-wooly"))
@@ -53,6 +64,10 @@
self.modules = list()
self.modules_by_name = dict()
+ # This is an argument to CuminAuthorizator in init()
+ self.access_path = None
+ self.do_authorize = False
+
self.tasks = list()
self.user = None
@@ -64,14 +79,10 @@
self.form_defaults = self.CuminFormDefaults()
- # Persona methods are used to control what pages and/or
- # modules are present
-
+ # Persona value maps to xml definitions handled
+ # in persona.py
self.persona = persona
- self.personas = dict()
- self.personas["default"] = self._init_default_persona
- self.personas["grid"] = self._init_grid_persona
- self.personas["messaging"] = self._init_messaging_persona
+
self._page_links = list()
# self.model.sql_logging_enabled = True
@@ -107,61 +118,66 @@
raise Exception(msg % self.home)
self.model.check()
+ self.database.check()
+
+ def _init_persona(self, modules):
- def _init_grid_persona(self):
- self.main_page = GridMainPage(self, "index.html")
+ # Determine the view based on which modules are enabled
+ if "messaging" in modules:
+ if "grid" in modules:
+ view = MainView(self, "main")
+ else:
+ view = MessagingMainView(self, "main")
+ else:
+ view = GridMainView(self, "main")
+
+ self.main_page = MainPage(self, "index.html", view)
self.add_page(self.main_page, add_to_link_set=True)
self.set_default_page(self.main_page)
-
- self.add_common_pages()
- import account
- import grid
- import inventory
- import usergrid
+ self.form_page = CuminFormPage(self, "form.html")
+ self.add_page(self.form_page)
- account.Module(self, "account")
- grid.Module(self, "grid")
- inventory.Module(self, "inventory")
- usergrid.Module(self, "usergrid")
+ self.add_page(StatFlashPage(self, "chart.json"))
+ self.add_page(FlashFullPage(self, "flashpage.html"))
- def _init_messaging_persona(self):
- self.main_page = MessagingMainPage(self, "index.html")
- self.add_page(self.main_page, add_to_link_set=True)
- self.set_default_page(self.main_page)
+ self.export_page = CuminExportPage(self, "csv")
+ self.add_page(self.export_page)
+
+ self.about_page = AboutPage(self, "about.html")
+ self.add_page(self.about_page)
- self.add_common_pages()
+ self.resource_page.protected = False
- import account
- import messaging
- import inventory
+ # Enable the modules that are associated with the
+ # persona (from persona.py)
+ for name in modules:
+ try:
+ m = __import__(name, globals())
+ m.Module(self, name)
+ except:
+ pass
- account.Module(self, "account")
- messaging.Module(self, "messaging")
- inventory.Module(self, "inventory")
+ def db_init(self, schema_version_check=True):
+ self.model.init()
+ self.database.init(schema_version_check)
- def _init_default_persona(self):
- self.main_page = MainPage(self, "index.html")
- self.add_page(self.main_page, add_to_link_set=True)
- self.set_default_page(self.main_page)
-
- self.add_common_pages()
-
- import account
- import messaging
- import grid
- import inventory
- import usergrid
-
- account.Module(self, "account")
- messaging.Module(self, "messaging")
- grid.Module(self, "grid")
- inventory.Module(self, "inventory")
- usergrid.Module(self, "usergrid")
-
- def init(self):
+ def init(self, schema_version_check=True):
log.info("Initializing %s", self)
+ # Do this initialization as late as possible so that
+ # the application can set config values.
+ self.authenticator = CuminAuthenticator(self)
+ self.authorizator = CuminAuthorizator(self, self.access_path, self.do_authorize)
+ try:
+ self.authorizator.set_persona(self.persona)
+ except:
+ msg = "Persona is not defined '%s'"
+ raise Exception(msg % self.persona)
+
+ self.authorize_cb = self.authorizator.authorize
+ self.mainpage_cb = self.authorizator.find_mainpage
+
# Create RPC interfaces for QMF and aviary.
# These service have overlapping functionality,
# so they are wrapped in a sage.Catalog object
@@ -212,14 +228,12 @@
self.model.init()
self.session.init()
- self.database.init()
+ self.database.init(schema_version_check)
self.server.init()
- # Create the correct persona
- if self.persona in self.personas:
- self.personas[self.persona]()
- else:
- self._init_default_persona()
+ # This will set up the main view and enable the
+ # modules that are associated with the persona
+ self._init_persona(self.authorizator.get_enabled_modules())
for module in self.modules:
module.init()
@@ -229,24 +243,6 @@
super(Cumin, self).init()
- def add_common_pages(self):
- self.form_page = CuminFormPage(self, "form.html")
- self.add_page(self.form_page)
-
- self.add_page(StatChartPage(self, "stats.png"))
- self.add_page(StatStackedPage(self, "stacked.png"))
- self.add_page(PieChartPage(self, PieChartPage.PAGE_NAME))
- self.add_page(StatFlashPage(self, "chart.json"))
- self.add_page(FlashFullPage(self, "flashpage.html"))
-
- self.export_page = CuminExportPage(self, "csv")
- self.add_page(self.export_page)
-
- self.about_page = AboutPage(self, "about.html")
- self.add_page(self.about_page)
-
- self.resource_page.protected = False
-
def start(self):
log.info("Starting %s", self)
@@ -288,9 +284,8 @@
def __init__(self, app, name):
self.app = app
self.name = name
-
self.tasks = list()
-
+ log.debug("Init cumin module %s", name )
assert not hasattr(app, self.name), self.name
self.app.modules.append(self)
@@ -303,45 +298,10 @@
def init_test(self, test):
pass
-class GridMainPage(CuminPage, ModeSet):
- def __init__(self, app, name):
- super(GridMainPage, self).__init__(app, name)
-
- self.main = GridMainView(app, "main")
- self.add_mode(self.main)
- self.set_default_frame(self.main)
-
- self.page_html_class = "Cumin"
-
- def render_title(self, session):
- return self.get_title(session)
-
- def get_title(self, session):
- return "Administrator"
-
class GridMainView(CuminMainView):
def __init__(self, app, name):
super(GridMainView, self).__init__(app, name)
- #self.overview = GridOverviewFrame(app, "overview")
- #self.add_tab(self.overview)
-
-class MessagingMainPage(CuminPage, ModeSet):
- def __init__(self, app, name):
- super(MessagingMainPage, self).__init__(app, name)
-
- self.main = MessagingMainView(app, "main")
- self.add_mode(self.main)
- self.set_default_frame(self.main)
-
- self.page_html_class = "Cumin"
-
- def render_title(self, session):
- return self.get_title(session)
-
- def get_title(self, session):
- return "Administrator"
-
class MessagingMainView(CuminMainView):
def __init__(self, app, name):
super(MessagingMainView, self).__init__(app, name)
@@ -349,13 +309,21 @@
self.overview = MessagingOverviewFrame(app, "overview")
self.add_tab(self.overview)
-class MainPage(CuminPage, ModeSet):
+class MainView(CuminMainView):
def __init__(self, app, name):
+ super(MainView, self).__init__(app, name)
+
+ self.overview = OverviewFrame(app, "overview")
+ self.add_tab(self.overview)
+
+class MainPage(CuminPage, ModeSet):
+ def __init__(self, app, name, main_view):
super(MainPage, self).__init__(app, name)
- self.main = MainView(app, "main")
+ self.main = main_view
self.add_mode(self.main)
self.set_default_frame(self.main)
+ self.cumin_module = ["messaging", "grid"]
self.page_html_class = "Cumin"
@@ -365,13 +333,6 @@
def get_title(self, session):
return "Administrator"
-class MainView(CuminMainView):
- def __init__(self, app, name):
- super(MainView, self).__init__(app, name)
-
- self.overview = OverviewFrame(app, "overview")
- self.add_tab(self.overview)
-
# XXX Add qmf tab
class OverviewFrame(CuminFrame):
@@ -409,46 +370,12 @@
systems = TopSystemTable(app, "systems")
self.add_child(systems)
+ systems.cumin_module = "inventory"
submissions = TopSubmissionTable(app, "submissions")
self.add_child(submissions)
+ submissions.cumin_module = "grid"
- # XXX
-
- #queues = messaging.queue.TopQueueSet(app, "queues")
- #self.add_child(queues)
-
- #submissions = grid.submission.TopSubmissionSet(app, "submissions")
- #self.add_child(submissions)
-
- #systems = inventory.system.TopSystemSet(app, "systems")
- #self.add_child(systems)
-
-# don't appear to need this....
- # class Heading(CuminHeading):
- # def render_title(self, session):
- # return "Overview"
-
- # def render_icon_href(self, session):
- # return "resource?name=action-36.png"
-
-class GridOverviewFrame(OverviewFrame):
- def __init__(self, app, name):
- super(GridOverviewFrame, self).__init__(app, name)
-
- def get_overview_view(self, app):
- return GridOverviewView(app, "view")
-
-class GridOverviewView(Widget):
- def __init__(self, app, name):
- super(GridOverviewView, self).__init__(app, name)
-
- systems = TopSystemTable(app, "systems")
- self.add_child(systems)
-
- submissions = TopSubmissionTable(app, "submissions")
- self.add_child(submissions)
-
class MessagingOverviewFrame(OverviewFrame):
def __init__(self, app, name):
super(MessagingOverviewFrame, self).__init__(app, name)
@@ -681,7 +608,6 @@
qdate = self.field.get_content(session, record)
if qdate is None:
return 0
-
return fmt_duration(time.time() - secs(qdate))
def render_text_align(self, session):
Modified: branches/stability/cumin/python/cumin/messaging/binding.py
===================================================================
--- branches/stability/cumin/python/cumin/messaging/binding.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/messaging/binding.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -16,6 +16,7 @@
from wooly.parameters import DictParameter, StringParameter, ListParameter,\
IntegerParameter
from wooly.util import StringCatalog, Writer
+from cumin.util import xml_escape
strings = StringCatalog(__file__)
log = logging.getLogger("cumin.messaging.exchange")
@@ -80,7 +81,7 @@
def render_content(self, session):
# binding doesn't have a name, use the binding key
obj = self.object.get(session)
- return obj.bindingKey
+ return xml_escape(obj.bindingKey)
class BindingRemove(ObjectFrameTask):
def __init__(self, app, frame):
@@ -468,11 +469,11 @@
def render_n_value(self, session, i):
names = self.names.get(session)
- return len(names) > i and names[i] or ""
+ return len(names) > i and xml_escape(names[i]) or ""
def render_v_value(self, session, i):
values = self.values.get(session)
- return len(values) > i and values[i] or ""
+ return len(values) > i and xml_escape(values[i]) or ""
def get_exchange(self, session):
exchange_string = self.exchange.get(session)
Modified: branches/stability/cumin/python/cumin/messaging/brokerlink.py
===================================================================
--- branches/stability/cumin/python/cumin/messaging/brokerlink.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/messaging/brokerlink.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -37,7 +37,7 @@
class BrokerLinkRemoveForm(ObjectFrameTaskForm):
def render_content(self, session):
obj = self.object.get(session)
- return obj.host
+ return xml_escape(obj.host)
class BrokerLinkRemove(ObjectFrameTask):
def __init__(self, app, frame):
@@ -185,13 +185,14 @@
if not self.param.get(session):
self.param.set(session, exchange.id)
exchanges.append(exchange)
+
return exchanges
def render_item_value(self, session, exchange):
return exchange.id
def render_item_content(self, session, exchange):
- return exchange.name
+ return xml_escape(exchange.name)
def render_item_checked_attr(self, session, exchange):
if self.param.get(session) == exchange.id:
Modified: branches/stability/cumin/python/cumin/messaging/connection.py
===================================================================
--- branches/stability/cumin/python/cumin/messaging/connection.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/messaging/connection.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -101,8 +101,8 @@
def get_item_content(self, session, item):
args = (item.remoteProcessName, item.remotePid)
if args[1] is None:
- return item.address
- return "%s (%i)" % args
+ return xml_escape(item.address)
+ return xml_escape("%s (%i)" % args)
class ConnectionProcessColumn(ObjectTableColumn):
def __init__(self, app, name, attr, pid_attr):
Modified: branches/stability/cumin/python/cumin/messaging/exchange.py
===================================================================
--- branches/stability/cumin/python/cumin/messaging/exchange.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/messaging/exchange.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -94,7 +94,7 @@
return "Remove"
def get_item_content(self, session, item):
- return item.name or "Default exchange"
+ return xml_escape(item.name) or "Default exchange"
def do_invoke(self, invoc, exchange):
session = self.app.model.get_session_by_object(exchange)
@@ -130,7 +130,7 @@
return exchange.id
def render_item_content(self, session, exchange):
- return exchange.name or "<em>Default</em>"
+ return xml_escape(exchange.name) or "<em>Default</em>"
def render_item_checked_attr(self, session, exchange):
return exchange is self.param.get(session) and "checked=\"checked\""
Modified: branches/stability/cumin/python/cumin/messaging/main.py
===================================================================
--- branches/stability/cumin/python/cumin/messaging/main.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/messaging/main.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -16,6 +16,7 @@
super(Module, self).__init__(app, name)
self.frame = MessagingFrame(self.app, "messaging")
+ self.frame.cumin_module = name
def init(self):
super(Module, self).init()
Modified: branches/stability/cumin/python/cumin/messaging/queue.py
===================================================================
--- branches/stability/cumin/python/cumin/messaging/queue.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/messaging/queue.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -593,7 +593,7 @@
return queue_list
def render_item_content(self, session, queue):
- return queue.name or "<em>Default</em>"
+ return xml_escape(queue.name) or "<em>Default</em>"
def render_item_value(self, session, queue):
return queue._id
@@ -694,7 +694,7 @@
return "Source Queue"
def render_inputs(self, session):
- return self.get(session)
+ return xml_escape(self.get(session))
def get(self, session):
queue = self.form.object.get(session)
Modified: branches/stability/cumin/python/cumin/model.py
===================================================================
--- branches/stability/cumin/python/cumin/model.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/model.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -8,7 +8,7 @@
from cumin.formats import fmt_datetime, fmt_dict, fmt_none_brief, fmt_none,\
fmt_duration
-from cumin.util import calc_rate, JobStatusInfo, secs
+from cumin.util import calc_rate, JobStatusInfo, secs, xml_escape
from cumin.sqladapter import SqlAdapter
from rosemary.sqlfilter import SqlComparisonFilter
from rosemary.sqlquery import SqlQueryOptions
@@ -439,14 +439,20 @@
def get_title(self, session):
return self.name
- def format_value(self, session, value):
- return CuminStatistic.fmt_value(value)
+ def format_value(self, session, value, escape=False):
+ return CuminStatistic.fmt_value(value, escape)
@classmethod
- def fmt_value(cls, value):
+ def fmt_value(cls, value, escape=False):
try:
+ # Don't want to escape the None case because
+ # the formatter produces xml, and we don't
+ # need to escape the number cases either.
return cls.formatters[type(value)](value)
except KeyError:
+ # But we do optionally want to escape the string cases
+ if escape:
+ return xml_escape(value)
return value
class MetaData(object):
Modified: branches/stability/cumin/python/cumin/objectframe.py
===================================================================
--- branches/stability/cumin/python/cumin/objectframe.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/objectframe.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -93,8 +93,10 @@
obj = self.object.get(session)
for attr in self.get_attributes(session):
+ # name and title are from rosemary xml files
name = attr.title
- value = obj.get_formatted_value(attr.name)
+ # We are going to escape the value in ObjectAttributesEntry below
+ value = obj.get_formatted_value(attr.name, escape=False)
title = attr.description
writer.write(self.entry.render(session, name, value, title))
@@ -284,7 +286,7 @@
return cls
def render_content(self, session, frame):
- return frame.get_title(session)
+ return xml_escape(frame.get_title(session))
class ObjectViewHeading(ObjectViewChild):
def __init__(self, app, name, object):
@@ -297,7 +299,7 @@
retval = self.parent.render_title(session)
if retval is not None and isinstance(retval, str) and len(retval) > 100:
retval = retval[:100] + "..."
- return retval
+ return xml_escape(retval)
class ObjectViewSummary(ObjectViewChild):
def __init__(self, app, name, object):
@@ -467,7 +469,7 @@
return super(ObjectFrameTaskForm, self).render_content(session)
obj = self.object.get(session)
- return obj.get_title()
+ return xml_escape(obj.get_title())
class ObjectFrameTaskFeedbackForm(ObjectFrameTaskForm):
# substitute our submit button because it has feedback when it is clicked
Modified: branches/stability/cumin/python/cumin/objectselector.py
===================================================================
--- branches/stability/cumin/python/cumin/objectselector.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/objectselector.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -1,7 +1,7 @@
from cumin.objectframe import ObjectFrame
from cumin.qmfadapter import ObjectQmfAdapter, ObjectQmfField
from cumin.sqladapter import ObjectSqlAdapter, ObjectSqlField
-from cumin.util import Identifiable
+from cumin.util import Identifiable, xml_escape
from rosemary.model import RosemaryClass, RosemaryAttribute, RosemaryReference
from rosemary.sqlfilter import SqlLikeFilter
@@ -489,7 +489,7 @@
pass
def get_item_content(self, session, item):
- return item.get_formatted_value("name")
+ return item.get_formatted_value("name", escape=True)
class ObjectSelectorTaskForm(FoldingFieldSubmitForm):
def __init__(self, app, name, task):
@@ -545,6 +545,7 @@
return self.parent.selection.get(session)
def render_item_content(self, session, item):
+ # already escaped in get_item_content
return self.parent.task.get_item_content(session, item)
def render_item_class(self, session, item):
Copied: branches/stability/cumin/python/cumin/persona.py (from rev 5295, trunk/cumin/python/cumin/persona.py)
===================================================================
--- branches/stability/cumin/python/cumin/persona.py (rev 0)
+++ branches/stability/cumin/python/cumin/persona.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -0,0 +1,170 @@
+#!/usr/bin/env python
+import logging
+import os.path
+import re
+
+try:
+ import xml.etree.ElementTree as etree
+except ImportError:
+ import elementtree.ElementTree as etree
+
+log = logging.getLogger("cumin.authorize")
+
+class CuminGroup(object):
+ def __init__(self, name):
+ self.name = name
+ self.mainpage = "index.html"
+ self.allowed = [] # which modules the group can access
+
+class CuminPersona(object):
+ def __init__(self, name, auth):
+ self.name = name # may be handy to have this here
+
+ self.auth = auth.lower() == "true" # should groups be checked?
+ self.modules = [] # which modules are enabled
+ self.mapping = {} # dictionary of CuminGroup objects
+
+ def find_mainpage(self, group):
+ return self.mapping[group].mainpage
+
+ def lookup(self, group, module):
+ try:
+ if type(module) in (list, tuple):
+ return len(set(module) & \
+ set(self.mapping[group].allowed)) > 0
+ else:
+ return module in self.mapping[group].allowed
+ except KeyError:
+ return False
+
+class CuminPersonaMap(object):
+ def __init__(self):
+ log.info("Initializing %s", self.__class__.__name__)
+ self.personas = {}
+ self.filepath = None
+
+ def __repr__(self):
+ return ("%s %s" % ( self.__class__.__name__, self.filepath ))
+
+ def parse_personafile(self, filepath):
+ #TODO: handle exceptions during parsing
+ acctree = etree.ElementTree().parse(filepath)
+ for prec in acctree.getiterator('Persona'):
+ pkey = prec.attrib["name"]
+ auth = prec.attrib["auth"]
+ persona = CuminPersona(pkey, auth)
+ self.personas[pkey] = persona
+
+ for pchild in prec.getchildren():
+ if pchild.tag == "Module":
+ persona.modules.append(pchild.attrib["name"])
+ elif pchild.tag == "GroupAccess":
+ gkey = pchild.attrib["name"]
+ group = CuminGroup(gkey)
+ persona.mapping[gkey] = group
+ for gchild in pchild.getchildren():
+ if gchild.tag == "MainPage":
+ group.mainpage = gchild.attrib["name"]
+ elif gchild.tag == "ModuleAccess":
+ if gchild.attrib["name"] == "*":
+ group.allowed.extend(persona.modules)
+ else:
+ group.allowed.append(gchild.attrib["name"])
+
+class CuminAuthorizator(object):
+ def __init__(self, app, access_path, do_authorize):
+ self.app = app
+ self.access_path = access_path
+ log.info("Initializing %s", self)
+ self.personas = CuminPersonaMap()
+ self.persona = None
+ self.do_authorize = do_authorize
+
+ log.debug("Access file path is %s", access_path)
+ if os.path.isdir(access_path):
+ files = [os.path.join(access_path, x) \
+ for x in os.listdir(access_path)]
+ elif os.path.isfile(access_path):
+ files = [access_path]
+ else:
+ files = []
+ log.error("Path '%s' for access files does not exist!" % access_path)
+
+ for accfile in files:
+ if os.path.isfile(accfile) and \
+ os.path.splitext(accfile)[1] == ".xml":
+ try:
+ self.personas.parse_personafile(accfile)
+ except:
+ log.error("Access file '%s' does not parse!" % accfile)
+
+ def set_persona(self, name):
+ self.persona = self.personas.personas[name]
+
+ def is_enforcing(self):
+ return self.do_authorize and self.persona.auth
+
+ def find_mainpage(self, web_session):
+ # Find a main page for the current login session.
+ # If user is in the admin group, prefer that one.
+ # If we have multiple groups someday beyond user/admin,
+ # we may have to have a priority scheme here (or find
+ # a way to NOT have more than one mainpage).
+ try:
+ group = web_session.client_session.attributes['login_session'].group
+ except KeyError:
+ group = "nogroup"
+ if type(group) == type([]):
+ if "admin" in group:
+ group = "admin"
+ else:
+ group = group[0]
+ return self.persona.find_mainpage(group)
+
+ def get_enabled_modules(self):
+ return self.persona.modules
+
+ def contains_valid_group(self, groups):
+ for g in groups:
+ if g in self.persona.mapping:
+ return True
+ return False
+
+ def authorize(self, web_session, widget):
+ if not hasattr(widget, "cumin_module") or \
+ widget.cumin_module is None:
+ # no module for this widget, we have to allow it
+ return True
+
+ # We can't logically be authorized for a module we haven't even
+ # enabled! This may occur when parts of a display have
+ # associated themselves with a module explicitly.
+ # (like a column in a table that is only valid if a
+ # particular module has been loaded).
+ if type(widget.cumin_module) in (list, tuple):
+ if len(set(widget.cumin_module) & set(self.persona.modules)) == 0:
+ return False
+ elif not widget.cumin_module in self.persona.modules:
+ return False
+
+ if not self.is_enforcing():
+ # Auth checks against group are off.
+ return True
+
+ try:
+ group = web_session.client_session.attributes['login_session'].group
+ except KeyError:
+ group = "nogroup"
+
+ if type(group) == type([]):
+ for g in group:
+ if self.persona.lookup(g, widget.cumin_module):
+ log.debug("Allowing %s authorization for %s ",
+ widget.cumin_module, group)
+ return True
+ else:
+ return self.persona.lookup(group, widget.cumin_module)
+
+ log.debug("Denying %s authorization for %s ", widget.my_name, group)
+ return False
+
Modified: branches/stability/cumin/python/cumin/server.py
===================================================================
--- branches/stability/cumin/python/cumin/server.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/server.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -3,5 +3,12 @@
from wooly.server import WebServer
class CuminServer(WebServer):
+ def __init__(self, app, host, port, server_cert="", server_key=""):
+
+ # Elements of WebServer will look for the cert/key fields
+ self.server_cert = server_cert
+ self.server_key = server_key
+ super(CuminServer, self).__init__(app, host, port)
+
def authorized(self, session):
return True
Modified: branches/stability/cumin/python/cumin/stat.py
===================================================================
--- branches/stability/cumin/python/cumin/stat.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/stat.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -15,7 +15,7 @@
from cumin.widgets import StateSwitch
from wooly.template import WidgetTemplate
from cumin.charts import CuminPieChart, TimeSeriesChart, StackedValueChart
-from cumin.util import calc_rate, secs, nvl
+from cumin.util import calc_rate, secs, nvl, xml_escape
from cumin.OpenFlashChart import Element, Chart
strings = StringCatalog(__file__)
@@ -55,7 +55,7 @@
def render_item_value(self, session, item):
value = self.get_item_value(session, item)
- return self.render_formatted_value(session, item, value)
+ return self.render_formatted_value(session, item, value, escape=True)
def get_item_unit(self, session, item):
stat, _ = item
@@ -65,7 +65,10 @@
_, value = item
return value
- def render_formatted_value(self, session, item, value):
+ def render_formatted_value(self, session, item, value, escape=False):
+ # Since this routine delegates to CuminStatistic which may produce
+ # xml for None values, we need to handle escaping here and in
+ # CuminStatistic
def fmt_b(value):
return value is not None and fmt_bytes(value) or None
@@ -85,7 +88,7 @@
except:
pass
- return CuminStatistic.fmt_value(value)
+ return CuminStatistic.fmt_value(value, escape)
class NewStatSet(ItemSet):
def __init__(self, app, name):
@@ -113,7 +116,7 @@
def render_item_value(self, session, item):
stat, value = item
- return stat.format_value(session, value)
+ return stat.format_value(session, value, escape=True)
class DurationSwitch(StateSwitch):
def __init__(self, app, name):
Modified: branches/stability/cumin/python/cumin/usergrid/main.py
===================================================================
--- branches/stability/cumin/python/cumin/usergrid/main.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/usergrid/main.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -11,3 +11,4 @@
self.app.user_grid_page = MainPage(self.app, "usergrid.html")
self.app.add_page(self.app.user_grid_page, add_to_link_set=True)
+ self.app.user_grid_page.cumin_module = self.name
Modified: branches/stability/cumin/python/cumin/util.py
===================================================================
--- branches/stability/cumin/python/cumin/util.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/util.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -11,14 +11,9 @@
from random import sample
from threading import Thread, Event
from time import mktime, time, sleep
-from xml.sax.saxutils import escape as do_xml_escape
-
from parsley.threadingex import print_threads
+from wooly.util import xml_escape
-def xml_escape(string):
- if string:
- return do_xml_escape(string)
-
def short_id():
return "%08x" % randint(0, sys.maxint)
Modified: branches/stability/cumin/python/cumin/widgets.py
===================================================================
--- branches/stability/cumin/python/cumin/widgets.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/cumin/python/cumin/widgets.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -63,7 +63,7 @@
def render_user_name(self, session):
login = session.client_session.attributes["login_session"]
- return login.user.name
+ return xml_escape(login.user.name)
def render_logout_href(self, session):
page = self.app.login_page
@@ -83,7 +83,7 @@
return branch.marshal()
else:
return super(CuminMainView, self).render_tab_href(session, tab)
-
+
def render_about_href(self, session):
page = self.app.about_page
lsess = Session(page)
@@ -92,29 +92,24 @@
class CuminPageLinks(ItemSet):
def __init__(self, app, name):
super(CuminPageLinks, self).__init__(app, name)
-
+ self.app = app
self.html_class = CuminPageLinks.__name__
- self.check_admin = self.CheckAdmin(app)
-
def do_get_items(self, session):
return self.app.page_links
def render_item_content(self, session, page):
href = Session(page).marshal()
title = page.render_title(session)
+ if self.app.authorize_cb(session, page):
+ return fmt_link(href, title)
- return fmt_link(href, title)
-
def render_item_class(self, session, page):
if page is session.page:
return "selected"
else:
return "_"
- class CheckAdmin(CuminSqlDataSet):
- pass
-
class CuminFrame(Frame, ModeSet):
def __init__(self, app, name):
super(CuminFrame, self).__init__(app, name)
@@ -158,7 +153,6 @@
def render_href(self, session, *args):
branch = session.branch()
-
if hasattr(self, "view"):
self.view.show(branch)
@@ -1194,14 +1188,24 @@
# XXX this should move somewhere else
class LoginSession(object):
- def __init__(self, app, user):
+ def __init__(self, app, user, group):
self.app = app
- self.user = user
+ self.group = group
+ # If this is an external user, create
+ # an adapter here. Lots of things
+ # expect user.name to be defined.
+ if type(user) is str:
+ self.user = LoginSession.ExternalUser()
+ self.user.name = user
+ else:
+ self.user = user
self.created = datetime.now()
-
self.task_invocations = list()
+ class ExternalUser(object):
+ pass
+
class CuminPage(HtmlPage):
def __init__(self, app, name):
super(CuminPage, self).__init__(app, name)
@@ -1214,44 +1218,66 @@
self.error_tmpl = WidgetTemplate(self, "error_html")
self.not_found_tmpl = WidgetTemplate(self, "not_found_html")
- def do_process(self, session):
+ def _redirect_to_login(self, session):
session.cursor = self.app.database.get_read_cursor()
-
if not self.authorized(session):
page = self.app.login_page
-
sess = Session(page)
page.origin.set(sess, session.marshal())
-
self.redirect.set(session, sess.marshal())
+ return True
+ return False
- return
+ def redirect_on_not_authorized(self, session):
+ # If the user is not logged in, then it doesn't have a
+ # a group identity yet. Check to see if we should
+ # redirect to the login page before looking for a
+ # valid page to redirect to based on group
+ if not self._redirect_to_login(session):
+ super(CuminPage, self).redirect_on_not_authorized(session)
- super(CuminPage, self).do_process(session)
+ def do_process(self, session):
+ if not self._redirect_to_login(session):
+ super(CuminPage, self).do_process(session)
def authorized(self, session):
if not self.protected:
return True
login = session.client_session.attributes.get("login_session")
-
if login:
when = datetime.now() - timedelta(hours=24)
-
if login.created > when:
return True
+
elif self.app.user:
- cls = self.app.model.com_redhat_cumin.User
- users = cls.get_selection(session.cursor, name=self.app.user)
+ username = self.app.user
+ user, ok = self.app.authenticator.find_user(username)
+ if not ok:
+ # We couldn't find the user, internally or externally
+ log.info("User '%s' not found" % username)
+ return False
- if not users:
- raise Exception("User '%s' not found" % self.app.user)
+ # Check for valid group
+ if self.app.authorizator.is_enforcing():
+ if not user:
+ # This is an external user, default role is 'user'
+ roles = ['user']
+ else:
+ cursor = self.app.database.get_read_cursor()
+ roles = self.app.admin.get_roles_for_user(
+ cursor, user)
+ if not self.app.authorizator.contains_valid_group(roles):
+ log.info("No valid roles for '%s'" % username)
+ return False # go to login page
+ else:
+ roles = []
- login = LoginSession(self.app, users[0])
+ if user is None:
+ user = username
+ login = LoginSession(self.app, user, roles)
session.client_session.attributes["login_session"] = login
-
return True
-
return False
def render_error(self, session):
@@ -1395,15 +1421,16 @@
super(BaseBindingInput, self).__init__(app, name, field_param)
def render_item_content(self, session, field):
- return field.name or "Default"
+ return xml_escape(field.name) or "Default"
def render_value(self, session):
if self.disabled:
field = self.form.object.get(session)
- return fmt_shorten(field.get_formatted_value("name"), pre=36, post=4)
+ return fmt_shorten(field.get_formatted_value("name", escape=True),
+ pre=36, post=4)
else:
input_value = self.param.get(session)
- return input_value and input_value or ""
+ return input_value and xml_escape(input_value) or ""
def base_get_items(self, session, objects):
obj_list = []
@@ -1621,4 +1648,4 @@
def render_listcontainer_height(self, session):
return "%dpx" % self.listcontainer_height
-
\ No newline at end of file
+
Modified: branches/stability/mint/python/mint/database.py
===================================================================
--- branches/stability/mint/python/mint/database.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/mint/python/mint/database.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -14,22 +14,31 @@
def check(self):
log.info("Checking %s", self)
+ # checks moved to init because the model has to be
+ # initialized before we can check the schema. And
+ # there is no point connecting to the database twice...
- self.check_connection()
-
def init(self):
log.info("Initializing %s", self)
-
- def check_connection(self):
- conn = self.get_connection()
-
+ conn = None
try:
+ conn = self.get_connection()
cursor = conn.cursor()
- cursor.execute("select now()")
-
- log.debug("Database is talking at '%s'", self.dsn)
- finally:
+ self.check_connection(cursor)
+ self.app.admin.check_schema(cursor)
conn.close()
+ except psycopg2.OperationalError:
+ conn and conn.close()
+ log.error("Connection to database failed, is the database running?")
+ raise
+ except Exception, e:
+ conn and conn.close()
+ log.error(str(e))
+ raise
+ def check_connection(self, cursor):
+ cursor.execute("select now()")
+ log.debug("Database is talking at '%s'", self.dsn)
+
def __repr__(self):
return self.__class__.__name__
Modified: branches/stability/mint/python/mint/main.py
===================================================================
--- branches/stability/mint/python/mint/main.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/mint/python/mint/main.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -4,7 +4,7 @@
from session import MintSession
from update import UpdateThread
from vacuum import VacuumThread
-
+from cumin.admin import *
from util import *
log = logging.getLogger("mint.main")
@@ -16,7 +16,7 @@
self.session = MintSession(self, broker_uris)
self.database = MintDatabase(self, database_dsn)
-
+ self.admin = CuminAdmin(self)
self.update_thread = UpdateThread(self)
self.expire_enabled = True
Modified: branches/stability/rosemary/python/rosemary/model.py
===================================================================
--- branches/stability/rosemary/python/rosemary/model.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/rosemary/python/rosemary/model.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -3,6 +3,7 @@
from sqlquery import *
from sqltype import *
import util
+from cumin.util import xml_escape
log = logging.getLogger("rosemary.model")
@@ -833,10 +834,12 @@
for attr in ("name", "Name"):
if hasattr(self, attr):
- return self.get_formatted_value(attr)
+ return self.get_formatted_value(attr, escape=True)
- def get_formatted_value(self, attr):
+ def get_formatted_value(self, attr, escape=False):
value = self.get_value(attr)
+ if escape:
+ value = xml_escape(value)
formatter = None
if attr in self._class._properties_by_name:
formatter = self._class._properties_by_name[attr].formatter
Modified: branches/stability/wooly/python/wooly/__init__.py
===================================================================
--- branches/stability/wooly/python/wooly/__init__.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/wooly/python/wooly/__init__.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -2,7 +2,7 @@
from resources import ResourceFinder
from template import *
from util import *
-
+from traceback import print_exception
log = logging.getLogger("wooly")
strings = StringCatalog(__file__)
@@ -125,6 +125,8 @@
self.__defer_tmpl = WidgetTemplate(self, "deferred_html")
self.nocontent_tmpl = WidgetTemplate(self, "nocontent")
+ self.my_name = self.__class__.__module__ + '.' + self.__class__.__name__
+
def init(self):
log.debug("Initializing %s" % self)
@@ -285,26 +287,36 @@
for child in self.children:
child.save_parameters(session, params)
+ def redirect_on_not_authorized(self, session):
+ # Give an opportunity to redirect to a valid page
+ # when a user is not authorized for this widget.
+ pass
+
def process(self, session, *args):
- if self.update_enabled:
- self.page.enable_update(session, self)
+ if not self.app.authorize_cb(session, self):
+ self.redirect_on_not_authorized(session)
+ else:
+ if self.update_enabled:
+ self.page.enable_update(session, self)
- if self.defer_enabled:
- self.page.enable_defer(session, self)
+ if self.defer_enabled:
+ self.page.enable_defer(session, self)
- if self.app.debug:
- profile = self.page.profile.get(session)
+ if self.app.debug:
+ profile = self.page.profile.get(session)
- call = ProcessCall(profile, self, args)
- call.do(session)
- else:
- self.do_process(session, *args)
+ call = ProcessCall(profile, self, args)
+ call.do(session)
+ else:
+ self.do_process(session, *args)
def do_process(self, session, *args):
for child in self.children:
child.process(session)
def render(self, session, *args):
+ if not self.app.authorize_cb(session, self):
+ return ""
if self.app.debug:
profile = self.page.profile.get(session)
@@ -479,7 +491,12 @@
return self.render(session)
def service_error(self, session):
- self.error.set(session, PageError(self, session))
+ # Well, we can have other handled exceptions pop up before
+ # we actually get to the render so we want to record here
+ # what the root cause was.
+ cls, value, traceback = sys.exc_info()
+ self.error.set(session, PageError(self, session,
+ cls, value, traceback))
return self.render(session)
@@ -503,15 +520,18 @@
pass
class PageError(object):
- def __init__(self, page, session):
+ def __init__(self, page, session, cls, value, traceback):
self.page = page
self.session = session
+ self.cls = cls
+ self.value = value
+ self.traceback = traceback
def render(self):
writer = Writer()
writer.write("APPLICATION ERROR\n\n")
- print_exc(None, writer)
+ print_exception(self.cls, self.value, self.traceback, None, writer)
writer.write("\n")
Modified: branches/stability/wooly/python/wooly/forms.py
===================================================================
--- branches/stability/wooly/python/wooly/forms.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/wooly/python/wooly/forms.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -53,7 +53,7 @@
class FormError(object):
def __init__(self, message):
- self.message = message
+ self.message = xml_escape(message)
def get_message(self, session):
return self.message
@@ -133,6 +133,26 @@
title = self.widget.render_title(session)
return "The '%s' field must have a value" % title
+class QuoteValueError(FormError):
+ def __init__(self, widget):
+ super(QuoteValueError, self).__init__(None)
+
+ self.widget = widget
+
+ def get_message(self, session):
+ title = self.widget.render_title(session)
+ return "The '%s' field may not contain double quotes" % title
+
+class XMLValueError(FormError):
+ def __init__(self, widget):
+ super(XMLValueError, self).__init__(None)
+
+ self.widget = widget
+
+ def get_message(self, session):
+ title = self.widget.render_title(session)
+ return "The '%s' field may not contain XML special characters" % title
+
class EmptyInputError(FormError):
def __init__(self):
super(EmptyInputError, self).__init__("This value is required")
@@ -453,6 +473,19 @@
self.input = StringInput(app, "input")
self.add_child(self.input)
+class NoXMLStringField(StringField):
+ def validate(self, session):
+ super(NoXMLStringField, self).validate(session)
+
+ des = self.get(session)
+ if '"' in des:
+ error = QuoteValueError(self)
+ self.form.errors.add(session, error)
+
+ elif xml_escape(des) != des:
+ error = XMLValueError(self)
+ self.form.errors.add(session, error)
+
class MultilineStringField(ScalarField):
def __init__(self, app, name):
super(MultilineStringField, self).__init__(app, name, None)
Modified: branches/stability/wooly/python/wooly/pages.py
===================================================================
--- branches/stability/wooly/python/wooly/pages.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/wooly/python/wooly/pages.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -142,6 +142,14 @@
return sess.marshal()
+ def redirect_on_not_authorized(self, session):
+ mainpage = self.app.mainpage_cb(session)
+ if mainpage in self.app.pages_by_name:
+ log.debug("Not authorized, redirecting to %s", mainpage)
+ page = self.app.pages_by_name[mainpage]
+ sess = Session(page)
+ self.redirect.set(session, sess.marshal())
+
def do_process(self, session):
self.update_script.process(session)
self.defer_script.process(session)
Modified: branches/stability/wooly/python/wooly/server.py
===================================================================
--- branches/stability/wooly/python/wooly/server.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/wooly/python/wooly/server.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -212,7 +212,34 @@
request_queue_size=32,
numthreads=32,
max=32)
+
+ if self.server.server_cert and self.server.server_key:
+ ssl_adapter = None
+ # Try the Python ssl module solution first
+ try:
+ from wsgiserver.ssl_builtin import BuiltinSSLAdapter
+ ssl_adapter = BuiltinSSLAdapter(self.server.server_cert,
+ self.server.server_key)
+ log.info("Webserver: ssl enabled via the Python ssl module.")
+ except:
+ pass
+
+ if ssl_adapter is None:
+ try:
+ from wsgiserver.ssl_pyopenssl import pyOpenSSLAdapter
+ ssl_adapter = pyOpenSSLAdapter(self.server.server_cert,
+ self.server.server_key)
+ log.info("Webserver: ssl enabled via the pyOpenSSL module.")
+ except:
+ pass
+
+ if ssl_adapter is None:
+ log.warn("Webserver: SSL was requested, but imports failed "
+ "for SSL implementations. Running with http only.")
+ else:
+ self.wsgi_server.ssl_adapter = ssl_adapter
+
self.wsgi_server.environ["wsgi.version"] = (1, 1)
def run(self):
Modified: branches/stability/wooly/python/wooly/table.py
===================================================================
--- branches/stability/wooly/python/wooly/table.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/wooly/python/wooly/table.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -70,6 +70,7 @@
writer = Writer()
for column in self.get_visible_columns(session):
+
writer.write(column.render(session))
return writer.to_string()
@@ -114,6 +115,7 @@
self.visible = True
self.width = None
self.static_header = False
+ self.do_escape = True
def render_class(self, session):
tokens = list()
@@ -171,8 +173,23 @@
def render_header_link_class(self, session):
pass
+ def do_render_cell_content(self, session, record):
+ r = self.render_cell_content(session, record)
+ if self.do_escape:
+ return xml_escape(r)
+ return r
+
def render_cell_content(self, session, record):
pass
+
+ def do_render_cell_title(self, session, record):
+ r = self.render_cell_title(session, record)
+ if self.do_escape:
+ return xml_escape(r)
+ return r
+
+ def render_cell_title(self, session, record):
+ pass
class TableColumnCss(TableChild):
def __init__(self, app, name):
@@ -242,11 +259,11 @@
return self.parent.render_class(session)
def render_content(self, session, record):
- return self.parent.render_cell_content(session, record)
+ return self.parent.do_render_cell_content(session, record)
def render_cell_title(self, session, record):
#gives us the title="" attribute for each table cell
- return self.parent.render_cell_title(session, record)
+ return self.parent.do_render_cell_title(session, record)
class TableHeader(TableChild):
def render_colspan(self, session):
@@ -256,7 +273,8 @@
writer = Writer()
for column in self.table.get_visible_columns(session):
- writer.write(column.header.render(session))
+ if self.app.authorize_cb(session, column):
+ writer.write(column.header.render(session))
return writer.to_string()
@@ -269,7 +287,8 @@
writer = Writer()
for column in self.table.get_visible_columns(session):
- writer.write(column.cell.render(session, record))
+ if self.app.authorize_cb(session, column):
+ writer.write(column.cell.render(session, record))
return writer.to_string()
Modified: branches/stability/wooly/python/wooly/util.py
===================================================================
--- branches/stability/wooly/python/wooly/util.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/wooly/python/wooly/util.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -18,6 +18,11 @@
from parsley.collectionsex import *
+def xml_escape(string, entities=None):
+ if type(string) in (str, unicode):
+ return escape(string, entities)
+ return string
+
def unique_id():
bits0 = random.getrandbits(32)
bits1 = random.getrandbits(32)
Modified: branches/stability/wooly/python/wooly/widgets.py
===================================================================
--- branches/stability/wooly/python/wooly/widgets.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/wooly/python/wooly/widgets.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -58,7 +58,8 @@
writer = Writer()
for tab in self.__tabs:
- self.__tab_tmpl.render(writer, session, tab)
+ if self.app.authorize_cb(session, tab):
+ self.__tab_tmpl.render(writer, session, tab)
return writer.to_string()
@@ -197,7 +198,7 @@
return writer.to_string()
def render_item_content(self, session, item):
- return item
+ return xml_escape(item)
def render_item_class(self, session, item):
return "_"
Modified: branches/stability/wooly/python/wooly/wsgiserver/ssl_builtin.py
===================================================================
--- branches/stability/wooly/python/wooly/wsgiserver/ssl_builtin.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/wooly/python/wooly/wsgiserver/ssl_builtin.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -14,7 +14,7 @@
except ImportError:
ssl = None
-from cherrypy import wsgiserver
+from wooly import wsgiserver
class BuiltinSSLAdapter(wsgiserver.SSLAdapter):
Modified: branches/stability/wooly/python/wooly/wsgiserver/ssl_pyopenssl.py
===================================================================
--- branches/stability/wooly/python/wooly/wsgiserver/ssl_pyopenssl.py 2012-05-31 12:18:57 UTC (rev 5394)
+++ branches/stability/wooly/python/wooly/wsgiserver/ssl_pyopenssl.py 2012-05-31 14:49:15 UTC (rev 5395)
@@ -34,7 +34,7 @@
import threading
import time
-from cherrypy import wsgiserver
+from wooly import wsgiserver
try:
from OpenSSL import SSL
11 years, 11 months
r5394 - in trunk: cumin/model/plumage cumin/model/upgrades mint/python/mint/plumage
by croberts@fedoraproject.org
Author: croberts
Date: 2012-05-31 12:18:57 +0000 (Thu, 31 May 2012)
New Revision: 5394
Modified:
trunk/cumin/model/plumage/plumage.xml
trunk/cumin/model/plumage/rosemary.xml
trunk/cumin/model/upgrades/1.2_to_1.3
trunk/mint/python/mint/plumage/session.py
Log:
Changes to cumin-report to extract more data from the plumage database for use in charts.
Modified: trunk/cumin/model/plumage/plumage.xml
===================================================================
--- trunk/cumin/model/plumage/plumage.xml 2012-05-30 19:52:07 UTC (rev 5393)
+++ trunk/cumin/model/plumage/plumage.xml 2012-05-31 12:18:57 UTC (rev 5394)
@@ -7,6 +7,14 @@
<statistic name="used" type="uint32" uint="uint32" />
<statistic name="unused" type="uint32" uint="uint32" />
<statistic name="owner" type="uint32" uint="uint32" />
- <statistic name="efficiency" type="float" />
+ <statistic name="efficiency" type="float" />
+ <statistic name="freemem" type="uint32" uint="uint32" />
+ <statistic name="usedmem" type="uint32" uint="uint32" />
+ <statistic name="availmem" type="uint32" uint="uint32" />
+ <statistic name="totalmem" type="uint32" uint="uint32" />
+ <statistic name="freecpu" type="uint32" uint="uint32" />
+ <statistic name="usedcpu" type="uint32" uint="uint32" />
+ <statistic name="availcpu" type="uint32" uint="uint32" />
+ <statistic name="totalcpu" type="uint32" uint="uint32" />
</class>
-</schema>
+</schema>
\ No newline at end of file
Modified: trunk/cumin/model/plumage/rosemary.xml
===================================================================
--- trunk/cumin/model/plumage/rosemary.xml 2012-05-30 19:52:07 UTC (rev 5393)
+++ trunk/cumin/model/plumage/rosemary.xml 2012-05-31 12:18:57 UTC (rev 5394)
@@ -36,7 +36,39 @@
<statistic name="efficiency">
<title>Efficiency percentage</title>
</statistic>
+
+ <statistic name="freemem">
+ <title>Free memory (pool)</title>
+ </statistic>
+
+ <statistic name="usedmem">
+ <title>Used memory (pool)</title>
+ </statistic>
+
+ <statistic name="availmem">
+ <title>Available memory (pool)</title>
+ </statistic>
+
+ <statistic name="totalmem">
+ <title>Total memory (pool)</title>
+ </statistic>
+ <statistic name="freecpu">
+ <title>Free cpus (pool)</title>
+ </statistic>
+
+ <statistic name="usedcpu">
+ <title>Used cpus (pool)</title>
+ </statistic>
+
+ <statistic name="availcpu">
+ <title>Available cpus (pool)</title>
+ </statistic>
+
+ <statistic name="totalcpu">
+ <title>Total cpus (pool)</title>
+ </statistic>
+
</class>
</package>
Modified: trunk/cumin/model/upgrades/1.2_to_1.3
===================================================================
--- trunk/cumin/model/upgrades/1.2_to_1.3 2012-05-30 19:52:07 UTC (rev 5393)
+++ trunk/cumin/model/upgrades/1.2_to_1.3 2012-05-31 12:18:57 UTC (rev 5394)
@@ -3,3 +3,20 @@
echo 'create sequence "com.redhat.grid.plumage"."OSUtil_id_seq"' | psql -d cumin -U cumin -h localhost
echo 'create table "com.redhat.grid.plumage"."OSUtil" ("_id" int8 not null,"host" text not null,"ts" timestamp not null,"total" int8 not null,"used" int8 not null,"unused" int8 not null,"owner" int8 not null,"efficiency" float4 not null,constraint "OSUtil_pk" primary key ("_id"))'| psql -d cumin -U cumin -h localhost
echo 'create table "com.redhat.grid.plumage"."OSUtil_samples" ("ts" timestamp not null,"total" int8 not null,"used" int8 not null,"unused" int8 not null,"owner" int8 not null,"efficiency" float4 not null)'| psql -d cumin -U cumin -h localhost
+echo 'ALTER TABLE "com.redhat.grid.plumage"."OSUtil" ADD COLUMN freemem int8 NOT NULL DEFAULT 0' | psql -d cumin -U cumin -h localhost
+echo 'ALTER TABLE "com.redhat.grid.plumage"."OSUtil" ADD COLUMN usedmem int8 NOT NULL DEFAULT 0' | psql -d cumin -U cumin -h localhost
+echo 'ALTER TABLE "com.redhat.grid.plumage"."OSUtil" ADD COLUMN availmem int8 NOT NULL DEFAULT 0' | psql -d cumin -U cumin -h localhost
+echo 'ALTER TABLE "com.redhat.grid.plumage"."OSUtil" ADD COLUMN totalmem int8 NOT NULL DEFAULT 0' | psql -d cumin -U cumin -h localhost
+echo 'ALTER TABLE "com.redhat.grid.plumage"."OSUtil" ADD COLUMN freecpu int8 NOT NULL DEFAULT 0' | psql -d cumin -U cumin -h localhost
+echo 'ALTER TABLE "com.redhat.grid.plumage"."OSUtil" ADD COLUMN usedcpu int8 NOT NULL DEFAULT 0' | psql -d cumin -U cumin -h localhost
+echo 'ALTER TABLE "com.redhat.grid.plumage"."OSUtil" ADD COLUMN availcpu int8 NOT NULL DEFAULT 0' | psql -d cumin -U cumin -h localhost
+echo 'ALTER TABLE "com.redhat.grid.plumage"."OSUtil" ADD COLUMN totalcpu int8 NOT NULL DEFAULT 0' | psql -d cumin -U cumin -h localhost
+echo 'ALTER TABLE "com.redhat.grid.plumage"."OSUtil_samples" ADD COLUMN freemem int8 NOT NULL DEFAULT 0' | psql -d cumin -U cumin -h localhost
+echo 'ALTER TABLE "com.redhat.grid.plumage"."OSUtil_samples" ADD COLUMN usedmem int8 NOT NULL DEFAULT 0' | psql -d cumin -U cumin -h localhost
+echo 'ALTER TABLE "com.redhat.grid.plumage"."OSUtil_samples" ADD COLUMN availmem int8 NOT NULL DEFAULT 0' | psql -d cumin -U cumin -h localhost
+echo 'ALTER TABLE "com.redhat.grid.plumage"."OSUtil_samples" ADD COLUMN totalmem int8 NOT NULL DEFAULT 0' | psql -d cumin -U cumin -h localhost
+echo 'ALTER TABLE "com.redhat.grid.plumage"."OSUtil_samples" ADD COLUMN freecpu int8 NOT NULL DEFAULT 0' | psql -d cumin -U cumin -h localhost
+echo 'ALTER TABLE "com.redhat.grid.plumage"."OSUtil_samples" ADD COLUMN usedcpu int8 NOT NULL DEFAULT 0' | psql -d cumin -U cumin -h localhost
+echo 'ALTER TABLE "com.redhat.grid.plumage"."OSUtil_samples" ADD COLUMN availcpu int8 NOT NULL DEFAULT 0' | psql -d cumin -U cumin -h localhost
+echo 'ALTER TABLE "com.redhat.grid.plumage"."OSUtil_samples" ADD COLUMN totalcpu int8 NOT NULL DEFAULT 0' | psql -d cumin -U cumin -h localhost
+
Modified: trunk/mint/python/mint/plumage/session.py
===================================================================
--- trunk/mint/python/mint/plumage/session.py 2012-05-30 19:52:07 UTC (rev 5393)
+++ trunk/mint/python/mint/plumage/session.py 2012-05-31 12:18:57 UTC (rev 5394)
@@ -1,11 +1,13 @@
from __future__ import division
import time
+import inspect
from mint.util import MintDaemonThread
from time import sleep
from update import ObjectUpdate
from random import randrange
from datetime import timedelta, datetime
+from bson.code import Code
try:
from pymongo import Connection
@@ -22,6 +24,25 @@
UTC_DIFF = datetime.utcnow() - datetime.now() # needed because everything in mongo is UTC
+timestamp_mapper = Code("""
+ function () {
+ emit({ts:this.ts, st:this.st}, {mem: this.mem, cpu: this.cpu});
+ }
+ """)
+
+timestamp_reducer = Code("""
+ function (key, values) {
+ var memsum = 0;
+ var cpusum = 0;
+ values.forEach(function(doc) {
+ memsum += doc.mem;
+ cpusum += doc.cpu;
+ });
+ return {mem: memsum, cpu: cpusum}; //format of this needs to be same as second param of emit
+ }
+ """)
+
+
class OSUtil(object):
pass
@@ -153,7 +174,40 @@
sleep(1)
if self.stop_requested:
break
+
+ def fillOSUtilStats(self, time, temptable):
+ record = OSUtil()
+ record.host= "%s:%s" % (self.connection.host, self.connection.port)
+ record.total = len(self.collection.find({'ts':time}).distinct('n'))
+ record.used = len(self.collection.find({'ts':time,'st':{'$nin':['Unclaimed','Owner']}}).distinct('n'))
+ record.unused = len(self.collection.find({'ts':time,'st':'Unclaimed'}).distinct('n'))
+ record.owner = len(self.collection.find({'ts':time,'st':'Owner'}).distinct('n'))
+ reduced = self.collection.map_reduce(timestamp_mapper, timestamp_reducer, temptable, query={'ts':time,})
+ reduced_collection = Collection(self.database,temptable)
+ record.freemem = int(reduced_collection.find({'_id.st':{'$nin':['Claimed','Owner']}})[0]['value']['mem'])
+ record.freecpu = int(reduced_collection.find({'_id.st':{'$nin':['Claimed','Owner']}})[0]['value']['cpu'])
+ record.usedmem = int(reduced_collection.find({'_id.st':'Claimed'})[0]['value']['mem'])
+ record.usedcpu = int(reduced_collection.find({'_id.st':'Claimed'})[0]['value']['cpu'])
+
+ record.availmem = int(reduced_collection.find({'_id.st':'Claimed'})[0]['value']['mem']) + int(reduced_collection.find({'_id.st':'Unclaimed'})[0]['value']['mem'])
+ record.availcpu = int(reduced_collection.find({'_id.st':'Claimed'})[0]['value']['cpu']) + int(reduced_collection.find({'_id.st':'Unclaimed'})[0]['value']['cpu'])
+
+ record.totalmem = int(reduced_collection.find({'_id.st':'Claimed'})[0]['value']['mem']) + \
+ int(reduced_collection.find({'_id.st':'Unclaimed'})[0]['value']['mem']) + \
+ int(reduced_collection.find({'_id.st':'Owner'})[0]['value']['mem'])
+
+ record.totalcpu = int(reduced_collection.find({'_id.st':'Claimed'})[0]['value']['cpu']) + \
+ int(reduced_collection.find({'_id.st':'Unclaimed'})[0]['value']['cpu']) + \
+ int(reduced_collection.find({'_id.st':'Owner'})[0]['value']['cpu'])
+
+ if(record.total != record.owner):
+ record.efficiency = (record.used/(record.total-record.owner))*100
+ else:
+ record.efficiency = 0
+ record.ts = time - UTC_DIFF
+ return record
+
def run(self):
self._check_connection()
if self.stop_requested:
@@ -177,17 +231,7 @@
sample_times = map(lambda i: times[i],filter(lambda i: i%5 == 0,range(len(times))))
for time in sample_times:
- record = OSUtil()
- record.host= "%s:%s" % (self.connection.host, self.connection.port)
- record.total = len(self.collection.find({'ts':time}).distinct('n'))
- record.used = len(self.collection.find({'ts':time,'st':{'$nin':['Unclaimed','Owner']}}).distinct('n'))
- record.unused = len(self.collection.find({'ts':time,'st':'Unclaimed'}).distinct('n'))
- record.owner = len(self.collection.find({'ts':time,'st':'Owner'}).distinct('n'))
- if(record.total != record.owner):
- record.efficiency = (record.used/(record.total-record.owner))*100
- else:
- record.efficiency = 0
- record.ts = time - UTC_DIFF
+ record = self.fillOSUtilStats(time, "history")
obj = ObjectUpdate(self.app.model, record, self.cls)
self.app.update_thread.enqueue(obj)
@@ -220,17 +264,7 @@
sample_times = map(lambda i: times[i],filter(lambda i: i%5 == 0,range(len(times))))
for time in sample_times:
- record = OSUtil()
- record.host= "%s:%s" % (self.connection.host, self.connection.port)
- record.total = len(self.collection.find({'ts':time}).distinct('n'))
- record.used = len(self.collection.find({'ts':time,'st':{'$nin':['Unclaimed','Owner']}}).distinct('n'))
- record.unused = len(self.collection.find({'ts':time,'st':'Unclaimed'}).distinct('n'))
- record.owner = len(self.collection.find({'ts':time,'st':'Owner'}).distinct('n'))
- if(record.total != record.owner):
- record.efficiency = (record.used/(record.total-record.owner))*100
- else:
- record.efficiency = 0
- record.ts = time - UTC_DIFF
+ record = self.fillOSUtilStats(time, "current")
obj = ObjectUpdate(self.app.model, record, self.cls)
self.app.update_thread.enqueue(obj)
@@ -263,17 +297,7 @@
sample_times = map(lambda i: times[i],filter(lambda i: i%5 == 0,range(len(times))))
for time in sample_times:
- record = OSUtil()
- record.host= "%s:%s" % (self.connection.host, self.connection.port)
- record.total = len(self.collection.find({'ts':time}).distinct('n'))
- record.used = len(self.collection.find({'ts':time,'st':{'$nin':['Unclaimed','Owner']}}).distinct('n'))
- record.unused = len(self.collection.find({'ts':time,'st':'Unclaimed'}).distinct('n'))
- record.owner = len(self.collection.find({'ts':time,'st':'Owner'}).distinct('n'))
- if(record.total != record.owner):
- record.efficiency = (record.used/(record.total-record.owner))*100
- else:
- record.efficiency = 0
- record.ts = time - UTC_DIFF
+ record = self.fillOSUtilStats(time, "catchup")
obj = ObjectUpdate(self.app.model, record, self.cls)
self.app.update_thread.enqueue(obj)
11 years, 11 months
r5393 - in trunk/cumin: bin etc python/cumin
by tmckay@fedoraproject.org
Author: tmckay
Date: 2012-05-30 19:52:07 +0000 (Wed, 30 May 2012)
New Revision: 5393
Modified:
trunk/cumin/bin/cumin-admin
trunk/cumin/bin/cumin-web
trunk/cumin/etc/cumin.conf
trunk/cumin/python/cumin/authenticator.py
trunk/cumin/python/cumin/config.py
trunk/cumin/python/cumin/main.py
Log:
Change the default for "auth:" to empty string instead of "internal".
Since the internal database is always checked first, the "internal" keyword
is not needed and may confuse users. The word will still be allowed and
will not generate an error in log files but it means nothing.
BZ737979 (mod)
Modified: trunk/cumin/bin/cumin-admin
===================================================================
--- trunk/cumin/bin/cumin-admin 2012-05-30 18:00:55 UTC (rev 5392)
+++ trunk/cumin/bin/cumin-admin 2012-05-30 19:52:07 UTC (rev 5393)
@@ -66,7 +66,7 @@
print "Command '%s' is unknown" % name
sys.exit(1)
- authmech = [x.strip() for x in values.common.auth.split(";")]
+ authmech = [x.strip() for x in values.common.auth.split(";") if x != ""]
app = Cumin(config.get_home(), [], opts.database, authmech=authmech)
Modified: trunk/cumin/bin/cumin-web
===================================================================
--- trunk/cumin/bin/cumin-web 2012-05-30 18:00:55 UTC (rev 5392)
+++ trunk/cumin/bin/cumin-web 2012-05-30 19:52:07 UTC (rev 5393)
@@ -193,7 +193,7 @@
raise ArgError
broker_uris = [x.strip() for x in opts.brokers.split(",")]
- authmech = [x.strip() for x in values.auth.split(";")]
+ authmech = [x.strip() for x in values.auth.split(";") if x != ""]
cumin = Cumin(config.get_home(), broker_uris, opts.database,
opts.host, opts.port,
opts.server_cert, opts.server_key,
Modified: trunk/cumin/etc/cumin.conf
===================================================================
--- trunk/cumin/etc/cumin.conf 2012-05-30 18:00:55 UTC (rev 5392)
+++ trunk/cumin/etc/cumin.conf 2012-05-30 19:52:07 UTC (rev 5393)
@@ -112,7 +112,7 @@
# max-qmf-table-sort: 1000
# persona: grid
# authorize: False
-# auth: internal
+# auth:
# ldap_tls_cacertfile:
# ldap_tls_cacertdir:
# ldap_timeout: 30
@@ -307,7 +307,7 @@
## role will not see the 'Administrator' tab.
## The default role for a new user is 'user'.
-## auth: internal
+## auth:
## Allows specification of LDAP servers for external authentication of
## users during login. A server must support simple bind for authentication.
## This is a semicolon separated list of URLs of the following form:
Modified: trunk/cumin/python/cumin/authenticator.py
===================================================================
--- trunk/cumin/python/cumin/authenticator.py 2012-05-30 18:00:55 UTC (rev 5392)
+++ trunk/cumin/python/cumin/authenticator.py 2012-05-30 19:52:07 UTC (rev 5393)
@@ -397,7 +397,10 @@
log.info("Authenticator: import of ldap modules failed, "\
"ldap mechanism not available")
for authmech in app.authmech:
- if authmech != "internal":
+ # let the 'internal' word be used here since it has been
+ # on the trunk for a while. It means nothing, since the
+ # internal database will always be consulted first.
+ if authmech not in ("internal", ""):
# Allow initial ldap= to be skipped
try:
if authmech.startswith("ldap://"):
Modified: trunk/cumin/python/cumin/config.py
===================================================================
--- trunk/cumin/python/cumin/config.py 2012-05-30 18:00:55 UTC (rev 5392)
+++ trunk/cumin/python/cumin/config.py 2012-05-30 19:52:07 UTC (rev 5393)
@@ -124,7 +124,7 @@
# Put this here, because authentication is something that
# might need to be done commonly
param = ConfigParameter(self, "auth", str)
- param.default = 'internal'
+ param.default = ""
param = ConfigParameter(self, "ldap_tls_cacertfile", str)
param.default = ""
@@ -171,13 +171,6 @@
param = ConfigParameter(self, "authorize", bool)
param.default = False
- # Turn these off for now...
-# param = ConfigParameter(self, "authz-provider", str)
-# param.default = "internal"
-
-# param = ConfigParameter(self, "auth-create-ondemand", bool)
-# param.default = False
-
# param = ConfigParameter(self, "auth-proxy", bool)
# param.default = False
Modified: trunk/cumin/python/cumin/main.py
===================================================================
--- trunk/cumin/python/cumin/main.py 2012-05-30 18:00:55 UTC (rev 5392)
+++ trunk/cumin/python/cumin/main.py 2012-05-30 19:52:07 UTC (rev 5393)
@@ -36,7 +36,7 @@
host="localhost", port=45672,
server_cert="", server_key="",
persona="default",
- authmech=["internal"]):
+ authmech=[]):
super(Cumin, self).__init__()
self.home = home
11 years, 11 months
r5392 - trunk/cumin/python/cumin
by tmckay@fedoraproject.org
Author: tmckay
Date: 2012-05-30 18:00:55 +0000 (Wed, 30 May 2012)
New Revision: 5392
Modified:
trunk/cumin/python/cumin/authenticator.py
Log:
Find user must check for empty list as well as False. Make the test more general.
Modified: trunk/cumin/python/cumin/authenticator.py
===================================================================
--- trunk/cumin/python/cumin/authenticator.py 2012-05-23 20:02:16 UTC (rev 5391)
+++ trunk/cumin/python/cumin/authenticator.py 2012-05-30 18:00:55 UTC (rev 5392)
@@ -154,7 +154,9 @@
def find_user(self, user):
conn, res = self._find_user(user)
- return res is not False
+ if res:
+ return True
+ return False
def change_pw(self, user, oldpass, newpass):
conn, res = self._find_user(user)
11 years, 11 months
r5391 - trunk/mint/python/mint/plumage
by croberts@fedoraproject.org
Author: croberts
Date: 2012-05-23 20:02:16 +0000 (Wed, 23 May 2012)
New Revision: 5391
Modified:
trunk/mint/python/mint/plumage/session.py
Log:
Prevent possible divide by zero error in computing pool efficiency.
Modified: trunk/mint/python/mint/plumage/session.py
===================================================================
--- trunk/mint/python/mint/plumage/session.py 2012-05-23 18:06:45 UTC (rev 5390)
+++ trunk/mint/python/mint/plumage/session.py 2012-05-23 20:02:16 UTC (rev 5391)
@@ -183,7 +183,10 @@
record.used = len(self.collection.find({'ts':time,'st':{'$nin':['Unclaimed','Owner']}}).distinct('n'))
record.unused = len(self.collection.find({'ts':time,'st':'Unclaimed'}).distinct('n'))
record.owner = len(self.collection.find({'ts':time,'st':'Owner'}).distinct('n'))
- record.efficiency = (record.used/(record.total-record.owner))*100
+ if(record.total != record.owner):
+ record.efficiency = (record.used/(record.total-record.owner))*100
+ else:
+ record.efficiency = 0
record.ts = time - UTC_DIFF
obj = ObjectUpdate(self.app.model, record, self.cls)
@@ -223,7 +226,10 @@
record.used = len(self.collection.find({'ts':time,'st':{'$nin':['Unclaimed','Owner']}}).distinct('n'))
record.unused = len(self.collection.find({'ts':time,'st':'Unclaimed'}).distinct('n'))
record.owner = len(self.collection.find({'ts':time,'st':'Owner'}).distinct('n'))
- record.efficiency = (record.used/(record.total-record.owner))*100
+ if(record.total != record.owner):
+ record.efficiency = (record.used/(record.total-record.owner))*100
+ else:
+ record.efficiency = 0
record.ts = time - UTC_DIFF
obj = ObjectUpdate(self.app.model, record, self.cls)
@@ -263,7 +269,10 @@
record.used = len(self.collection.find({'ts':time,'st':{'$nin':['Unclaimed','Owner']}}).distinct('n'))
record.unused = len(self.collection.find({'ts':time,'st':'Unclaimed'}).distinct('n'))
record.owner = len(self.collection.find({'ts':time,'st':'Owner'}).distinct('n'))
- record.efficiency = (record.used/(record.total-record.owner))*100
+ if(record.total != record.owner):
+ record.efficiency = (record.used/(record.total-record.owner))*100
+ else:
+ record.efficiency = 0
record.ts = time - UTC_DIFF
obj = ObjectUpdate(self.app.model, record, self.cls)
11 years, 11 months
r5390 - in trunk: cumin/model/plumage mint/python/mint/plumage rosemary/python/rosemary
by croberts@fedoraproject.org
Author: croberts
Date: 2012-05-23 18:06:45 +0000 (Wed, 23 May 2012)
New Revision: 5390
Modified:
trunk/cumin/model/plumage/rosemary.xml
trunk/mint/python/mint/plumage/session.py
trunk/rosemary/python/rosemary/model.py
Log:
Refactoring code to load plumage data so that the loading method can be defined in rosemary.xml, at runtime, we grab that name and use it as the method to call to perform the customized loading operations.
Modified: trunk/cumin/model/plumage/rosemary.xml
===================================================================
--- trunk/cumin/model/plumage/rosemary.xml 2012-05-23 17:00:00 UTC (rev 5389)
+++ trunk/cumin/model/plumage/rosemary.xml 2012-05-23 18:06:45 UTC (rev 5390)
@@ -1,6 +1,10 @@
<model>
<package name="com.redhat.grid.plumage" persistent="timestamp">
<class name="OSUtil">
+ <loading_class>
+ <name>OSUtilLoader</name>
+ </loading_class>
+
<source>
<database>condor_stats</database>
<collection>samples.machine</collection>
Modified: trunk/mint/python/mint/plumage/session.py
===================================================================
--- trunk/mint/python/mint/plumage/session.py 2012-05-23 17:00:00 UTC (rev 5389)
+++ trunk/mint/python/mint/plumage/session.py 2012-05-23 18:06:45 UTC (rev 5390)
@@ -49,22 +49,13 @@
if cls._package._name in self.app.package_filter:
black_list.add(cls)
else:
- # We're going to have a thread for this...
- self.threads.append(CatchUpPlumageSessionThread(
- self.app,
- self.server_host,
- self.server_port,
- cls))
- self.threads.append(PlumageSessionThread(
- self.app,
- self.server_host,
- self.server_port,
- cls))
- self.threads.append(CurrentPlumageSessionThread(
- self.app,
- self.server_host,
- self.server_port,
- cls))
+ try:
+ loaders = ClassLoaders()
+ # Here we grab the name of the method to use (should be in rosemary.xml package/class/loading_class)
+ func = getattr(loaders, cls.loading_class, None)
+ func(self, cls)
+ except Exception, e:
+ log.error("No loading function for class %s (%s). Be sure that the method exists and is defined in rosemary.xml" % (cls._name, str(e)))
# Update our list, minus the black_list
self.app.classes.difference_update(black_list)
@@ -75,22 +66,14 @@
for pkg in self.app.model._packages:
if pkg._name not in self.app.package_filter:
self.app.packages.add(pkg)
+ loaders = ClassLoaders()
for cls in pkg._classes:
- self.threads.append(CatchUpPlumageSessionThread(
- self.app,
- self.server_host,
- self.server_port,
- cls))
- self.threads.append(PlumageSessionThread(
- self.app,
- self.server_host,
- self.server_port,
- cls))
- self.threads.append(CurrentPlumageSessionThread(
- self.app,
- self.server_host,
- self.server_port,
- cls))
+ try:
+ # Here we grab the name of the method to use (should be in rosemary.xml package/class/loading_class)
+ func = getattr(loaders, cls.loading_class, None)
+ func(self, cls)
+ except Exception, e:
+ log.error("No loading function for class %s (%s). Be sure that the method exists and is defined in rosemary.xml" % (cls._name, str(e)))
def start(self):
log.info("Starting %s", self)
if not imports_ok:
@@ -107,6 +90,25 @@
def __repr__(self):
return "%s(%s:%s)" % (self.__class__.__name__, self.server_host, self.server_port)
+class ClassLoaders(object):
+ ''' method for loading the com.redhat.grid.plumage.OSUtil data from plumage, name is found in rosemary.xml under package/class/loading_class '''
+ def OSUtilLoader(self, obj, cls):
+ obj.threads.append(CatchUpPlumageSessionThread(
+ obj.app,
+ obj.server_host,
+ obj.server_port,
+ cls))
+ obj.threads.append(PlumageSessionThread(
+ obj.app,
+ obj.server_host,
+ obj.server_port,
+ cls))
+ obj.threads.append(CurrentPlumageSessionThread(
+ obj.app,
+ obj.server_host,
+ obj.server_port,
+ cls))
+
class PlumageSessionThread(MintDaemonThread):
def __init__(self, app, server_host, server_port, cls):
super(PlumageSessionThread, self).__init__(app)
Modified: trunk/rosemary/python/rosemary/model.py
===================================================================
--- trunk/rosemary/python/rosemary/model.py 2012-05-23 17:00:00 UTC (rev 5389)
+++ trunk/rosemary/python/rosemary/model.py 2012-05-23 18:06:45 UTC (rev 5390)
@@ -759,6 +759,7 @@
self.source = ""
self.database = ""
self.collection = ""
+ self.loading_class = ""
def init(self):
super(Generic_class, self).init()
@@ -787,6 +788,7 @@
self.source = elem.findtext("source")
self.database = elem.findtext("source/database")
self.collection = elem.findtext("source/collection")
+ self.loading_class = elem.findtext("loading_class/name")
def load_class_components(self, elem):
for child in elem.findall("property"):
11 years, 11 months
r5389 - trunk/cumin/bin
by tmckay@fedoraproject.org
Author: tmckay
Date: 2012-05-23 17:00:00 +0000 (Wed, 23 May 2012)
New Revision: 5389
Modified:
trunk/cumin/bin/cumin-database
Log:
Add logging to /var/log/messages for unhandled errors connecting to the database in check-created-wait. This is to provide information on startup failures at boot.
Modified: trunk/cumin/bin/cumin-database
===================================================================
--- trunk/cumin/bin/cumin-database 2012-05-21 20:05:36 UTC (rev 5388)
+++ trunk/cumin/bin/cumin-database 2012-05-23 17:00:00 UTC (rev 5389)
@@ -125,30 +125,31 @@
function check-created-wait {
for ((c=0; c<=30; c++))
do
- res="$(psql -d cumin -U cumin -h localhost -c '\q' 2>&1)"
+ res="$(psql -d cumin -U cumin -h localhost -c '\q' 2>&1)" || true
case $res in
*"could not connect"* | *"database system is starting up"*)
if [ $c -eq 0 ] ; then
echo
fi
- echo $res
- echo retry...
+ echo "Error connecting to the database server, retry"
sleep 1
;;
"")
return 0
;;
*)
- echo $res
- echo "Error: The database may not be created"
+ logger -p daemon.error $res
+ echo "Error: Could not connect to the database server."
+ echo "Check /var/log/messages for the specific error."
+ echo "The database may not be created."
if [ "$1" != noadvice ]; then
- echo "Hint: Run 'cumin-database install'"
+ echo "Hint: Try 'cumin-database install'"
fi
return 1
;;
esac
done
- echo "Error: Could not connect to server"
+ echo "Error: Could not connect to the database server"
return 1
}
11 years, 11 months
r5388 - trunk/cumin/bin
by tmckay@fedoraproject.org
Author: tmckay
Date: 2012-05-21 20:05:36 +0000 (Mon, 21 May 2012)
New Revision: 5388
Modified:
trunk/cumin/bin/cumin-database
Log:
Tweak output in the retry case to contain the error string
Modified: trunk/cumin/bin/cumin-database
===================================================================
--- trunk/cumin/bin/cumin-database 2012-05-21 19:49:49 UTC (rev 5387)
+++ trunk/cumin/bin/cumin-database 2012-05-21 20:05:36 UTC (rev 5388)
@@ -131,7 +131,8 @@
if [ $c -eq 0 ] ; then
echo
fi
- echo could not connect to server, retry...
+ echo $res
+ echo retry...
sleep 1
;;
"")
11 years, 11 months
r5387 - trunk/cumin/bin
by tmckay@fedoraproject.org
Author: tmckay
Date: 2012-05-21 19:49:49 +0000 (Mon, 21 May 2012)
New Revision: 5387
Modified:
trunk/cumin/bin/cumin-database
Log:
Add "the database system is starting up" to the retry list in check-created-wait. Print out the string returned from psql in a failure case.
BZ823506
Modified: trunk/cumin/bin/cumin-database
===================================================================
--- trunk/cumin/bin/cumin-database 2012-05-21 18:43:55 UTC (rev 5386)
+++ trunk/cumin/bin/cumin-database 2012-05-21 19:49:49 UTC (rev 5387)
@@ -127,7 +127,7 @@
do
res="$(psql -d cumin -U cumin -h localhost -c '\q' 2>&1)"
case $res in
- *"could not connect"*)
+ *"could not connect"* | *"database system is starting up"*)
if [ $c -eq 0 ] ; then
echo
fi
@@ -138,7 +138,8 @@
return 0
;;
*)
- echo "Error: The database is not created"
+ echo $res
+ echo "Error: The database may not be created"
if [ "$1" != noadvice ]; then
echo "Hint: Run 'cumin-database install'"
fi
11 years, 11 months