r5469 - trunk/cumin/python/cumin
by croberts@fedoraproject.org
Author: croberts
Date: 2012-09-14 18:42:21 +0000 (Fri, 14 Sep 2012)
New Revision: 5469
Modified:
trunk/cumin/python/cumin/objectselector.py
trunk/cumin/python/cumin/objectselector.strings
Log:
Completing work for BZ 851205 by adding the <col> tag in the tables. This is used to determine column width since we are now using table-layout:fixed.
Modified: trunk/cumin/python/cumin/objectselector.py
===================================================================
--- trunk/cumin/python/cumin/objectselector.py 2012-09-12 14:56:14 UTC (rev 5468)
+++ trunk/cumin/python/cumin/objectselector.py 2012-09-14 18:42:21 UTC (rev 5469)
@@ -495,7 +495,7 @@
self.cell.replace_child(self.cell.input)
self.sortable = False
- self.width = "1%"
+ self.width = "25px;"
def render_cell_value(self, session, record):
try:
Modified: trunk/cumin/python/cumin/objectselector.strings
===================================================================
--- trunk/cumin/python/cumin/objectselector.strings 2012-09-12 14:56:14 UTC (rev 5468)
+++ trunk/cumin/python/cumin/objectselector.strings 2012-09-14 18:42:21 UTC (rev 5469)
@@ -92,6 +92,7 @@
[ObjectSelectorTable.html]
<table id="{id}" class="{class}">
+ {columns}
<style type="text/css">
{css}
</style>
11 years, 8 months
r5468 - in branches/vmotoska/cumin/python/cumin: grid users
by vmotoska@fedoraproject.org
Author: vmotoska
Date: 2012-09-12 14:56:14 +0000 (Wed, 12 Sep 2012)
New Revision: 5468
Modified:
branches/vmotoska/cumin/python/cumin/grid/job.py
branches/vmotoska/cumin/python/cumin/users/widgets.py
Log:
delete functionality, grid/job syntax error ?
Modified: branches/vmotoska/cumin/python/cumin/grid/job.py
===================================================================
--- branches/vmotoska/cumin/python/cumin/grid/job.py 2012-09-12 13:14:27 UTC (rev 5467)
+++ branches/vmotoska/cumin/python/cumin/grid/job.py 2012-09-12 14:56:14 UTC (rev 5468)
@@ -766,10 +766,9 @@
if "property" in item:
if item["name"] == "JobStatus":
status = item["value"]
- if JobStatusInfo.get_status_string(status) in {"Completed",
- "Removed"}:
+ if JobStatusInfo.get_status_string(status) in ["Completed", "Removed"]:
render = False
- break
+ break
return render and not error and self.edit_button.render(session) or ""
def render_title(self, session):
Modified: branches/vmotoska/cumin/python/cumin/users/widgets.py
===================================================================
--- branches/vmotoska/cumin/python/cumin/users/widgets.py 2012-09-12 13:14:27 UTC (rev 5467)
+++ branches/vmotoska/cumin/python/cumin/users/widgets.py 2012-09-12 14:56:14 UTC (rev 5468)
@@ -3,7 +3,6 @@
from crypt import crypt
from random import sample
-from cumin.objectselector import ObjectSelector, ObjectLinkColumn, ObjectTable, ObjectTableColumn
from cumin.objectframe import ObjectFrame
from cumin.sqladapter import ObjectSqlAdapter
@@ -76,7 +75,7 @@
if len(data) > 0:
for userrow in data:
user_record = list(userrow)
- tmp_res = [user_record[0],user_record[1],user_record[2],user_record[3],user_record[4],'']
+ tmp_res = [user_record[2],user_record[1],user_record[2],user_record[3],user_record[4],user_record[5]]
added = False
for i,r in enumerate(res):
if r[1] == user_record[1]:
@@ -101,14 +100,45 @@
return len(self.get_data(values,options))
-class UsersRemove(ObjectSelectorTask):
+class UsersRemoveTask(ObjectSelectorTask):
+ def __init__(self, app, selector):
+ super(UsersRemoveTask, self).__init__(app, selector)
+ self.cls = app.model.com_redhat_cumin.User
+ self.selector = selector
+ self.form = UserRemoveTaskForm(app, self.name, self)
+
def get_title(self, session):
return "Remove"
+ def do_invoke(self, invoc, items):
+ invoc.end()
+
def do_exit(self, session):
self.app.main_page.main.users.view.show(session)
+class UserRemoveTaskForm(ObjectSelectorTaskForm):
+ def __init__(self, app, name, task):
+ super(UserRemoveTaskForm, self).__init__(app, name, task)
+ self.cls = task.cls
+ self.task = task
+
+# def do_process(self, session):
+# log.debug("-----%s",self.get_selection(session))
+# super(UserRemoveTaskForm, self).do_process(session)
+
+
+ def process_submit(self, session):
+ if self.get_selection(session) == 0:
+ self.cancel(session)
+ for i in self.selection.get(session):
+ i.delete(session.cursor)
+
+ super(UserRemoveTaskForm, self).process_submit(session)
+
+ def render_title(self, session):
+ return "Remove user:"
+
class UsersSelector(ObjectSelector):
def __init__(self, app, name):
cls = app.model.com_redhat_cumin.UserRoleMapping
@@ -119,7 +149,6 @@
self.table.adapter = UserDataAdapter(app)
frame = "main.users.userdetail"
col = UsernameSearchColumn(app, "name", user_cls.name, user_cls._id, frame)
-
ext_col = ExternalUserColumn(app, "external", user_cls.password )
mob_col = MobileUserColumn(app, "mobile", user_cls.pin)
rol_col = UserRoleAssignmentColumn(app, "roles",role_cls.name)
@@ -128,7 +157,7 @@
self.add_column(ext_col)
self.add_column(mob_col)
self.add_search_filter(col)
- self.remove = UsersRemove(app, self)
+ self.remove = UsersRemoveTask(app, self)
self.links.add_child(AddUserLink(app, "adduser"))
11 years, 8 months
r5467 - branches/tmckay/sage/python/sage/aviary
by tmckay@fedoraproject.org
Author: tmckay
Date: 2012-09-12 13:14:27 +0000 (Wed, 12 Sep 2012)
New Revision: 5467
Modified:
branches/tmckay/sage/python/sage/aviary/aviaryoperations.py
Log:
Update some docstring comments
Modified: branches/tmckay/sage/python/sage/aviary/aviaryoperations.py
===================================================================
--- branches/tmckay/sage/python/sage/aviary/aviaryoperations.py 2012-09-12 13:11:56 UTC (rev 5466)
+++ branches/tmckay/sage/python/sage/aviary/aviaryoperations.py 2012-09-12 13:14:27 UTC (rev 5467)
@@ -19,6 +19,12 @@
log = logging.getLogger("sage.aviary")
class SudsLogging(object):
+ '''
+ Class to enable logging of SOAP transactions.
+
+ This class allows detailed logging of SOAP transactions through
+ the Suds module. It is meant as a debugging aid during development.
+ '''
_on = False
sudslogs = {"suds.client": None, "suds.transport": None, "suds.xsd.schema": None, "suds.wsdl": None}
@@ -65,7 +71,11 @@
def _get_host(name, servers):
'''
Lookup a host in a dictionary produced by sage.util.host_list.
- Return the scheme and URL for the host.
+
+ The host may be just a hostname or hostname:port.
+ Return the scheme and URL for the host. The scheme is separated
+ out for convenience (although it could easily be parsed from the
+ URL)
'''
scheme = ""
host = ""
@@ -106,7 +116,7 @@
class ServerList(object):
'''
Query an Aviary locator object for endpoints by reource and subtype.
- Lookup an endpoint of the specified type by machine name.
+ Lookup an endpoint of the specified type by hostname or hostname:port.
Allow the cached list to be refreshed on demand.
'''
def __init__(self, locator, resource, subtype):
@@ -172,10 +182,11 @@
def find_server(self, machine, refresh=False):
'''
- Lookup a URL in the cached server list by machine.
+ Lookup a URL in the cached server list by hostname or hostname:port.
Generate a new server list if refresh is True, or if the
initial host lookup fails and refresh == "on_no_host".
- Return the scheme and URL for the specified machine.
+ Return the scheme and URL for the specified host if found or raise
+ an exception if the host could not be found.
'''
# Update the list if necessary and return host info for machine.
# If refresh is True, the server list will be updated. This is
@@ -199,9 +210,20 @@
class FixedServerList(object):
'''
- Allows lookup for endpoints by machine when the server list is fixed.
+ Query a fixed server list for Aviary endpoints.
+ Lookup an endpoint in the fixed list by hostname or hostname:port.
+ The fixed server list is an alternative to using an Aviary locator.
'''
def __init__(self, servers, port, path, subtype):
+ '''
+ Initialize a fixed server list of Aviary endpoints.
+ servers -- a comma separated list of (partial) URLs
+ port -- default port value if a URL does not specify a port
+ path -- default path value if a URL does not specify a path
+ subtype -- this is used to identify the service type in error
+ messages. Predefind values are "JOB" and "QUERY_SERVER".
+ The scheme for a URL will be "http" if not specified.
+ '''
# Fixed server list, there is no point to a retry on failed ops.
self.should_retry = False
@@ -221,6 +243,11 @@
self.nice = subtype
def find_server(self, machine, *args):
+ '''
+ Lookup a URL in the fixed server list by hostname or hostname:port.
+ Return the scheme and URL for the specified host if found or raise
+ an exception if the host could not be found.
+ '''
scheme, host = _get_host(machine, self.servers)
if host == "":
log.info("AviaryOperations: failed to locate %s on %s" \
11 years, 8 months
r5466 - in branches/tmckay: cumin/python/cumin sage/python/sage sage/python/sage/aviary sage/python/sage/qmf
by tmckay@fedoraproject.org
Author: tmckay
Date: 2012-09-12 13:11:56 +0000 (Wed, 12 Sep 2012)
New Revision: 5466
Modified:
branches/tmckay/cumin/python/cumin/model.py
branches/tmckay/sage/python/sage/aviary/aviaryoperations.py
branches/tmckay/sage/python/sage/qmf/qmfoperations.py
branches/tmckay/sage/python/sage/util.py
Log:
Add a get_submission_summaries() call to retrieve multiple submssions by id,
with or without job summaries included and with the job summaries in either native form or adapted to the QMF format.
The existing get_job_summaries() becomes a subcase of get_submission_summaries()
Modified: branches/tmckay/cumin/python/cumin/model.py
===================================================================
--- branches/tmckay/cumin/python/cumin/model.py 2012-09-11 14:08:44 UTC (rev 5465)
+++ branches/tmckay/cumin/python/cumin/model.py 2012-09-12 13:11:56 UTC (rev 5466)
@@ -795,8 +795,8 @@
pass
self.model.app.remote.get_job_summaries(self.submission,
- completion,
- self.machine_name)
+ self.machine_name,
+ callback=completion)
def delete(self):
del self.model.job_summaries_by_submission[self.submission._id]
Modified: branches/tmckay/sage/python/sage/aviary/aviaryoperations.py
===================================================================
--- branches/tmckay/sage/python/sage/aviary/aviaryoperations.py 2012-09-11 14:08:44 UTC (rev 5465)
+++ branches/tmckay/sage/python/sage/aviary/aviaryoperations.py 2012-09-12 13:11:56 UTC (rev 5466)
@@ -11,7 +11,8 @@
from datetime import datetime
from threading import Lock
from suds import *
-from sage.util import CallSync, CallThread, host_list, parse_URL, get_datadir
+from sage.util import MethodResult, CallSync, CallThread, host_list,\
+ parse_URL, get_datadir
from aviarylocator import AviaryLocator
from clients import ClientPool, TransportFactory
@@ -230,8 +231,10 @@
class MockScheduler(object):
'''
+ Pseudo scheduler object.
+
Most methods in the _AviaryXXX objects were written to be compatible
- with QMF methods which take a scheduler object.
+ with QMF methods which take Rosemary objects.
This is a mock scheduler object containing the fields
referenced by the methods: a scheduler name, a pool identifier,
@@ -246,6 +249,25 @@
self.Pool = pool
self.Machine = machine
+
+class MockSubmission(object):
+ '''
+ Pseudo submission object.
+
+ Most methods in the _AviaryXXX objects were written to be compatible
+ with QMF methods which take a Rosemary object.
+
+ This is a mock submission object containing the fields
+ referenced by the methods: a submission name and a submission owner.
+
+ This object may be used when a submission object from Rosemary
+ is not available (ie, sage is being used outside of Cumin). The
+ only required value is name.
+ '''
+ def __init__(self, name, owner = None):
+ self.Name = name
+ self.Owner = owner
+
class _AviaryJobMethods(object):
# Do this here rather than __init__ so we don't have to worry about
@@ -590,9 +612,107 @@
self.query_client_pool.return_object(client)
return res;
- def get_job_summaries(self, submission, callback, machine_name):
- assert callback
+ def get_job_summaries(self, submission, machine_name, callback=None):
+ # This routine matches the semantics of the QMF wrapper function
+ # of the same name. Using Aviary, this is actually just a subcase of
+ # get_submission_summaries() below with the following restrictions:
+
+ # 1) This routine works with only a single submission
+ # 2) Job summaries are always retrieved with the submission summary
+ # 3) Job summaries are always adapted to match the QMF format
+ # 4) Only job summaries are returned, the rest of the submission
+ # summary is discarded.
+ def my_process_results(result):
+ result = self._pretty_result(result, machine_name)
+ if isinstance(result, Exception):
+ status, data = result, None
+ else:
+ # This is intended to work with a single submission
+ result = result[0]
+ status = _AviaryCommon._get_status(result.status)
+ if status == "OK" and hasattr(result, "jobs"):
+ data = {"Jobs": self._adapt_job_summaries(result.jobs)}
+ else:
+ data = {"Jobs": None}
+ return status, data
+
+ return self._get_submission_summaries(submission, machine_name, callback,
+ True, my_process_results)
+
+ def get_submission_summaries(self, submission, machine_name, callback=None,
+ include_job_summaries=True,
+ adapt_job_summaries=True):
+
+ def my_process_results(result):
+ result = self._pretty_result(result, machine_name)
+ if isinstance(result, Exception):
+ status, data = result, None
+ else:
+ status = 0
+ data = result
+ if adapt_job_summaries and include_job_summaries:
+ for d in data:
+ d.jobs = self._adapt_job_summaries(d.jobs)
+ return status, data
+
+ return self._get_submission_summaries(submission, machine_name, callback,
+ include_job_summaries,
+ my_process_results)
+
+ def get_submission_ids_by_name(self, host, page_size, name):
+ return self._get_submission_ids(host, page_size, None, name=name)
+
+ def get_submission_ids_by_qdate(self, host, page_size, qdate, mode):
+ if long(qdate) > sys.maxint:
+ raise Exception("Qdate is larger than max int")
+ if not mode in ("BEFORE", "AFTER"):
+ raise Exception("Mode must be 'BEFORE' or 'AFTER'")
+ return self._get_submission_ids(host, page_size, mode, qdate=qdate)
+
+ def _get_submission_summaries(self, submission, machine_name,
+ callback,
+ include_job_summaries,
+ process_results):
+
+ def my_callback(result):
+ query_client.set_enable_attributes(False)
+ self.query_client_pool.return_object(query_client)
+ status, data = process_results(result)
+ callback(status, data)
+
+ query_client = self.query_client_pool.get_object()
+ self._setup_client(query_client,
+ self.query_servers,
+ machine_name,
+ "getSubmissionSummary")
+
+ if include_job_summaries:
+ query_client.set_enable_attributes(True)
+ query_client.set_attributes({"includeJobSummaries": "true"})
+
+ if not type(submission) in (list, tuple):
+ submission = [submission]
+ subIds = []
+ for sub in submission:
+ subId = query_client.factory.create('ns0:SubmissionID')
+ subId.name = sub.Name
+ subId.owner = sub.Owner
+ subIds.append(subId)
+
+ if callback:
+ t = CallThread(self.call_client_retry, my_callback,
+ query_client, "getSubmissionSummary", subIds)
+ t.start()
+ else:
+ res = self._call_sync(process_results, self.call_client_retry,
+ query_client, "getSubmissionSummary", subIds)
+ query_client.set_enable_attributes(False)
+ self.query_client_pool.return_object(query_client)
+ return res;
+
+ def _adapt_job_summaries(self, jobs):
+
def to_int_seconds(dt):
# Change a datetime.datetime into int seconds since epoch
# Note, this works nicely if the datetime happens to include microseconds
@@ -607,71 +727,34 @@
return str(getattr(job, attr))
return ""
- def adapt(jobs):
- # Make an aviary job summary look like the canonical form
- # that cumin is expecting (actually the QMF form because of history).
- result = list()
- for job in jobs:
- cluster, proc = job.id.job.split(".")
- j = dict()
- j["ClusterId"] = int(cluster)
- j["Cmd"] = str(job.cmd)
- j["EnteredCurrentStatus"] = to_int_seconds(job.last_update)
+ # Make an aviary job summary look like the canonical form
+ # that cumin is expecting (actually the QMF form because of history).
+ result = list()
+ for job in jobs:
+ cluster, proc = job.id.job.split(".")
+ j = dict()
+ j["ClusterId"] = int(cluster)
+ j["Cmd"] = str(job.cmd)
+ j["EnteredCurrentStatus"] = to_int_seconds(job.last_update)
- # Note, GlobalJobId here will not match the same value from
- # QMF because the qdate portion of the name is missing
- j["GlobalJobId"] = job.id.scheduler + \
- "#" + job.id.job
+ # Note, GlobalJobId here will not match the same value from
+ # QMF because the qdate portion of the name is missing
+ j["GlobalJobId"] = job.id.scheduler + \
+ "#" + job.id.job
- j["JobStatus"] = str(job.job_status)
- j["ProcId"] = int(proc)
- j["QDate"] = to_int_seconds(job.queued)
-
- # These may be null...
- j["Args"] = get_string(job, "args1")
- j["ReleaseReason"] = get_string(job, "released")
- j["HoldReason"] = get_string(job, "held")
- result.append(j)
- return result
-
- def my_callback(result):
- query_client.set_enable_attributes(False)
- self.query_client_pool.return_object(query_client)
- result = self._pretty_result(result, machine_name)
- if isinstance(result, Exception):
- callback(result, None)
- else:
- status = _AviaryCommon._get_status(result[0].status)
- if status == "OK" and hasattr(result[0], "jobs"):
- data = {"Jobs": adapt(result[0].jobs)}
- else:
- data = {"Jobs": None}
- callback(status, data)
+ j["JobStatus"] = str(job.job_status)
+ j["ProcId"] = int(proc)
+ j["QDate"] = to_int_seconds(job.queued)
- query_client = self.query_client_pool.get_object()
- self._setup_client(query_client,
- self.query_servers, # server lookup object
- machine_name, # host we want
- "getSubmissionSummary")
+ # These may be null...
+ j["Args"] = get_string(job, "args1")
+ j["ReleaseReason"] = get_string(job, "released")
+ j["HoldReason"] = get_string(job, "held")
+ result.append(j)
+ return result
- # What we really want here is the job summaries from the
- # submission summary response. To get those, we have to
- # set an extra attribute on the client...
- query_client.set_enable_attributes(True)
- query_client.set_attributes({"includeJobSummaries": "true"})
+ def _get_submission_ids(self, host, page_size, mode, name=None, qdate=None):
- # Make a submission id. (see query wsdl)
- subId = query_client.factory.create('ns0:SubmissionID')
- subId.name = submission.Name
- subId.owner = submission.Owner
-
- t = CallThread(self.call_client_retry, my_callback,
- query_client, "getSubmissionSummary", subId)
- t.start()
-
- def _get_submission_ids(self, host, page_size, mode, host_is_endpoint,
- name=None, qdate=None):
-
def my_process_results(result):
# Fix up the exception message if necessary
result = self._pretty_result(result, host)
@@ -706,22 +789,6 @@
self.query_client_pool.return_object(query_client)
return res;
- def get_submission_ids_by_name(self, host, page_size, name,
- host_is_endpoint = False):
-
- return self._get_submission_ids(host, page_size, None,
- host_is_endpoint, name=name)
-
- def get_submission_ids_by_qdate(self, host, page_size, qdate, mode,
- host_is_endpoint = False):
-
- if long(qdate) > sys.maxint:
- raise Exception("Qdate is larger than max int")
- if not mode in ("BEFORE", "AFTER"):
- raise Exception("Mode must be 'BEFORE' or 'AFTER'")
- return self._get_submission_ids(host, page_size, mode,
- host_is_endpoint, qdate=qdate)
-
class _AviaryCommon(object):
def __init__(self, name, locator,
key="", cert="", root_cert="", domain_verify=True):
@@ -848,8 +915,8 @@
except Exception, e:
result = e
cb_args = process_results(result)
- sync.get_completion()(*cb_args)
- return sync
+ sync.get_completion()(*cb_args)
+ return MethodResult(sync)
class AviaryOperations(_AviaryCommon, _AviaryJobMethods, _AviaryQueryMethods):
def __init__(self, name, datadir,
Modified: branches/tmckay/sage/python/sage/qmf/qmfoperations.py
===================================================================
--- branches/tmckay/sage/python/sage/qmf/qmfoperations.py 2012-09-11 14:08:44 UTC (rev 5465)
+++ branches/tmckay/sage/python/sage/qmf/qmfoperations.py 2012-09-12 13:11:56 UTC (rev 5466)
@@ -167,9 +167,18 @@
# submission operations
def get_job_summaries(self, submission, callback, *args):
- assert callback
- return self._call(submission, "GetJobSummaries", callback, 0, 0)
+ '''
+ This method is asynchronous iff 'callback' is supplied.
+ kwargs will be searched for 'callback', 'default' and 'timeout' arguments.
+ '''
+
+ callback = "callback" in kwargs and kwargs["callback"] or None
+ default = "default" in kwargs and kwargs["default"] or None
+ timeout = "timeout" in kwargs and kwargs["timeout"] or 5
+
+ return self._call(submission, "GetJobSummaries", callback, default, timeout)
+
# Secret private implementation stuff, don't look!
def _call(self, obj, meth, cb, dflt, tout, *args):
Modified: branches/tmckay/sage/python/sage/util.py
===================================================================
--- branches/tmckay/sage/python/sage/util.py 2012-09-11 14:08:44 UTC (rev 5465)
+++ branches/tmckay/sage/python/sage/util.py 2012-09-12 13:11:56 UTC (rev 5466)
@@ -17,11 +17,17 @@
self.status will be 'OK' or 0 if the call succeeded and
may contain explanatory text otherwise
'''
- def __init__(self):
- self.data = None
- self.got_data = False
- self.error = False
- self.status = None
+ def __init__(self, src=None):
+ if isinstance(src, MethodResult):
+ self.data = src.data
+ self.got_data = src.got_data
+ self.error = src.error
+ self.status = src.status
+ else:
+ self.data = None
+ self.got_data = False
+ self.error = False
+ self.status = None
class ErrorResult(MethodResult):
'''
@@ -81,8 +87,11 @@
if not isinstance(status, Exception):
data = status
status = 0
+ elif len(args) == 2:
+ status, data = args[0:2]
else:
- status, data = args[0:2]
+ status = 0
+ data = args
self.status = status
if (status == 0) or (status == "OK"):
11 years, 8 months
r5465 - in branches/vmotoska: cumin/bin cumin/bin/test cumin/instance/etc cumin/model/access cumin/model/admin cumin/python/cumin cumin/python/cumin/account cumin/python/cumin/users wooly/bin wooly/python/wooly
by vmotoska@fedoraproject.org
Author: vmotoska
Date: 2012-09-11 14:08:44 +0000 (Tue, 11 Sep 2012)
New Revision: 5465
Added:
branches/vmotoska/cumin/python/cumin/users/
branches/vmotoska/cumin/python/cumin/users/__init__.py
branches/vmotoska/cumin/python/cumin/users/main.py
branches/vmotoska/cumin/python/cumin/users/main.strings
branches/vmotoska/cumin/python/cumin/users/widgets.py
branches/vmotoska/cumin/python/cumin/users/widgets.strings
Modified:
branches/vmotoska/cumin/bin/cumin-web
branches/vmotoska/cumin/bin/test/cumin-bench
branches/vmotoska/cumin/instance/etc/cumin.conf
branches/vmotoska/cumin/model/access/persona.xml
branches/vmotoska/cumin/model/admin/cumin.xml
branches/vmotoska/cumin/python/cumin/account/widgets.py
branches/vmotoska/cumin/python/cumin/admin.py
branches/vmotoska/cumin/python/cumin/authenticator.py
branches/vmotoska/cumin/python/cumin/config.py
branches/vmotoska/cumin/python/cumin/main.py
branches/vmotoska/cumin/python/cumin/widgets.py
branches/vmotoska/wooly/bin/wooly-demo
branches/vmotoska/wooly/python/wooly/demo.py
branches/vmotoska/wooly/python/wooly/pages.py
Log:
Users module snapshot, http auth in beta state, mobile app start...
Modified: branches/vmotoska/cumin/bin/cumin-web
===================================================================
--- branches/vmotoska/cumin/bin/cumin-web 2012-09-11 14:06:56 UTC (rev 5464)
+++ branches/vmotoska/cumin/bin/cumin-web 2012-09-11 14:08:44 UTC (rev 5465)
@@ -215,8 +215,8 @@
set_ldap_configs(cumin, values)
# Not used right now
- #cumin.auth_create_ondemand = values.auth_create_ondemand
- #cumin.auth_proxy = values.auth_proxy
+ cumin.auth_create_ondemand = values.auth_create_ondemand
+ cumin.auth_proxy = values.auth_proxy
# Someday we may let this be configurable, for now it will
# be hardwired
Modified: branches/vmotoska/cumin/bin/test/cumin-bench
===================================================================
--- branches/vmotoska/cumin/bin/test/cumin-bench 2012-09-11 14:06:56 UTC (rev 5464)
+++ branches/vmotoska/cumin/bin/test/cumin-bench 2012-09-11 14:08:44 UTC (rev 5465)
@@ -4,7 +4,7 @@
from wooly.bench import BenchmarkHarness
-from cumin import Cumin
+from cumin.main import Cumin
from cumin.config import *
def do_main():
Modified: branches/vmotoska/cumin/instance/etc/cumin.conf
===================================================================
--- branches/vmotoska/cumin/instance/etc/cumin.conf 2012-09-11 14:06:56 UTC (rev 5464)
+++ branches/vmotoska/cumin/instance/etc/cumin.conf 2012-09-11 14:08:44 UTC (rev 5465)
@@ -1,11 +1,18 @@
[common]
-# database: dbname=cumin user=cumin host=localhost
-# brokers: localhost:5672
-# debug: False
+database: dbname=cumin user=cumin host=localhost
+#rokers: localhost:5672
+brokers: cumin/rasca@172.22.80.10:5672
+debug: True
+aviary-job-servers: http://172.22.80.10:9090,http://zodiac.sors.local:9090
+aviary-query-servers: http://172.22.80.10:9091,http://zodiac.sors.local:9091
[web]
# host: localhost
-# host: 0.0.0.0
-user: guest
+host: 0.0.0.0
+user: motto
+persona: default
+auth-proxy: false
+auth-create-ondemand: false
+auth: ldap=ldap://127.0.0.1?dc=test,dc=local?sub?cn=%%s
[data]
Modified: branches/vmotoska/cumin/model/access/persona.xml
===================================================================
--- branches/vmotoska/cumin/model/access/persona.xml 2012-09-11 14:06:56 UTC (rev 5464)
+++ branches/vmotoska/cumin/model/access/persona.xml 2012-09-11 14:08:44 UTC (rev 5465)
@@ -6,6 +6,7 @@
<Module name="grid"/>
<Module name="inventory"/>
<Module name="usergrid"/>
+ <Module name="users" />
<GroupAccess name="nogroup">
<MainPage name="login.html"/>
@@ -29,6 +30,7 @@
<Module name="grid"/>
<Module name="inventory"/>
<Module name="usergrid"/>
+ <Module name="users" />
<GroupAccess name="nogroup">
<MainPage name="login.html"/>
Modified: branches/vmotoska/cumin/model/admin/cumin.xml
===================================================================
--- branches/vmotoska/cumin/model/admin/cumin.xml 2012-09-11 14:06:56 UTC (rev 5464)
+++ branches/vmotoska/cumin/model/admin/cumin.xml 2012-09-11 14:08:44 UTC (rev 5465)
@@ -7,6 +7,7 @@
<class name="User">
<property name="name" type="sstr" index="y"/>
<property name="password" type="sstr"/>
+ <property name="pin" type="sstr" optional="y"/>
</class>
<class name="Role">
Modified: branches/vmotoska/cumin/python/cumin/account/widgets.py
===================================================================
--- branches/vmotoska/cumin/python/cumin/account/widgets.py 2012-09-11 14:06:56 UTC (rev 5464)
+++ branches/vmotoska/cumin/python/cumin/account/widgets.py 2012-09-11 14:08:44 UTC (rev 5465)
@@ -3,6 +3,7 @@
from wooly import *
from wooly.widgets import *
from wooly.resources import *
+from wooly.forms import *
from cumin import *
from cumin.objecttask import *
@@ -10,6 +11,7 @@
from cumin.util import *
import main
+import random
from wooly import Session
@@ -34,7 +36,76 @@
self.settings = SettingsFrame(app, "main")
self.add_tab(self.settings)
-
+
+class MobileSettingsForm(FoldingFieldSubmitForm):
+ def __init__(self, app, name):
+ super(MobileSettingsForm, self).__init__(app, name)
+
+ self.enable_mobile_access = self.MobileEnableBox(app, "mobile_access")
+ self.add_field(self.enable_mobile_access)
+
+ self.mobile_pin_field = self.MobilePinField(app, "mobile_pin")
+ self.mobile_pin_field.input.size = 40
+ self.mobile_pin_field.required = False
+ self.mobile_pin_field.input.disabled = True
+ self.add_field(self.mobile_pin_field)
+
+
+ def render_title(self, session):
+ return "Mobile Settings"
+
+ def render_content(self, session, *args):
+ user = session.client_session.attributes["login_session"].user
+ (pin, msg) = self.app.authenticator.get_pin(user.name)
+ self.mobile_pin_field.input.param.set(session, pin)
+ if pin:
+ self.enable_mobile_access.param.set(session, "yes" )
+ return self.content.render(session, *args)
+
+ def process_submit(self, session):
+ user = session.client_session.attributes["login_session"].user
+ if hasattr(user, "load"):
+ user.load(session.cursor)
+
+ try:
+
+ if self.enable_mobile_access.param.get(session) == "yes":
+ state = "enabled"
+ if not self.mobile_pin_field.input.param.get(session):
+ val = "".join([ random.choice("".join(map(chr, range(97, 123)))) for i in range(0,10) ])
+ else:
+ val = self.mobile_pin_field.input.param.get(session)
+
+ (msg, pin ) = self.app.authenticator.update_pin(user.name, val)
+ else:
+ ( msg, pin ) = self.app.authenticator.update_pin(user.name, None )
+ state = "disabled"
+
+ except Exception, e:
+ log.debug("Problem occured during pin setup %s " % e)
+
+ if not self.errors.get(session):
+ session.add_notice(Notice(preamble="Settings updated",
+ message="Mobile access is now %s" % state,
+ capitalize=False))
+ url = self.return_url.get(session)
+ self.page.redirect.set(session, url)
+
+
+ class MobilePinField(StringField):
+ def render_title(self, session):
+ return "Mobile Auth String"
+
+ class MobileEnableBox(TwoOptionRadioField):
+ def render_title(self, session):
+ return "Enable mobile access"
+
+ def render_title_1(self, session):
+ return "Yes"
+
+ def render_title_2(self, session):
+ return "No"
+
class SettingsFrame(Frame):
def __init__(self, app, name):
super(SettingsFrame, self).__init__(app, name)
@@ -42,9 +113,19 @@
self.change_password_form = ChangePasswordForm(app, "change_password")
self.app.form_page.modes.add_mode(self.change_password_form)
+ my_linkset = LinkSet(app, "settings_liks")
+
link = self.ChangePasswordLink(app, "change_password")
- self.add_child(link)
+ my_linkset.add_link(link)
+ self.mobile_settings_form = MobileSettingsForm(app, "mobile_settings")
+ self.app.form_page.modes.add_mode(self.mobile_settings_form)
+
+ link2 = self.MobileSettingsLink(app, "mobile_settings")
+ my_linkset.add_link(link2)
+
+ self.add_child(my_linkset)
+
def render_title(self, session):
return "Settings"
@@ -52,7 +133,7 @@
def render_href(self, session):
nsession = wooly.Session(self.app.form_page)
form = self.frame.change_password_form
-
+
form.return_url.set(nsession, session.marshal())
form.show(nsession)
@@ -61,6 +142,19 @@
def render_content(self, sessino):
return "Change password"
+ class MobileSettingsLink(Link):
+ def render_href(self, session):
+ nsession = wooly.Session(self.app.form_page)
+ form = self.frame.mobile_settings_form
+
+ form.return_url.set(nsession, session.marshal())
+ form.show(nsession)
+
+ return nsession.marshal()
+
+ def render_content(self, session):
+ return "Mobile Settings"
+
class LoginPage(HtmlPage):
def __init__(self, app, name):
super(LoginPage, self).__init__(app, name)
@@ -197,7 +291,7 @@
# changes, refresh the user object
if hasattr(user, "load"):
user.load(session.cursor)
-
+
new0 = self.new0.get(session)
new1 = self.new1.get(session)
Modified: branches/vmotoska/cumin/python/cumin/admin.py
===================================================================
--- branches/vmotoska/cumin/python/cumin/admin.py 2012-09-11 14:06:56 UTC (rev 5464)
+++ branches/vmotoska/cumin/python/cumin/admin.py 2012-09-11 14:08:44 UTC (rev 5465)
@@ -88,6 +88,10 @@
cls = self.app.model.com_redhat_cumin.Role
return cls.get_object(cursor, name=name)
+ def get_role_by_id(self, cursor, id):
+ cls = self.app.model.com_redhat_cumin.Role
+ return cls.get_object(cursor, _id=id)
+
def add_role(self, cursor, name):
cls = self.app.model.com_redhat_cumin.Role
@@ -153,7 +157,6 @@
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)
Modified: branches/vmotoska/cumin/python/cumin/authenticator.py
===================================================================
--- branches/vmotoska/cumin/python/cumin/authenticator.py 2012-09-11 14:06:56 UTC (rev 5464)
+++ branches/vmotoska/cumin/python/cumin/authenticator.py 2012-09-11 14:08:44 UTC (rev 5465)
@@ -145,6 +145,7 @@
if conn:
filter = self._gen_filter(self.url.filterstr, user)
try:
+ log.debug("%s", filter)
res = conn.search_s(self.url.dn, self.url.scope, filter)
except Exception, e:
msg = str(e)
@@ -390,9 +391,10 @@
authen_map = {'script': CuminAuthenticatorScript}
class CuminAuthenticator(object):
- def __init__(self, app, log_authentication=True):
+ def __init__(self, app, log_authentication=True, autocreate=False):
self.authenticators=[]
self.app = app
+ self.autocreate = autocreate
_syslog.log_auth = log_authentication
log.info("Initializing %s", self)
if not have_ldap:
@@ -502,14 +504,14 @@
_syslog.log(msg)
return user, res
- def find_user(self, username):
+ def find_user(self, username, ignore_external=False):
# 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
+ res = user and ( len(user.password) > 0 or ignore_external )
if not res:
# Try to find the user externally
log.debug("Authenticator: finding external user")
@@ -522,6 +524,66 @@
break
return user, res
+ def create_external_user(self, username):
+ # after propper testing remove ...
+ if not self.autocreate:
+ return False
+
+ log.debug("Authenticator: creating external user %s", username)
+ conn = self.app.database.get_connection()
+ try:
+ curs = conn.cursor()
+ cls = self.app.model.com_redhat_cumin.User
+ found = cls.get_object(curs, name=username)
+ if found:
+ log.error("Attempt to create external user that exists")
+ return False
+ usr = cls.create_object(curs)
+ usr.name = username
+ usr.password = ""
+ usr.pin = ""
+ usr.fake_qmf_values()
+ usr.save(curs)
+ conn.commit()
+
+ except Exception ,e:
+ log.error("%s", e)
+ log.error("Unable to add ondemand user")
+ finally:
+ conn.close()
+ return True
+
+ def update_pin(self, username, pin):
+ conn = self.app.database.get_connection()
+ try:
+ cur = conn.cursor()
+ cls = self.app.model.com_redhat_cumin.User
+ user = cls.get_object(cur, name=username)
+ if user is None:
+ message = "Error locationg user %s " % username
+ else:
+ message = ""
+ user.pin = pin
+ user.save(cur)
+ conn.commit()
+ finally:
+ conn.close()
+ return pin, message
+
+ def get_pin(self, username):
+ 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:
+ message = "Error locationg user %s " % username
+ pin = ""
+ else:
+ message = ""
+ pin = user.pin
+ return pin, message
+
+
+
def update_password(self, username, oldpassword, newpassword):
status = False
message = ""
Modified: branches/vmotoska/cumin/python/cumin/config.py
===================================================================
--- branches/vmotoska/cumin/python/cumin/config.py 2012-09-11 14:06:56 UTC (rev 5464)
+++ branches/vmotoska/cumin/python/cumin/config.py 2012-09-11 14:08:44 UTC (rev 5465)
@@ -178,9 +178,12 @@
param = ConfigParameter(self, "authorize", bool)
param.default = False
-# param = ConfigParameter(self, "auth-proxy", bool)
-# param.default = False
+ param = ConfigParameter(self, "auth-proxy", bool)
+ param.default = False
+ param = ConfigParameter(self, "auth-create-ondemand", bool)
+ param.default = False
+
param = ConfigParameter(self, "wallaby-broker", str)
param.default = ""
Modified: branches/vmotoska/cumin/python/cumin/main.py
===================================================================
--- branches/vmotoska/cumin/python/cumin/main.py 2012-09-11 14:06:56 UTC (rev 5464)
+++ branches/vmotoska/cumin/python/cumin/main.py 2012-09-11 14:08:44 UTC (rev 5465)
@@ -162,7 +162,8 @@
try:
m = __import__(name, globals())
m.Module(self, name)
- except ImportError:
+ except Exception,e:
+ log.debug("Failed to load module %s (%s)", name, e)
pass
except Exception, e:
import traceback
@@ -174,10 +175,9 @@
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.authenticator = CuminAuthenticator(self, autocreate=self.auth_create_ondemand)
self.authorizator = CuminAuthorizator(self, self.access_path, self.do_authorize)
try:
self.authorizator.set_persona(self.persona)
@@ -351,7 +351,7 @@
self.main = main_view
self.add_mode(self.main)
self.set_default_frame(self.main)
- self.cumin_module = ["messaging", "grid"]
+ self.cumin_module = ["messaging", "grid" , "users" ]
self.page_html_class = "Cumin"
@@ -556,6 +556,7 @@
"is not")
self.adapter.query.add_filter(filter)
+
class TopSubmissionTable(TopTable):
def __init__(self, app, name):
cls = app.model.com_redhat_grid.Submission
Added: branches/vmotoska/cumin/python/cumin/users/__init__.py
===================================================================
--- branches/vmotoska/cumin/python/cumin/users/__init__.py (rev 0)
+++ branches/vmotoska/cumin/python/cumin/users/__init__.py 2012-09-11 14:08:44 UTC (rev 5465)
@@ -0,0 +1 @@
+from main import *
Added: branches/vmotoska/cumin/python/cumin/users/main.py
===================================================================
--- branches/vmotoska/cumin/python/cumin/users/main.py (rev 0)
+++ branches/vmotoska/cumin/python/cumin/users/main.py 2012-09-11 14:08:44 UTC (rev 5465)
@@ -0,0 +1,58 @@
+from cumin.main import CuminModule
+from cumin.util import *
+
+from widgets import *
+
+strings = StringCatalog(__file__)
+
+class Module(CuminModule):
+ def __init__(self, app, name):
+ super(Module, self).__init__(app, name)
+ self.frame = UsersMainFrame(app, "users")
+ self.frame.cumin_module = name
+
+ def init(self):
+ self.app.main_page.main.users = self.frame
+ self.app.main_page.main.add_tab(self.frame)
+
+
+class UsersMainFrame(CuminFrame):
+ def __init__(self, app, name):
+ super(UsersMainFrame, self).__init__(app, name)
+
+ self.view = UsersMainView(app, "view")
+ self.add_mode(self.view)
+
+ self.userdetail = UserDetailFrame(app, "userdetail")
+ self.add_mode(self.userdetail)
+
+ self.addform = AddUserForm(app, "addform")
+ self.app.form_page.modes.add_mode(self.addform)
+
+ def init(self):
+ super(UsersMainFrame, self).init()
+
+ def render_title(self, session):
+ return "Users"
+
+class UsersMainView(Widget):
+ def __init__(self, app, name):
+ super(UsersMainView, self).__init__(app, name)
+
+ heading = self.Heading(app, "heading")
+ self.add_child(heading)
+
+ self.tabs = TabbedModeSet(app, "tabs")
+ self.add_child(self.tabs)
+
+ self.tabs.add_tab(UsersSelector(app, "users"))
+ self.tabs.add_tab(RolesSelector(app, "roles"))
+
+ class Heading(CuminHeading):
+ def render_title(self, session):
+ return "Users"
+
+ def render_icon_href(self, session):
+ return "resource?name=client-36.png"
+
+
Added: branches/vmotoska/cumin/python/cumin/users/main.strings
===================================================================
Added: branches/vmotoska/cumin/python/cumin/users/widgets.py
===================================================================
--- branches/vmotoska/cumin/python/cumin/users/widgets.py (rev 0)
+++ branches/vmotoska/cumin/python/cumin/users/widgets.py 2012-09-11 14:08:44 UTC (rev 5465)
@@ -0,0 +1,305 @@
+from wooly.widgets import *
+from cumin.widgets import *
+from crypt import crypt
+from random import sample
+
+from cumin.objectselector import ObjectSelector, ObjectLinkColumn, ObjectTable, ObjectTableColumn
+from cumin.objectframe import ObjectFrame
+
+from cumin.sqladapter import ObjectSqlAdapter
+
+strings = StringCatalog(__file__)
+log = logging.getLogger("cumin.users.widgets")
+
+
+
+class UsernameSearchColumn(ObjectLinkColumn):
+ def __init__(self, app, name, attr, idx, frame):
+ super(UsernameSearchColumn, self).__init__(app, name, attr, idx, frame)
+ self.attr.title = "Login"
+
+class UserRoleAssignmentColumn(ObjectTableColumn):
+ def __init__(self, app, name, attr):
+ super(UserRoleAssignmentColumn, self).__init__(app, name, attr)
+ self.attr.title = "Roles"
+
+class BooleanUserColumn(ObjectTableColumn):
+ def __init__(self, app, name, attr):
+ super(BooleanUserColumn, self).__init__(app, name, attr)
+ self.attr.invert = False
+
+ def render_cell_content(self, session, record):
+ if self.field.get_content(session, record):
+ return True and not self.attr.invert
+ else:
+ return False or self.attr.invert
+
+class ExternalUserColumn(BooleanUserColumn):
+ def __init__(self, app, name, attr):
+ super(ExternalUserColumn, self).__init__(app, name, attr)
+ self.attr.title = "External User"
+ self.attr.invert = True
+
+class MobileUserColumn(BooleanUserColumn):
+ def __init__(self, app, name, attr):
+ super(MobileUserColumn, self).__init__(app, name, attr)
+ self.attr.title = "Mobile Access"
+
+class UserDataAdapter(ObjectSqlAdapter):
+ def __init__(self, app):
+ # TODO: other way to do this ?
+ role_cls = app.model.com_redhat_cumin.Role
+ cls = app.model.com_redhat_cumin.UserRoleMapping
+ user_cls = app.model.com_redhat_cumin.User
+
+ super(UserDataAdapter, self).__init__(app, cls)
+
+ self.add_join(user_cls, cls.user , user_cls._id)
+ self.add_join(role_cls, cls.role, role_cls._id)
+ # for postgresql 8.4+ array_agg ?
+ # is support for 8.4 < needed ?
+ def get_data(self, values, options):
+ if options.limit:
+ opts_limit = options.limit
+ else:
+ opts_limit = None
+
+ if options.offset:
+ opts_offset = options.offset
+ else:
+ opts_offset = 0
+
+ options.limit = 100000
+ options.offset = 0
+ data = super(UserDataAdapter, self).get_data(values, options)
+ res = []
+ if len(data) > 0:
+ for userrow in data:
+ user_record = list(userrow)
+ tmp_res = [user_record[0],user_record[1],user_record[2],user_record[3],user_record[4],'']
+ added = False
+ for i,r in enumerate(res):
+ if r[1] == user_record[1]:
+ groups = res[i][3]
+ res[i][3] = "%s,%s" % ( groups, user_record[3])
+ added = True
+ break
+
+ if not added:
+ res.append(tmp_res)
+
+ last = len(res)
+ if opts_limit:
+ last = opts_offset + opts_limit
+
+ return res[opts_offset:last]
+
+ def get_count(self, values):
+ options = DataAdapterOptions()
+ options.limit = 100000
+ options.offset = 0
+ return len(self.get_data(values,options))
+
+
+class UsersRemove(ObjectSelectorTask):
+ def get_title(self, session):
+ return "Remove"
+
+ def do_exit(self, session):
+ self.app.main_page.main.users.view.show(session)
+
+
+class UsersSelector(ObjectSelector):
+ def __init__(self, app, name):
+ cls = app.model.com_redhat_cumin.UserRoleMapping
+ role_cls = app.model.com_redhat_cumin.Role
+ user_cls = app.model.com_redhat_cumin.User
+
+ super(UsersSelector, self).__init__(app, name, cls)
+ self.table.adapter = UserDataAdapter(app)
+ frame = "main.users.userdetail"
+ col = UsernameSearchColumn(app, "name", user_cls.name, user_cls._id, frame)
+
+ ext_col = ExternalUserColumn(app, "external", user_cls.password )
+ mob_col = MobileUserColumn(app, "mobile", user_cls.pin)
+ rol_col = UserRoleAssignmentColumn(app, "roles",role_cls.name)
+ self.add_column(col)
+ self.add_column(rol_col)
+ self.add_column(ext_col)
+ self.add_column(mob_col)
+ self.add_search_filter(col)
+ self.remove = UsersRemove(app, self)
+
+ self.links.add_child(AddUserLink(app, "adduser"))
+
+ def create_table(self, app, name, cls):
+ return UsersTable(app, name, cls)
+
+ def render_title(self, session):
+ return "Users"
+
+class UsersTable(ObjectSelectorTable):
+ def __init__(self, app, name, cls):
+ super(UsersTable, self).__init__(app, name, cls)
+
+
+class UserDetailFrame(ObjectFrame):
+ def __init__(self, app, name):
+ cls = app.model.com_redhat_cumin.User
+ super(UserDetailFrame, self).__init__(app, name, cls)
+ self.icon_href = "resource?name=client-36.png"
+
+class AddUserLink(Link):
+ def __init__(self, app, name):
+ super(AddUserLink, self).__init__(app, name)
+ self.form = AddUserForm(app, "addform")
+
+ def render_href(self, session):
+ ns = wooly.Session(self.app.form_page)
+ form = self.frame.addform
+ form.return_url.set(ns, session.marshal())
+ form.show(ns)
+ return ns.marshal()
+
+ def render_content(self, session):
+ return "Add User"
+
+class AddUserForm(FoldingFieldSubmitForm):
+ def __init__(self, app, name):
+ super(AddUserForm, self).__init__(app, name)
+
+ self.login_field = AddLoginField(app, "login_field")
+ self.add_field(self.login_field)
+
+ self.pass_field = AddPasswordField(app, "pass_field")
+ self.add_field(self.pass_field)
+
+ self.group_select = AddGroupSelectField(app, "group_field")
+ self.add_field(self.group_select)
+
+ def render_title(self, session):
+ return "Add User"
+
+ def process_submit(self, session):
+ self.validate(session)
+
+ passw = self.pass_field.input.param.get(session)
+ if len(passw) > 0:
+ crypted = crypt(passw, "".join(sample("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 2)))
+ else:
+ crypted = ""
+
+ login = self.login_field.input.param.get(session)
+ groups_ids = self.group_select.input.param.get(session)
+ try:
+ user_obj = self.app.admin.add_user(session.cursor, login, crypted)
+ for g in groups_ids:
+ group_obj = self.app.admin.get_role_by_id(session.cursor, g)
+ self.app.admin.add_assignment(session.cursor, user_obj, group_obj)
+ except Exception,e:
+ log.error("Problem adding user %s : %s",login, e)
+ err = FormError("Problem adding user, Please see the log file for details");
+ self.errors.append(err)
+
+
+ if not self.errors.get(session):
+ session.add_notice(Notice(preamble="User %s added" % login ,
+ message="OK",
+ capitalize=False))
+ url = self.return_url.get(session)
+ self.page.redirect.set(session, url)
+
+class AddLoginField(StringField):
+ def render_title(self, session):
+ return "Login"
+
+ def validate(self, session):
+ err = ""
+ login = self.input.param.get(session)
+ if len(login) < 3:
+ err = "Login must be longer than 3 characters."
+
+ cls = self.app.model.com_redhat_cumin.User
+ db_user = cls.get_object(session.cursor, name=login)
+
+ if db_user:
+ err = "User with login %s already exists." % login
+
+ if err:
+ errform = FormError(err)
+ self.form.errors.add(session ,errform)
+
+class AddPasswordField(StringField):
+ def render_title(self, session):
+ return "Password"
+
+ def render_help(self, session):
+ return "For external user leave password blank"
+
+ def validate(self, session):
+ passw = self.input.param.get(session)
+ if len(passw) > 0 and len(passw) < 5:
+ err = FormError("Password length should be at least 5 characters")
+ self.form.errors.add(err)
+
+class AddGroupSelectField(ScalarField):
+ def __init__(self, app, name):
+ super(AddGroupSelectField, self).__init__(app, name, None)
+
+ param = StringParameter(app, "param")
+ self.param = ListParameter(app, "param", param)
+ self.add_parameter(self.param)
+
+ self.input = self.GroupOptions(app, "input", self.param)
+ self.add_child(self.input)
+
+ def get(self, session):
+ return self.input.param.get(session)
+
+ def validate(self, session):
+ super(AddGroupSelectField, self).validate(session)
+
+ roles = self.input.param.get(session)
+ if not roles or len(roles) == 0:
+ error = FormError("No role selected")
+ self.form.errors.add(session, error)
+
+ def render_title(self, session):
+ return "Role"
+
+ class GroupOptions(OptionInputSet):
+ def get_items(self, session):
+ return self.do_get_items(session)
+
+ def do_get_items(self, session):
+ cls = self.app.model.com_redhat_cumin.Role
+ roles = cls.get_selection(session.cursor)
+ return roles
+
+ def render_disabled_attr(self, session):
+ # hack for multiple select
+ return "multiple=\"multiple\""
+
+ def render_item_value(self, session, item):
+ return item._id
+
+ def render_item_content(self, session, item):
+ return item.name
+
+ def render_item_selected_attr(self, session, item):
+ # TODO: str
+ if str(item._id) in self.param.get(session):
+ return "selected=\"selected\""
+
+class RolesFrame(ObjectFrame):
+ def __init__(self, app, name):
+ cls = app.model.com_redhat_cumin.Role
+ super(RolesFrame, self).__init__(app, name, cls)
+
+class RolesSelector(ObjectSelector):
+ def __init__(self, app, name):
+ cls = app.model.com_redhat_cumin.Role
+
+ super(RolesSelector, self).__init__(app, name, cls)
+ self.add_attribute_column(cls.name)
+
Added: branches/vmotoska/cumin/python/cumin/users/widgets.strings
===================================================================
Modified: branches/vmotoska/cumin/python/cumin/widgets.py
===================================================================
--- branches/vmotoska/cumin/python/cumin/widgets.py 2012-09-11 14:06:56 UTC (rev 5464)
+++ branches/vmotoska/cumin/python/cumin/widgets.py 2012-09-11 14:08:44 UTC (rev 5465)
@@ -1324,11 +1324,12 @@
if self.app.auth_proxy:
try:
username = session.request_environment['HTTP_REMOTE_USER']
+ log.debug("Proxy auth user %s", username)
except KeyError:
log.debug("Proxy auth enabled but no remote user set")
if username:
- user, ok = self.app.authenticator.find_user(username)
+ user, ok = self.app.authenticator.find_user(username, ignore_external= ( self.app.auth_create_ondemand or self.app.auth_proxy ) )
if not ok:
# We couldn't find the user, internally or externally
if not self.app.auth_proxy:
@@ -1337,14 +1338,17 @@
else:
log.info("User %s not found in db, "\
"using auth proxy", username )
- #Hmmm, what is users here? For now just let it return
- # to the login page
- return False
+ if self.app.auth_create_ondemand:
+ log.info("Creating user ondemand %s", username)
+ if not self.app.authenticator.create_external_user(username):
+ return False
+ else:
+ user, ok = self.app.authenticator.find_user(username, ignore_external=True)
+ if not ok:
+ return False
+ else:
+ return False
-#TODO prehodit do authenticatora
-# ondemand -> authenticator.create_user
-# user -> authenticator.force_login
-
# Check for valid group
if self.app.authorizator.is_enforcing():
if not user:
Modified: branches/vmotoska/wooly/bin/wooly-demo
===================================================================
--- branches/vmotoska/wooly/bin/wooly-demo 2012-09-11 14:06:56 UTC (rev 5464)
+++ branches/vmotoska/wooly/bin/wooly-demo 2012-09-11 14:08:44 UTC (rev 5465)
@@ -2,6 +2,7 @@
import sys
import os
+from time import sleep
from wooly.demo import *
Modified: branches/vmotoska/wooly/python/wooly/demo.py
===================================================================
--- branches/vmotoska/wooly/python/wooly/demo.py 2012-09-11 14:06:56 UTC (rev 5464)
+++ branches/vmotoska/wooly/python/wooly/demo.py 2012-09-11 14:08:44 UTC (rev 5465)
@@ -4,6 +4,7 @@
from util import *
from widgets import *
from wooly import Application
+from time import sleep
strings = StringCatalog(__file__)
@@ -26,12 +27,17 @@
self.add_resource_dir(os.path.join(self.home, "resources"))
+ self.force_html_doctype = False
+
def start(self):
self.model.start()
def stop(self):
self.model.stop()
+ def authorize_cb(self, s, a):
+ return True
+
class DemoModel(object):
def __init__(self, app):
super(DemoModel, self).__init__()
Modified: branches/vmotoska/wooly/python/wooly/pages.py
===================================================================
--- branches/vmotoska/wooly/python/wooly/pages.py 2012-09-11 14:06:56 UTC (rev 5464)
+++ branches/vmotoska/wooly/python/wooly/pages.py 2012-09-11 14:08:44 UTC (rev 5465)
@@ -474,3 +474,10 @@
def do_render(self, session, *args):
csv = self.render_content(session, *args)
return csv and csv or " "
+
+class JSONPage(WidgetPage):
+ def get_content_type(self, session):
+ return "application/json"
+
+ def do_render(slef, session, *args):
+ return " "
11 years, 8 months
r5464 - branches
by vmotoska@fedoraproject.org
Author: vmotoska
Date: 2012-09-11 14:06:56 +0000 (Tue, 11 Sep 2012)
New Revision: 5464
Added:
branches/vmotoska/
Log:
Creating play branch
11 years, 8 months
r5463 - branches/tmckay/sage/python/sage/aviary
by tmckay@fedoraproject.org
Author: tmckay
Date: 2012-09-11 13:46:59 +0000 (Tue, 11 Sep 2012)
New Revision: 5463
Modified:
branches/tmckay/sage/python/sage/aviary/aviaryoperations.py
Log:
Allow an operations object to be constructed with no locator and no server lists and detect host names that are full URLs. This allows Aviary endpoints to be
passed directly for all operations.
Modified: branches/tmckay/sage/python/sage/aviary/aviaryoperations.py
===================================================================
--- branches/tmckay/sage/python/sage/aviary/aviaryoperations.py 2012-09-10 19:41:17 UTC (rev 5462)
+++ branches/tmckay/sage/python/sage/aviary/aviaryoperations.py 2012-09-11 13:46:59 UTC (rev 5463)
@@ -254,11 +254,13 @@
if self.locator:
self.job_servers = ServerList(self.locator, "SCHEDULER", "JOB")
- else:
+ elif job_servers:
self.job_servers = FixedServerList(job_servers,
"9090",
"/services/job/",
"JOB")
+ else:
+ self.job_servers = None
job_wsdl = "file:" + os.path.join(get_datadir(datadir,"job"),
"aviary-job.wsdl")
@@ -447,11 +449,13 @@
if self.locator:
self.query_servers = ServerList(self.locator,
"CUSTOM", "QUERY_SERVER")
- else:
+ elif query_servers:
self.query_servers = FixedServerList(query_servers,
"9091",
"/services/query/",
"QUERY_SERVER")
+ else:
+ self.query_servers = None
query_wsdl = "file:" + os.path.join(get_datadir(datadir,"query"),
"aviary-query.wsdl")
@@ -688,13 +692,9 @@
subId.name = name
subId.qdate = qdate
- if host_is_endpoint:
- servers = None
- else:
- servers = self.query_servers
self._setup_client(query_client,
- servers, # server lookup object
- host, # host we want
+ self.query_servers, # server lookup object
+ host, # host we want
"getSubmissionID")
res = self._call_sync(my_process_results,
@@ -748,17 +748,19 @@
return {"INTEGER": int, "FLOAT": float, "STRING": str, "BOOLEAN": bool}
def _set_client_info(self, client, refresh=False):
- # If there is no server_list, then client.server_name is expected to
- # be an endpoint
- if client.server_list:
+ # See if the server_name is a full URL. If so, we'll use it.
+ url = parse_URL(client.server_name)
+ if url.scheme and url.host and url.port and url.path:
+ scheme = url.scheme
+ host = client.server_name
+
+ elif client.server_list:
scheme, host = client.server_list.find_server(client.server_name,
refresh)
else:
- # We need the scheme value separated out for the transport
- scheme = parse_URL(client.server_name).scheme
- if scheme is None:
- scheme = "http"
- host = client.server_name
+ log.debug("AviaryOperations: no server list specified and %s "\
+ "is not a URL" % client.server_name)
+ raise Exception("Cannot resolve %s to an Aviary endpoint" % client.server_name)
# Have to set the URL for the method. This might go away someday...
client.set_options(location=host+client.method_name)
@@ -856,10 +858,7 @@
query_servers="",
key="", cert="", root_cert="", domain_verify=True):
- # If job_servers and query_servers is not set, we must have a
- # locator. If locator has been set, it must be the right type
- # because it will override the server lists.
- if not (job_servers and query_servers) or locator is not None:
+ if locator is not None:
assert isinstance(locator, AviaryLocator)
super(AviaryOperations, self).__init__(name, locator,
@@ -876,10 +875,7 @@
job_servers="",
key="", cert="", root_cert="", domain_verify=True):
- # If job_servers is not set, we must have a
- # locator. If locator has been set, it must be the right type
- # because it will override the server list.
- if not job_servers or locator is not None:
+ if locator is not None:
assert isinstance(locator, AviaryLocator)
super(AviaryJobOperations, self).__init__(name, locator,
@@ -894,10 +890,7 @@
query_servers="",
key="", cert="", root_cert="", domain_verify=True):
- # If query_servers is not set, we must have a
- # locator. If locator has been set, it must be the right type
- # because it will override the server list.
- if not query_servers or locator is not None:
+ if locator is not None:
assert isinstance(locator, AviaryLocator)
super(AviaryQueryOperations, self).__init__(name, locator,
@@ -912,10 +905,6 @@
query_servers="",
key="", cert="", root_cert="", domain_verify=True):
- if not (job_servers or query_servers or locator_uri):
- raise Exception("locator_uri, job_servers, and query_servers "\
- "may not all be null strings")
-
# If locator uri has not been specified, it's disabled and we will
# use the specified job_servers and query_servers values
if locator_uri:
11 years, 8 months
r5462 - branches/tmckay/sage/python/sage/aviary
by tmckay@fedoraproject.org
Author: tmckay
Date: 2012-09-10 19:41:17 +0000 (Mon, 10 Sep 2012)
New Revision: 5462
Modified:
branches/tmckay/sage/python/sage/aviary/aviaryoperations.py
Log:
Support host:port in server lookup instead of just host.
In the future, the url list for a host could be a dictionary keyed on port
Modified: branches/tmckay/sage/python/sage/aviary/aviaryoperations.py
===================================================================
--- branches/tmckay/sage/python/sage/aviary/aviaryoperations.py 2012-09-10 18:13:00 UTC (rev 5461)
+++ branches/tmckay/sage/python/sage/aviary/aviaryoperations.py 2012-09-10 19:41:17 UTC (rev 5462)
@@ -68,10 +68,28 @@
'''
scheme = ""
host = ""
+ port = None
+
+ # Support hostnames with ports, too.
+ if ":" in name:
+ u = parse_URL(name)
+ name = u.host
+ port = u.port
+
if name in servers:
+ url = None
urls = servers[name]
- if len(urls) > 0:
+ if port is not None:
+ # find the matching port
+ for u in urls:
+ if u.port == port:
+ url = u
+ break
+
+ elif len(urls) > 0:
url = random.sample(urls, 1)[0]
+
+ if url:
scheme = url.scheme
host = str(url)
# A particular method name is going to be appended to path,
11 years, 8 months
r5461 - branches/tmckay/cumin/python/cumin/grid
by tmckay@fedoraproject.org
Author: tmckay
Date: 2012-09-10 18:13:00 +0000 (Mon, 10 Sep 2012)
New Revision: 5461
Modified:
branches/tmckay/cumin/python/cumin/grid/limit.py
Log:
Tweak, reuse a temporary
Modified: branches/tmckay/cumin/python/cumin/grid/limit.py
===================================================================
--- branches/tmckay/cumin/python/cumin/grid/limit.py 2012-09-10 18:08:13 UTC (rev 5460)
+++ branches/tmckay/cumin/python/cumin/grid/limit.py 2012-09-10 18:13:00 UTC (rev 5461)
@@ -153,8 +153,7 @@
def do_reconfig(self, invoc, negotiator):
cb = invoc.make_callback()
try:
- call_async(invoc.make_callback(),
- self.app.remote.reconfig, negotiator)
+ call_async(cb, self.app.remote.reconfig, negotiator)
except Exception, e:
cb(e)
11 years, 8 months
r5460 - in branches/tmckay: cumin/python/cumin/grid sage/python/sage
by tmckay@fedoraproject.org
Author: tmckay
Date: 2012-09-10 18:08:13 +0000 (Mon, 10 Sep 2012)
New Revision: 5460
Modified:
branches/tmckay/cumin/python/cumin/grid/job.py
branches/tmckay/cumin/python/cumin/grid/limit.py
branches/tmckay/cumin/python/cumin/grid/negotiator.py
branches/tmckay/sage/python/sage/util.py
Log:
Fix up error handling for remote reconfig() calls
Modified: branches/tmckay/cumin/python/cumin/grid/job.py
===================================================================
--- branches/tmckay/cumin/python/cumin/grid/job.py 2012-09-07 16:02:45 UTC (rev 5459)
+++ branches/tmckay/cumin/python/cumin/grid/job.py 2012-09-10 18:08:13 UTC (rev 5460)
@@ -20,16 +20,11 @@
from wooly.widgets import ModeSet, PropertySet, TemplateRenderer, Notice
from wooly.template import WidgetTemplate
from wooly.parameters import ListParameter, IntegerParameter, DictParameter
+from sage.util import ErrorResult
strings = StringCatalog(__file__)
log = logging.getLogger("cumin.job")
-class ErrorResult(object):
- def __init__(self, err):
- self.status = "Failed"
- self.error = err
- self.data = None
-
class SubmissionObjectFrame(ObjectFrame):
def get_submission_sched(self, session, id):
submission = self.get_object(session, id)
Modified: branches/tmckay/cumin/python/cumin/grid/limit.py
===================================================================
--- branches/tmckay/cumin/python/cumin/grid/limit.py 2012-09-07 16:02:45 UTC (rev 5459)
+++ branches/tmckay/cumin/python/cumin/grid/limit.py 2012-09-10 18:08:13 UTC (rev 5460)
@@ -151,8 +151,12 @@
def do_invoke(self, invoc, negotiator, limit_name, limit_max):
def do_reconfig(self, invoc, negotiator):
- call_async(invoc.make_callback(),
- self.app.remote.reconfig, negotiator)
+ cb = invoc.make_callback()
+ try:
+ call_async(invoc.make_callback(),
+ self.app.remote.reconfig, negotiator)
+ except Exception, e:
+ cb(e)
# Use a custom callback so we can launch reconfig() as a
# second asynchronous task if set_limit succeeds and let that
Modified: branches/tmckay/cumin/python/cumin/grid/negotiator.py
===================================================================
--- branches/tmckay/cumin/python/cumin/grid/negotiator.py 2012-09-07 16:02:45 UTC (rev 5459)
+++ branches/tmckay/cumin/python/cumin/grid/negotiator.py 2012-09-10 18:08:13 UTC (rev 5460)
@@ -10,7 +10,7 @@
from wooly.parameters import ListParameter, StringParameter
from wooly.template import WidgetTemplate
from cumin.stat import StatFlashChart
-from sage.util import call_async
+from sage.util import call_async, ErrorResult
strings = StringCatalog(__file__)
log = logging.getLogger("cumin.grid.negotiator")
@@ -243,6 +243,8 @@
self.buttons = list()
+ self.reconfig_task = NegotiatorReconfigTask(self.app, self.task.frame)
+
#self.defer_enabled = True
def render_group_name(self, session, group):
@@ -381,7 +383,7 @@
changed = True
if changed:
- self.task.reconfig(negotiator)
+ self.reconfig_task.invoke(session, negotiator)
self.app.model.update_negotiator_config_value(negotiator)
def process_submit(self, session):
@@ -430,6 +432,13 @@
def render_height(self, session):
return 210
+class NegotiatorReconfigTask(ObjectFrameTask):
+ def do_invoke(self, invoc, negotiator):
+ invoc.description = "Reconfig"
+ result = self.app.remote.reconfig(negotiator)
+ invoc.status_code = result.status
+ invoc.end()
+
class NegotiatorGroupTask(ObjectFrameTask):
def do_exit(self, session):
self.app.main_page.main.grid.view.show(session)
@@ -447,9 +456,6 @@
invoc.status_code = result.status
invoc.end()
- def reconfig(self, negotiator):
- self.app.remote.reconfig(negotiator)
-
class NegotiatorEditDynamicQuota(NegotiatorGroupTask):
def __init__(self, app, frame):
super(NegotiatorEditDynamicQuota, self).__init__(app, frame)
Modified: branches/tmckay/sage/python/sage/util.py
===================================================================
--- branches/tmckay/sage/python/sage/util.py 2012-09-07 16:02:45 UTC (rev 5459)
+++ branches/tmckay/sage/python/sage/util.py 2012-09-10 18:08:13 UTC (rev 5460)
@@ -13,7 +13,7 @@
or a descendent of this class.
self.data will contain any data returned from the call
self.got_data will be True if the call succeeded
- self.error will be True if the call raised an error
+ self.error will evaluate to True if the call raised an error
self.status will be 'OK' or 0 if the call succeeded and
may contain explanatory text otherwise
'''
@@ -23,6 +23,19 @@
self.error = False
self.status = None
+class ErrorResult(MethodResult):
+ '''
+ Simple error result type.
+ This is handy when enveloping code knows there
+ is some error condition and needs to send back
+ an error result as if the method returned a failed status.
+ '''
+ def __init__(self, err):
+ super(ErrorResult, self).__init__()
+
+ self.error = err
+ self.status = "Failed"
+
class CallSync(MethodResult):
'''
General callback object for asynchronous operations.
11 years, 8 months