[luci] Added inital version of tool for DB import/export and password set.
by Jan Pokorný
commit 5e0e0564f8ad2aa328bf9082f22dff8282a7add8
Author: Jan Pokorny <jpokorny(a)redhat.com>
Date: Fri Oct 30 17:23:35 2009 +0100
Added inital version of tool for DB import/export and password set.
I would like to finish import/export parts during next days.
luci_util.py | 418 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 418 insertions(+), 0 deletions(-)
---
diff --git a/luci_util.py b/luci_util.py
new file mode 100644
index 0000000..52e3c2d
--- /dev/null
+++ b/luci_util.py
@@ -0,0 +1,418 @@
+# -*- coding: utf-8 -*-
+
+"""Script for service routines connected with Luci.
+
+Actions available:
+ * set initial administrator's password/change current one
+ * import/export of the local database
+
+"""
+
+# Note to debugging:
+# Let the function/method to be debugged decorated with '_debug' decorator.
+# Special function 'debug_print()' serves for debug printing and is active
+# only when the corresponding function is decorated with '_debug'.
+# When function/method need no longer such kind of debugging, the decorator
+# can be removed and lines containing 'debug_print()' commented out.
+# When '_debug' decorator is used, it's definition can be commented out
+# or even removed from the code.
+
+
+__version__ = "0.1"
+
+
+from optparse import OptionParser, OptionGroup
+
+
+#
+# GLOBALS
+#
+
+USAGE = '%prog [-c <config file>] [-i <path>|-e <path>|-p <password>]\n' \
+ 'Use -h or --help to display details.'
+VERSION = '%%prog %s' % __version__
+
+#DEFAULT_INI_LOCATION = '/var/lib/luci/'
+DEFAULT_INI_LOCATION='~/tg2env/luci'
+
+DEFAULT_INI_FILENAME='development.ini'
+
+
+#
+# FUNCTIONS AND DECORATORS FOR DEVELOPMENT/DEBUGGING
+#
+
+def _debug(fn):
+ """For decoration of functions/methods to use verbose debugging.
+
+ Is uses generally unrecommended technique, but for this purpose it
+ should be ok.
+
+ """
+ fn_name = fn.func_name
+
+ # Local version of debug_print that really does the job.
+ def debug_print(*args, **kwargs):
+ print args, kwargs
+
+ def decorator(*args, **kwargs):
+
+ # Backup current function connected with 'debug_print' identifier
+ # in func_globals. Then set the version that really does the job.
+ old_debug_print = fn.func_globals.get('debug_print', None)
+ fn.func_globals['debug_print'] = debug_print
+
+ print '--> %s():' % fn_name
+ input = ''
+ if len(args) > 0:
+ input += '\t- args: '
+ input += ', '.join([repr(s) for s in args])
+ if len(kwargs) > 0:
+ input += '\t- kwargs: '
+ input += ', '.join([repr(s) for kd in (kwargs.items())])
+ print '\tinput:\n%s' % input
+
+ # Perform the decorated functions.
+ retval = fn(*args, **kwargs)
+
+ # Restore 'debug_print'.
+ if old_debug_print:
+ fn.func_globals['debug_print'] = old_debug_print
+
+ # Print some basic debug messages, what was called with what arguments
+ # and that was returned.
+ print '<-- %s():' % fn_name
+ print '\toutput: %s' % repr(retval)
+
+ return retval
+
+ return decorator
+
+
+# Default empty debug printing. Do not redefine, use '@_debug' decorator
+# for functions/methods instead.
+def debug_print(s):
+ pass
+
+
+#
+# EXCEPTIONS
+#
+
+class LuciUtilError(Exception):
+ """Just for creating a 'identifiable type' of an exception."""
+ pass
+
+
+#
+# HELPER FUNCTIONS
+#
+
+@_debug
+def _setParserOptions(parser):
+ """Set options for OptionParser instance.
+
+ The purpose is to gather setting of available options at one place.
+ Apart from it, it also serves to collect mutually exclusive options
+ to the lists of them, so this situation can be than easily discovered.
+
+ Keyword arguments:
+ parser OptionParser instance.
+
+ Return values:
+ (parser, mutex_opts_list) pair, where:
+ parser Passed instance of parser with some command-line options
+ added.
+ mutex_opts_list
+ List of lists of mutually exclusive options.
+
+ """
+
+ mutex_opts_list = []
+
+ mutex_opts_list.append([])
+ mutex_opts_index = 0
+
+ # ---
+ group_db = OptionGroup(parser, 'Whole database related options')
+ group_db.add_option('-i', '--import-db',
+ help='import existing database',
+ metavar='<path to DB file to import>',
+ action='store', type='string', dest='import_src')
+ group_db.add_option('-e', '--export-db',
+ help='export current database',
+ metavar='<path to DB file where to export>',
+ action='store', type='string', dest='export_dest')
+ mutex_opts_list[mutex_opts_index].append(group_db.get_option('-i'))
+ mutex_opts_list[mutex_opts_index].append(group_db.get_option('-e'))
+
+ # ---
+ group_misc = OptionGroup(parser, 'Miscellaneous options')
+ group_misc.add_option('-p', '--change-password',
+ help='change password (use \'-\' for direct input afterwards)',
+ metavar='<password>',
+ action='store', type='string', dest='pwd')
+ mutex_opts_list[mutex_opts_index].append(group_db.get_option('-p'))
+
+ # ---
+
+ parser.add_option_group(group_db)
+ parser.add_option_group(group_misc)
+
+ parser.add_option('-c', '--config',
+ #help='which configuration file for paster to use',
+ metavar='<configuration file for paster>',
+ action='store', type='string', dest='config_file')
+
+ return parser, mutex_opts_list
+
+
+@_debug
+def _getTG2ConfigParser(config_file):
+ """Create and return (Safe)ConfigParser instance with config file loaded.
+
+ If given path to config file is not an absolute path, its supposed that
+ given path is relative to DEFAULT_INI_LOCATION.
+
+ Keyword arguments:
+ config_file Path to the config file (usually with .ini extension).
+
+ Return values:
+ Created (Safe)ConfigParser instance with config file loaded.
+
+ Raises:
+ LuciUtilError
+
+ """
+ from os.path import isabs, split, normpath, join
+ from ConfigParser import SafeConfigParser
+
+ if not isabs(config_file):
+ config_file = join(DEFAULT_INI_LOCATION, config_file)
+
+ config_file = normpath(config_file)
+ config_file_dir = split(config_file)[0]
+
+ parser = SafeConfigParser({'here': config_file_dir})
+
+ if len(parser.read(config_file)) == 0:
+ raise LuciUtilError, "Could not find configuration file %s." \
+ % config_file
+
+ return parser
+
+
+@_debug
+def _SQLAlchemyURLFromConfig(config_file):
+ """Open given config file and get 'sqlalchemy.url' value from [app:main].
+
+ Keyword arguments:
+ config_file Path to the config file (usually with .ini extension).
+
+ Return values:
+ 'sqlalchemy.url' value obtained from the given config file.
+
+ Raises:
+ LuciUtilError
+
+ """
+ from ConfigParser import Error
+
+ try:
+ config_parser = _getTG2ConfigParser(config_file)
+ sql_alchemy_url = config_parser.get('app:main', 'sqlalchemy.url')
+ except Error, e:
+ raise LuciUtilError, e
+
+ return sql_alchemy_url
+
+
+@_debug
+def _getDBFileFromSQLAlchemyURL(sql_alchemy_url):
+ """Convert 'sqlalchemy.url' value to path where such DB is stored.
+
+ Keyword arguments:
+ sql_alchemy_url Value as given by _SQLAlchemyURLFromConfig().
+
+ Return values:
+ Path where the db defined by 'sqlalchemy.url' is stored.
+
+ Raises:
+ LuciUtilError
+
+ """
+ if issubclass(type(sql_alchemy_url), basestring):
+ sql_alchemy_url = sql_alchemy_url.strip()
+ if sql_alchemy_url.startswith('sqlite:///'):
+ return sql_alchemy_url.replace('sqlite:///', '', 1)
+ else:
+ raise LuciUtilError, "DB URL \`%s\' not supported." \
+ % config_file
+
+
+#
+# FUNCTIONS FOR PARTICULAR OPTIONS
+#
+
+@_debug
+def changePassword(config_file, password=None):
+ """Change password for the administrator in the database.
+
+ Keyword arguments:
+ config_file Path to the config file (usually with .ini extension).
+ password The password to be set or None to ask at a run-time.
+
+ Return values:
+
+
+ Raises:
+
+
+ """
+
+ # TODO -- Needs to be finished (Jan Pokorny will probably not be engaged
+ # in that).
+
+ from sqlalchemy import create_engine
+ from sqlalchemy import Table, Column, Integer, String, MetaData
+ from sqlalchemy.orm import mapper
+ from sqlalchemy.ext.declarative import declarative_base
+ from sqlalchemy.orm.exc import NoResultFound
+ from sqlalchemy.orm import sessionmaker
+
+ from getpass import getpass, GetPassWarning # To type the password
+ # at a runtime.
+
+ from luci.model.auth import User, Group, Permission
+
+ try:
+ sqlalchemy_url = _SQLAlchemyURLFromConfig(config_file)
+ except LuciUtilError, e:
+ print e
+ else:
+ # TODO: Following need to be rewritten, it was only my experiment.
+ engine = create_engine(sqlalchemy_url, echo=True) # TODO: echo=False
+ session = sessionmaker(bind=engine)
+ metadata = MetaData()
+ metadata.create_all(engine)
+
+ # Try to get the current data for given user. If it raises error,
+ # such column/table has to be created first. (???)
+ try:
+ result = session.query(User).filter_by(user_name=u'manager').one()
+ except NoResultFound, e:
+ print e
+
+
+@_debug
+def importDatabase(config_file, source, backup_old=False):
+ """Import the database from given source (replace the old one if present).
+
+ If requested, the old DB file can be backed up.
+
+ Keyword arguments:
+ config_file Path to the config file (usually with .ini extension).
+ source Path of source DB file to be imported.
+ backup_old Whether to backup the old DB if was in place.
+
+ Return values:
+
+
+ Raises:
+
+
+ """
+ from shutil import copy
+
+ # TODO -- Jan Pokorny will have a look.
+
+ #
+ pass
+
+
+@_debug
+def exportDatabase(config_file, destination, overwrite=True):
+ """Export the database to given destination.
+
+ If requested, the file with the same name as a destination file
+ is NOT overwritten (if exists).
+
+ Keyword arguments:
+ config_file Path to the config file (usually with .ini extension).
+ destination Path to destination DB file, where to export DB file.
+ overwrite Whether to backup the old DB if was in place.
+
+ Return values:
+
+
+ Raises:
+
+
+ """
+
+ from shutil import copy
+
+ # TODO -- Jan Pokorny will have a look.
+
+ pass
+
+
+#
+# MAIN
+#
+
+@_debug
+def main():
+ (parser, mutex_opts_list) = _setParserOptions(
+ OptionParser(usage=USAGE, version=VERSION)
+ )
+ (options, args) = parser.parse_args()
+
+ debug_print(options)
+ debug_print(args)
+
+ # No stand-alone argument allowed.
+ if len(args) != 0:
+ parser.print_usage()
+ exit(1)
+
+ # Check, whether mutual exclusivity is kept.
+ for mutex_opts in mutex_opts_list:
+ i = 0
+ for mutex_opt in mutex_opts:
+ if getattr(options, mutex_opt.dest, False):
+ i += 1
+ if i > 1:
+ mutex_opts_str = ['\'' + opt._short_opts[0] + '\'' \
+ for opt in mutex_opts]
+ parser.error('all options %s are mutually exclusive' % \
+ ', '.join(mutex_opts_str))
+
+ # Set the configuration file to be used according to whether it is defined
+ # explicitly from command-line options or not.
+ if options.config_file:
+ config_file = options.config_file
+ else:
+ config_file = DEFAULT_INI_FILENAME
+
+ # Branch according to the option used.
+ if options.pwd:
+ # Minus as a password means to ask for it afterwards.
+ if options.pwd == '-':
+ changePassword(config_file)
+ else:
+ changePassword(config_file, options.pwd)
+ elif options.import_src:
+ importDatabase(config_file, options.import_src)
+ elif options.export_dest:
+ exportDatabase(config_file, options.export_dest)
+ else:
+ parser.print_usage()
+
+
+# -----------------------------------------------------------------------------
+
+if __name__ == '__main__':
+ # Module executed as a script.
+ main()
+
14 years, 7 months
[luci/demodata2: 2/2] Merge commit '2579a9afa7b386b6c03adff4bcacb17b0dbe9977' into demodata2
by Jan Pokorný
commit d79b3720b2e57dc5935d97c611fb6aeead28ccb2
Merge: 9f45f12... 2579a9a...
Author: Jan Pokorny <jpokorny(a)redhat.com>
Date: Thu Oct 29 17:24:34 2009 +0100
Merge commit '2579a9afa7b386b6c03adff4bcacb17b0dbe9977' into demodata2
* commit '2579a9afa7b386b6c03adff4bcacb17b0dbe9977':
Added logo to header.html and new logo.png to images.
Changes after merge: img tag for logo has to have an 'alt' attribute
to be valid, trailing spaces in 2 files removed.
luci/public/images/logo.png | Bin 0 -> 10565 bytes
luci/templates/cluster_part/failover.html | 2 +-
luci/templates/cluster_part/fence.html | 2 +-
luci/templates/header.html | 2 +-
4 files changed, 3 insertions(+), 3 deletions(-)
---
diff --cc luci/templates/cluster_part/failover.html
index ab2626b,c355ece..37def84
--- a/luci/templates/cluster_part/failover.html
+++ b/luci/templates/cluster_part/failover.html
@@@ -20,13 -18,9 +20,13 @@@
<form action="${tg.url(cmd_url + '/apply')}" method="post">
<div class="sectionblock">
- <div id="toolbar">
+ <div class="toolbar" id="toolbar_collapsed">
+ <div class="toolbar_view_button" onclick="onOverviewShow()">show</div>
- </div>
++ </div>
+ <div class="toolbar" id="toolbar_shown">
<input type="submit" name="${apply_cmds.DELETE}" value="${_('Delete')}" class="toolbar_button" id="tb_delete"/>
<a href="${tg.url(cmd_url + '/add')}" class="toolbar_button" id="tb_add">Add</a>
+ <div class="toolbar_view_button" onclick="onOverviewCollapse()">collapse</div>
</div>
<!--! OVERVIEW SECTION. -->
diff --cc luci/templates/cluster_part/fence.html
index 5633c84,b612ed6..3e8fbd7
--- a/luci/templates/cluster_part/fence.html
+++ b/luci/templates/cluster_part/fence.html
@@@ -18,10 -16,7 +18,10 @@@
<form action="${tg.url(cmd_url + '/apply')}" method="post">
<div class="sectionblock">
- <div id="toolbar">
+ <div class="toolbar" id="toolbar_collapsed">
+ <div class="toolbar_view_button" onclick="onOverviewShow()">show</div>
- </div>
++ </div>
+ <div class="toolbar" id="toolbar_shown">
<input type="submit" name="${apply_cmds.UPDATE}" value="${_('Update')}" class="toolbar_button" id="tb_update"/>
<input type="submit" name="${apply_cmds.DELETE}" value="${_('Delete')}" class="toolbar_button" id="tb_delete"/>
<a href="${tg.url(cmd_url + '/add')}" class="toolbar_button" id="tb_add">Add</a>
diff --cc luci/templates/header.html
index 5991110,c746888..e17d320
--- a/luci/templates/header.html
+++ b/luci/templates/header.html
@@@ -3,7 -3,7 +3,7 @@@
py:strip="">
<py:def function="header">
<div id="header">
- <h1>logo here</h1>
- <h1><img src="/images/logo.png" /></h1>
++ <h1><img src="/images/logo.png" alt="Cluster management logo"/></h1>
<ul class="headermenu">
<li py:if="request.identity" id="admin" class="loginlogout"><a href="${tg.url('/admin')}">Admin</a></li>
<li><a href="${tg.url('/about')}" class="${('', 'active')[defined('page') and page==page=='about']}">About</a></li>
14 years, 7 months
[luci] Added logo to header.html and new logo.png to images.
by Jeremy Perry
commit 2579a9afa7b386b6c03adff4bcacb17b0dbe9977
Author: Jeremy Perry <jeremy.perry(a)redhat.com>
Date: Tue Oct 27 17:16:33 2009 -0400
Added logo to header.html and new logo.png to images.
luci/public/images/logo.png | Bin 0 -> 10565 bytes
luci/templates/header.html | 2 +-
2 files changed, 1 insertions(+), 1 deletions(-)
---
diff --git a/luci/public/images/logo.png b/luci/public/images/logo.png
new file mode 100644
index 0000000..f911ea5
Binary files /dev/null and b/luci/public/images/logo.png differ
diff --git a/luci/templates/header.html b/luci/templates/header.html
index 5991110..c746888 100644
--- a/luci/templates/header.html
+++ b/luci/templates/header.html
@@ -3,7 +3,7 @@
py:strip="">
<py:def function="header">
<div id="header">
- <h1>logo here</h1>
+ <h1><img src="/images/logo.png" /></h1>
<ul class="headermenu">
<li py:if="request.identity" id="admin" class="loginlogout"><a href="${tg.url('/admin')}">Admin</a></li>
<li><a href="${tg.url('/about')}" class="${('', 'active')[defined('page') and page==page=='about']}">About</a></li>
14 years, 7 months
[luci/demodata2] Ability for collapsing for remaining pages (services, failovers, fences).
by Jan Pokorný
commit 9f45f120c013ef1edd57d71a09f365b9b76b15bb
Author: Jan Pokorny <jpokorny(a)redhat.com>
Date: Fri Oct 23 17:19:30 2009 +0200
Ability for collapsing for remaining pages (services, failovers, fences).
luci/templates/cluster_part/failover.html | 9 +++++++--
luci/templates/cluster_part/fence.html | 7 ++++++-
luci/templates/cluster_part/service.html | 7 ++++++-
3 files changed, 19 insertions(+), 4 deletions(-)
---
diff --git a/luci/templates/cluster_part/failover.html b/luci/templates/cluster_part/failover.html
index 6b05eaf..ab2626b 100644
--- a/luci/templates/cluster_part/failover.html
+++ b/luci/templates/cluster_part/failover.html
@@ -9,8 +9,9 @@
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
<title py:content="''">Template: Failovers</title>
- <link rel="stylesheet" type="text/css" media="screen" href="${tg.url('/css/failover.css')}" />
<script type="text/javascript" src="${tg.url('/js/failover.js')}"></script>
+ <script type="text/javascript" src="${tg.url('/js/shared.js')}"></script>
+ <link rel="stylesheet" type="text/css" media="screen" href="${tg.url('/css/failover.css')}" />
</head>
<body onload="onLoad()"
@@ -19,9 +20,13 @@
<form action="${tg.url(cmd_url + '/apply')}" method="post">
<div class="sectionblock">
- <div class="toolbar">
+ <div class="toolbar" id="toolbar_collapsed">
+ <div class="toolbar_view_button" onclick="onOverviewShow()">show</div>
+ </div>
+ <div class="toolbar" id="toolbar_shown">
<input type="submit" name="${apply_cmds.DELETE}" value="${_('Delete')}" class="toolbar_button" id="tb_delete"/>
<a href="${tg.url(cmd_url + '/add')}" class="toolbar_button" id="tb_add">Add</a>
+ <div class="toolbar_view_button" onclick="onOverviewCollapse()">collapse</div>
</div>
<!--! OVERVIEW SECTION. -->
diff --git a/luci/templates/cluster_part/fence.html b/luci/templates/cluster_part/fence.html
index 77c2b63..5633c84 100644
--- a/luci/templates/cluster_part/fence.html
+++ b/luci/templates/cluster_part/fence.html
@@ -9,6 +9,7 @@
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
<title py:content="''">Template: Fences</title>
+ <script type="text/javascript" src="${tg.url('/js/shared.js')}"></script>
<link rel="stylesheet" type="text/css" media="screen" href="${tg.url('/css/fence.css')}" />
</head>
@@ -17,10 +18,14 @@
<form action="${tg.url(cmd_url + '/apply')}" method="post">
<div class="sectionblock">
- <div class="toolbar">
+ <div class="toolbar" id="toolbar_collapsed">
+ <div class="toolbar_view_button" onclick="onOverviewShow()">show</div>
+ </div>
+ <div class="toolbar" id="toolbar_shown">
<input type="submit" name="${apply_cmds.UPDATE}" value="${_('Update')}" class="toolbar_button" id="tb_update"/>
<input type="submit" name="${apply_cmds.DELETE}" value="${_('Delete')}" class="toolbar_button" id="tb_delete"/>
<a href="${tg.url(cmd_url + '/add')}" class="toolbar_button" id="tb_add">Add</a>
+ <div class="toolbar_view_button" onclick="onOverviewCollapse()">collapse</div>
</div>
<!--! OVERVIEW SECTION. -->
diff --git a/luci/templates/cluster_part/service.html b/luci/templates/cluster_part/service.html
index 7593ef3..a8958cc 100644
--- a/luci/templates/cluster_part/service.html
+++ b/luci/templates/cluster_part/service.html
@@ -9,6 +9,7 @@
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
<title py:content="''">Template: Services</title>
+ <script type="text/javascript" src="${tg.url('/js/shared.js')}"></script>
<link rel="stylesheet" type="text/css" media="screen" href="${tg.url('/css/service.css')}" />
</head>
@@ -17,10 +18,14 @@
<form action="${tg.url(cmd_url + '/apply')}" method="post">
<div class="sectionblock">
- <div class="toolbar">
+ <div class="toolbar" id="toolbar_collapsed">
+ <div class="toolbar_view_button" onclick="onOverviewShow()">show</div>
+ </div>
+ <div class="toolbar" id="toolbar_shown">
<input type="submit" name="${apply_cmds.REBOOT}" value="${_('Reboot')}" class="toolbar_button" id="tb_reboot" />
<input type="submit" name="${apply_cmds.DELETE}" value="${_('Delete')}" class="toolbar_button" id="tb_delete" />
<a href="${tg.url(cmd_url + '/add')}" class="toolbar_button" id="tb_add">Add</a>
+ <div class="toolbar_view_button" onclick="onOverviewCollapse()">collapse</div>
</div>
<!--! OVERVIEW SECTION. -->
14 years, 7 months
[luci/demodata2: 2/2] Merge commit 'eb02470e2fac5b617b78da7e07e6e8e9efd600e6' into demodata2
by Jan Pokorný
commit dfe17c9c04f8500181391c2aa1aea99de704f0ce
Merge: 8ab32aa... eb02470...
Author: Jan Pokorny <jpokorny(a)redhat.com>
Date: Fri Oct 23 14:57:45 2009 +0200
Merge commit 'eb02470e2fac5b617b78da7e07e6e8e9efd600e6' into demodata2
* commit 'eb02470e2fac5b617b78da7e07e6e8e9efd600e6':
Display cluster list on main homebase page.
Conflicts:
luci/controllers/root.py
luci/templates/homebase.html
Some little relevant changes were also made.
luci/controllers/root.py | 7 ++++-
luci/templates/homebase.html | 67 +++++++++++++++++++++++++++++++++++++++--
2 files changed, 69 insertions(+), 5 deletions(-)
---
diff --cc luci/controllers/root.py
index 477e011,fa0d4fd..3692762
--- a/luci/controllers/root.py
+++ b/luci/controllers/root.py
@@@ -1,20 -1,16 +1,21 @@@
# -*- coding: utf-8 -*-
"""Main Controller"""
-from tg import expose, flash, require, url, request, redirect, app_globals, tmpl_context, validate
-from tg import tmpl_context
+
+from tg import expose, flash, require, url, request, redirect, app_globals, \
+ tmpl_context
from pylons.i18n import ugettext as _, lazy_ugettext as l_
-from catwalk.tg2 import Catwalk
from repoze.what import predicates
+# TODO: Remove?
+from catwalk.tg2 import Catwalk
+
from luci.controllers.decorators import *
from luci.lib.base import BaseController
++from luci.lib.helpers import relativeUrlList2Str
from luci.controllers.error import ErrorController
+# TODO: Remove? See 'luci.controllers.secure'.
from luci.controllers.secure import SecureController
from luci.model import DBSession, metadata
@@@ -48,75 -47,25 +49,79 @@@ class RootController(BaseController)
"""
def __call__(self, environ, start_response):
- """Invoke the Controller"""
+ """Set some defaults within 'tmpl_context' and invoke the Controller."""
+ # Remembers, which title part has been already used.
+ tmpl_context.already_used = {}
+ # Vars for title parts adding and remembering appropriate arguments.
tmpl_context.title_list = []
- tmpl_context.already_used = dict()
- tmpl_context.already_used.setdefault(False)
+ tmpl_context.title_args = {}
return BaseController.__call__(self, environ, start_response)
+
# SUBCONTROLLERS
+ error = ErrorController()
+
+ # TODO: Remove? See 'luci.controllers.secure'.
secc = SecureController()
+
+ # TODO: Remove?
admin = Catwalk(model, DBSession)
- error = ErrorController()
+
# METHODS OF THE ROOT CONTROLLER
-
- @expose('luci.templates.index')
+
+ # Homebase pages.
+
+ @expose()
+ # Uncomment this (or replace with more suitable decorator/predicate)
+ # to apply access control.
+ #(a)require(predicates.not_anonymous())
def index(self):
"""Handle the front-page."""
- redirect ('/homebase/')
- return dict(page='index')
+ redirect ('/homebase')
+
+ @expose('luci.templates.homebase')
+ # Uncomment this (or replace with more suitable decorator/predicate)
+ # to apply access control.
+ #(a)require(predicates.not_anonymous())
+ def homebase(self):
+ """Handle the front-page."""
- return dict(page='homebase', homebasepage='homebasepage')
++
++ base_url_str = relativeUrlList2Str(scheme.APP_PREFIX, scheme.CLUSTERS)
++
++ return dict(page='homebase', homebasepage='homebasepage',
++ base_url=base_url_str)
+
+ # -- Start of section of methods that might be moved somewhere
+ # into more specific subcontrollers.
+
+ @expose('luci.templates.homebase')
+ # Uncomment this (or replace with more suitable decorator/predicate)
+ # to apply access control.
+ #(a)require(predicates.not_anonymous())
+ def addsystem(self):
+ tmpl_context.form = create_add_system_form
+ return dict(page='homebase', homebasepage='addsystem')
+
+ @expose('luci.templates.homebase')
+ # Uncomment this (or replace with more suitable decorator/predicate)
+ # to apply access control.
+ #(a)require(predicates.not_anonymous())
+ def adduser(self):
+ tmpl_context.form = create_add_user_form
+ return dict(page='homebase', homebasepage='adduser')
+
+ @expose('luci.templates.homebase')
+ # Uncomment this (or replace with more suitable decorator/predicate)
+ # to apply access control.
+ #(a)require(predicates.not_anonymous())
+ def managesystems(self):
+ tmpl_context.form = create_manage_systems_form
+ return dict(page='homebase', homebasepage='managesystems')
+
+ # -- End of section of methods that might be moved somewhere
+ # into more specific subcontrollers.
@expose('luci.templates.about')
def about(self):
diff --cc luci/templates/homebase.html
index 23fdee1,1cf49b5..7cdb9cc
--- a/luci/templates/homebase.html
+++ b/luci/templates/homebase.html
@@@ -11,7 -11,7 +11,7 @@@
<title>Login Form</title>
</head>
- <body>
-<body py:with="clusters = app_globals.data.clusters;form_utils = app_globals.form_utils">
++<body py:with="clusters = app_globals.data.clusters">
<div class="sidebar">
<h2>Take action</h2>
<div class="actions">
@@@ -42,16 -41,76 +42,75 @@@
<p>Enter credentials for a node from the cluster you wish to add to the cluster management interface.</p>
<div py:replace="tmpl_context.form()">Input Form</div>
</div>
- <div py:if="homebasepage == 'adduser'">
+ <div py:when="'adduser'">
<h2>Add a User</h2>
-- <p>Enter a username and password for a new user.</p>
++ <p>Enter a username and password for a new user.</p>
<div py:replace="tmpl_context.form()">Input Form</div>
</div>
- <div py:if="homebasepage == 'homebasepage'">
+ <div py:when="'homebasepage'">
<h2>Problems</h2>
<p>Table with summary of problems goes here</p>
- <h2><a href="${tg.url('/clusters')}" class="${('', 'active')[defined('page') and page==page=='clusters']}">Clusters</a></h2>
- <p>Table with summary of clusters goes here.</p>
- <h2><a href="${tg.url('/cluster')}" class="${('', 'active')[defined('page') and page==page=='clusters']}">Clusters</a></h2>
++ <h2><a href="${tg.url('/clusters')}" class="${('', 'active')[defined('page') and page=='clusters']}">Clusters</a></h2>
+ <div id="overview">
+ <table id="clusters_tlist" class="maintable">
+ <thead>
+ <tr>
+ <th class="icon"></th>
+ <th class="main_id">Name</th>
+ <th class="cluster_tlist_status">Status</th>
+ <th class="cluster_tlist_votes">Total Votes</th>
+ <th class="cluster_tlist_quorum">Min. Required Quorum</th>
+ <td class="table_space"></td>
+ </tr>
+ </thead>
+ <tbody>
+ <!--! List all the fences. -->
+ <tr py:for="i, (entity_name, cluster_data) in enumerate(clusters.iteritems())"
- py:attrs="not i%2 and {'class': 'even'} or None"
- py:with="identifier = form_utils.string2Id(entity_name)">
++ py:attrs="not i%2 and {'class': 'even'} or None">
+ <!--! Branch according to the status of the cluster. -->
+ <py:choose test="cluster_data['status']">
+ <!--! 1) Cluster is OK. -->
+ <py:when test="cluster_data.CLUSTER_OK">
+ <td class="icon"></td>
+ <td class="main_id">
+ <a href="${tg.url(base_url + '/' + entity_name)}">
+ <span class="entity_ok">${entity_name}</span>
+ </a>
+ </td>
+ <td class="cluster_tlist_status">OK</td>
+ </py:when>
+ <!--! 2) Cluster is not OK. -->
+ <py:when test="cluster_data.CLUSTER_NOT_OK">
+ <td class="icon">
+ <img src="${tg.url('/images/exclamation.png')}" alt="Cluster is not OK." />
+ </td>
+ <td class="main_id">
+ <a href="${tg.url(base_url + '/' + entity_name)}">
+ <span class="entity_fail">${entity_name}</span>
+ </a>
+ </td>
+ <td class="cluster_tlist_status">Not OK</td>
+ </py:when>
+ <!--! 3) Status of the cluster is unknown. -->
+ <py:when test="cluster_data.CLUSTER_UNKNOWN">
+ <td class="icon">
+ <img src="${tg.url('/images/question.png')}" alt="Status of the cluster is unknown." />
+ </td>
+ <td class="main_id">
+ <a href="${tg.url(base_url + '/' + entity_name)}">
+ <span class="entity_unknown">${entity_name}</span>
+ </a>
+ </td>
+ <td class="cluster_tlist_status">Status uknown</td>
+ </py:when>
+ </py:choose>
+ <td class="cluster_tlist_votes">${cluster_data['cluster_votes']}</td>
+ <td class="cluster_tlist_quorum">${cluster_data['required_quorum']}</td>
+ <td class="table_space"></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
</div>
</div>
</body>
14 years, 7 months
[luci/demodata2] Securing application prepared, although it have to be tweaked.
by Jan Pokorný
commit 8ab32aadf177f6450821f79a52e60a31c97314f7
Author: Jan Pokorny <jpokorny(a)redhat.com>
Date: Thu Oct 22 16:35:01 2009 +0200
Securing application prepared, although it have to be tweaked.
luci/controllers/cluster.py | 15 +++++++++++++++
luci/controllers/cluster_part/failover.py | 15 +++++++++++++++
luci/controllers/cluster_part/fence.py | 15 +++++++++++++++
luci/controllers/cluster_part/node.py | 15 +++++++++++++++
luci/controllers/cluster_part/service.py | 17 ++++++++++++++++-
luci/controllers/global_res.py | 14 ++++++++++++++
6 files changed, 90 insertions(+), 1 deletions(-)
---
diff --git a/luci/controllers/cluster.py b/luci/controllers/cluster.py
index 74e9491..bb6ed2f 100644
--- a/luci/controllers/cluster.py
+++ b/luci/controllers/cluster.py
@@ -41,6 +41,7 @@
from tg import flash, request, redirect, expose, tmpl_context, app_globals
from pylons.i18n import ugettext as _, lazy_ugettext as l_
+from repoze.what import predicates
from luci.lib.base import SubController, SubControllerApplyMixin, \
ApplyCommands, setApplyCommands
@@ -67,6 +68,13 @@ base2CmdCtrl = app_globals.scheme.base2CmdCtrl
class ClusterController(SubController):
"""Subcontroller handling basic requests related with `clusters' part."""
+ # TODO: Uncomment this (or replace with more suitable predicate)
+ # to apply access control.
+ # For some reason this won't work for me, similar problem discussed at:
+ # <http://groups.google.com.ar/group/turbogears/browse_thread/thread/28a8b85...>
+ # allow_only = predicates.not_anonymous()
+
+
@forPageShowWithUrlCorrection('luci.templates.cluster')
def default(self):
"""Handle simple clusters listing.
@@ -180,6 +188,13 @@ class ClusterApplyCommands(ApplyCommands):
class ClusterCmdController(SubController, SubControllerApplyMixin):
"""Subcontroller handling commands related with `clusters' part."""
+ # TODO: Uncomment this (or replace with more suitable predicate)
+ # to apply access control.
+ # For some reason this won't work for me, similar problem discussed at:
+ # <http://groups.google.com.ar/group/turbogears/browse_thread/thread/28a8b85...>
+ # allow_only = predicates.not_anonymous()
+
+
@forPageShowWithUrlCorrection('luci.templates.cluster_forms')
def create(self, name=''):
"""Display form for cluster creation."""
diff --git a/luci/controllers/cluster_part/failover.py b/luci/controllers/cluster_part/failover.py
index 4ff7d80..6a8072f 100644
--- a/luci/controllers/cluster_part/failover.py
+++ b/luci/controllers/cluster_part/failover.py
@@ -49,6 +49,7 @@
"""
from tg import flash, redirect, expose, tmpl_context, app_globals
from pylons.i18n import ugettext as _, lazy_ugettext as l_
+from repoze.what import predicates
from luci.lib.base import SubController, SubControllerApplyMixin, \
ApplyCommands, setApplyCommands
@@ -72,6 +73,13 @@ base2CmdCtrl = app_globals.scheme.base2CmdCtrl
class FailoverController(SubController):
"""Subcontroller handling basic requests related with `failovers' part."""
+ # TODO: Uncomment this (or replace with more suitable predicate)
+ # to apply access control.
+ # For some reason this won't work for me, similar problem discussed at:
+ # <http://groups.google.com.ar/group/turbogears/browse_thread/thread/28a8b85...>
+ # allow_only = predicates.not_anonymous()
+
+
@forPageShowWithUrlCorrection('luci.templates.cluster_part.failover')
def default(self):
"""Handle simple failovers listing.
@@ -202,6 +210,13 @@ class FailoverApplyCommands(ApplyCommands):
class FailoverCmdController(SubController, SubControllerApplyMixin):
"""Subcontroller handling commands related with `failovers' part."""
+ # TODO: Uncomment this (or replace with more suitable predicate)
+ # to apply access control.
+ # For some reason this won't work for me, similar problem discussed at:
+ # <http://groups.google.com.ar/group/turbogears/browse_thread/thread/28a8b85...>
+ # allow_only = predicates.not_anonymous()
+
+
@forImmediateRedirect(allowed_methods = ('GET', 'POST'))
def default(self, *args, **kwargs):
"""Handle requests not connected with any of defined method."""
diff --git a/luci/controllers/cluster_part/fence.py b/luci/controllers/cluster_part/fence.py
index 2bbb841..35a0a4a 100644
--- a/luci/controllers/cluster_part/fence.py
+++ b/luci/controllers/cluster_part/fence.py
@@ -42,6 +42,7 @@
"""
from tg import flash, redirect, expose, tmpl_context, app_globals
from pylons.i18n import ugettext as _, lazy_ugettext as l_
+from repoze.what import predicates
from luci.lib.base import SubController, SubControllerApplyMixin, \
ApplyCommands, setApplyCommands
@@ -66,6 +67,13 @@ base2CmdCtrl = app_globals.scheme.base2CmdCtrl
class FenceController(SubController):
"""Subcontroller handling basic requests related with `fences' part."""
+ # TODO: Uncomment this (or replace with more suitable predicate)
+ # to apply access control.
+ # For some reason this won't work for me, similar problem discussed at:
+ # <http://groups.google.com.ar/group/turbogears/browse_thread/thread/28a8b85...>
+ # allow_only = predicates.not_anonymous()
+
+
@forPageShowWithUrlCorrection('luci.templates.cluster_part.fence')
def default(self):
"""Handle simple fences listing.
@@ -192,6 +200,13 @@ class FenceApplyCommands(ApplyCommands):
class FenceCmdController(SubController, SubControllerApplyMixin):
"""Subcontroller handling commands related with `fences' part."""
+ # TODO: Uncomment this (or replace with more suitable predicate)
+ # to apply access control.
+ # For some reason this won't work for me, similar problem discussed at:
+ # <http://groups.google.com.ar/group/turbogears/browse_thread/thread/28a8b85...>
+ # allow_only = predicates.not_anonymous()
+
+
@forImmediateRedirect(allowed_methods = ('GET', 'POST'))
def default(self, *args, **kwargs):
"""Handle requests not connected with any of defined method."""
diff --git a/luci/controllers/cluster_part/node.py b/luci/controllers/cluster_part/node.py
index d6062d9..556d2a2 100644
--- a/luci/controllers/cluster_part/node.py
+++ b/luci/controllers/cluster_part/node.py
@@ -42,6 +42,7 @@
"""
from tg import flash, redirect, expose, tmpl_context, app_globals
from pylons.i18n import ugettext as _, lazy_ugettext as l_
+from repoze.what import predicates
from luci.lib.base import SubController, SubControllerApplyMixin, \
ApplyCommands, setApplyCommands
@@ -65,6 +66,13 @@ base2CmdCtrl = app_globals.scheme.base2CmdCtrl
class NodeController(SubController):
"""Subcontroller handling basic requests related with `nodes' part."""
+ # TODO: Uncomment this (or replace with more suitable predicate)
+ # to apply access control.
+ # For some reason this won't work for me, similar problem discussed at:
+ # <http://groups.google.com.ar/group/turbogears/browse_thread/thread/28a8b85...>
+ # allow_only = predicates.not_anonymous()
+
+
@forPageShowWithUrlCorrection('luci.templates.cluster_part.node')
def default(self):
"""Handle simple nodes listing.
@@ -214,6 +222,13 @@ class NodeApplyCommands(ApplyCommands):
class NodeCmdController(SubController, SubControllerApplyMixin):
"""Subcontroller handling commands related with `nodes' part."""
+ # TODO: Uncomment this (or replace with more suitable predicate)
+ # to apply access control.
+ # For some reason this won't work for me, similar problem discussed at:
+ # <http://groups.google.com.ar/group/turbogears/browse_thread/thread/28a8b85...>
+ # allow_only = predicates.not_anonymous()
+
+
@forImmediateRedirect(allowed_methods = ('GET', 'POST'))
def default(self, *args, **kwargs):
"""Handle requests not connected with any of defined method."""
diff --git a/luci/controllers/cluster_part/service.py b/luci/controllers/cluster_part/service.py
index 3c68cba..2e22599 100644
--- a/luci/controllers/cluster_part/service.py
+++ b/luci/controllers/cluster_part/service.py
@@ -42,6 +42,7 @@
"""
from tg import flash, redirect, expose, tmpl_context, app_globals
from pylons.i18n import ugettext as _, lazy_ugettext as l_
+from repoze.what import predicates
from luci.lib.base import SubController, SubControllerApplyMixin, \
ApplyCommands, setApplyCommands
@@ -66,6 +67,13 @@ base2CmdCtrl = app_globals.scheme.base2CmdCtrl
class ServiceController(SubController):
"""Subcontroller handling basic requests related with `services' part."""
+ # TODO: Uncomment this (or replace with more suitable predicate)
+ # to apply access control.
+ # For some reason this won't work for me, similar problem discussed at:
+ # <http://groups.google.com.ar/group/turbogears/browse_thread/thread/28a8b85...>
+ # allow_only = predicates.not_anonymous()
+
+
@forPageShowWithUrlCorrection('luci.templates.cluster_part.service')
def default(self):
"""Handle simple services listing.
@@ -131,7 +139,7 @@ class _CertainServiceController(SubController):
base_url=cluster_url + cluster_scheme.SERVICES,
cmd_url=cluster_url + base2CmdCtrl(cluster_scheme.SERVICES),
globalres_url=relativeUrlList2Str(scheme.APP_PREFIX,
- scheme.GLOBAL_RES),
+ scheme.GLOBAL_RES),
apply_cmds=ServiceApplyCommands)
@@ -194,6 +202,13 @@ class ServiceApplyCommands(ApplyCommands):
class ServiceCmdController(SubController, SubControllerApplyMixin):
"""Subcontroller handling commands related with `services' part."""
+ # TODO: Uncomment this (or replace with more suitable predicate)
+ # to apply access control.
+ # For some reason this won't work for me, similar problem discussed at:
+ # <http://groups.google.com.ar/group/turbogears/browse_thread/thread/28a8b85...>
+ # allow_only = predicates.not_anonymous()
+
+
@forImmediateRedirect(allowed_methods = ('GET', 'POST'))
def default(self, *args, **kwargs):
"""Handle requests not connected with any of defined method."""
diff --git a/luci/controllers/global_res.py b/luci/controllers/global_res.py
index dc4820e..a8f01e9 100644
--- a/luci/controllers/global_res.py
+++ b/luci/controllers/global_res.py
@@ -38,6 +38,7 @@
from tg import flash, request, redirect, expose, tmpl_context, app_globals
from pylons.i18n import ugettext as _, lazy_ugettext as l_
+from repoze.what import predicates
from luci.lib.base import SubController, SubControllerApplyMixin, \
ApplyCommands, setApplyCommands
@@ -62,6 +63,13 @@ base2CmdCtrl = app_globals.scheme.base2CmdCtrl
class GlobalResController(SubController):
"""Subcontroller handling basic requests related with `global resources' part."""
+ # TODO: Uncomment this (or replace with more suitable predicate)
+ # to apply access control.
+ # For some reason this won't work for me, similar problem discussed at:
+ # <http://groups.google.com.ar/group/turbogears/browse_thread/thread/28a8b85...>
+ # allow_only = predicates.not_anonymous()
+
+
@forPageShowWithUrlCorrection('luci.templates.global_res')
def default(self):
"""Handle simple global resources listing.
@@ -180,6 +188,12 @@ class GlobalResApplyCommands(ApplyCommands):
class GlobalResCmdController(SubController, SubControllerApplyMixin):
"""Subcontroller handling commands related with `global resources' part."""
+ # TODO: Uncomment this (or replace with more suitable predicate)
+ # to apply access control.
+ # For some reason this won't work for me, similar problem discussed at:
+ # <http://groups.google.com.ar/group/turbogears/browse_thread/thread/28a8b85...>
+ # allow_only = predicates.not_anonymous()
+
@forImmediateRedirect(allowed_methods = ('GET', 'POST'))
def default(self, *args, **kwargs):
"""Handle requests not connected with any of defined method."""
14 years, 7 months
[luci] Display cluster list on main homebase page.
by Chris Feist
commit eb02470e2fac5b617b78da7e07e6e8e9efd600e6
Author: Chris Feist <cfeist(a)redhat.com>
Date: Wed Oct 21 16:42:51 2009 -0500
Display cluster list on main homebase page.
luci/controllers/root.py | 5 ++-
luci/templates/homebase.html | 66 ++++++++++++++++++++++++++++++++++++++++--
2 files changed, 67 insertions(+), 4 deletions(-)
---
diff --git a/luci/controllers/root.py b/luci/controllers/root.py
index cda4171..fa0d4fd 100644
--- a/luci/controllers/root.py
+++ b/luci/controllers/root.py
@@ -85,7 +85,10 @@ class RootController(BaseController):
elif homebasepage == 'managesystems':
tmpl_context.form = create_manage_systems_form
- return dict(page='homebase',homebasepage=homebasepage,args=args)
+ app_url = scheme.APP_PREFIX + u'/'
+
+ return dict(page='homebase',homebasepage=homebasepage,args=args,
+ base_url=app_url + scheme.CLUSTERS)
@expose('luci.templates.cluster')
def cluster(self, clusterpage='clusterlist', **args):
diff --git a/luci/templates/homebase.html b/luci/templates/homebase.html
index 57d8313..1cf49b5 100644
--- a/luci/templates/homebase.html
+++ b/luci/templates/homebase.html
@@ -11,7 +11,7 @@
<title>Login Form</title>
</head>
-<body>
+<body py:with="clusters = app_globals.data.clusters;form_utils = app_globals.form_utils">
<div class="sidebar">
<h2>Take action</h2>
<div class="actions">
@@ -50,8 +50,68 @@
<h2>Problems</h2>
<p>Table with summary of problems goes here</p>
<h2><a href="${tg.url('/cluster')}" class="${('', 'active')[defined('page') and page==page=='clusters']}">Clusters</a></h2>
- <p>Table with summary of clusters goes here.</p>
+ <div id="overview">
+ <table id="clusters_tlist" class="maintable">
+ <thead>
+ <tr>
+ <th class="icon"></th>
+ <th class="main_id">Name</th>
+ <th class="cluster_tlist_status">Status</th>
+ <th class="cluster_tlist_votes">Total Votes</th>
+ <th class="cluster_tlist_quorum">Min. Required Quorum</th>
+ <td class="table_space"></td>
+ </tr>
+ </thead>
+ <tbody>
+ <!--! List all the fences. -->
+ <tr py:for="i, (entity_name, cluster_data) in enumerate(clusters.iteritems())"
+ py:attrs="not i%2 and {'class': 'even'} or None"
+ py:with="identifier = form_utils.string2Id(entity_name)">
+ <!--! Branch according to the status of the cluster. -->
+ <py:choose test="cluster_data['status']">
+ <!--! 1) Cluster is OK. -->
+ <py:when test="cluster_data.CLUSTER_OK">
+ <td class="icon"></td>
+ <td class="main_id">
+ <a href="${tg.url(base_url + '/' + entity_name)}">
+ <span class="entity_ok">${entity_name}</span>
+ </a>
+ </td>
+ <td class="cluster_tlist_status">OK</td>
+ </py:when>
+ <!--! 2) Cluster is not OK. -->
+ <py:when test="cluster_data.CLUSTER_NOT_OK">
+ <td class="icon">
+ <img src="${tg.url('/images/exclamation.png')}" alt="Cluster is not OK." />
+ </td>
+ <td class="main_id">
+ <a href="${tg.url(base_url + '/' + entity_name)}">
+ <span class="entity_fail">${entity_name}</span>
+ </a>
+ </td>
+ <td class="cluster_tlist_status">Not OK</td>
+ </py:when>
+ <!--! 3) Status of the cluster is unknown. -->
+ <py:when test="cluster_data.CLUSTER_UNKNOWN">
+ <td class="icon">
+ <img src="${tg.url('/images/question.png')}" alt="Status of the cluster is unknown." />
+ </td>
+ <td class="main_id">
+ <a href="${tg.url(base_url + '/' + entity_name)}">
+ <span class="entity_unknown">${entity_name}</span>
+ </a>
+ </td>
+ <td class="cluster_tlist_status">Status uknown</td>
+ </py:when>
+ </py:choose>
+ <td class="cluster_tlist_votes">${cluster_data['cluster_votes']}</td>
+ <td class="cluster_tlist_quorum">${cluster_data['required_quorum']}</td>
+ <td class="table_space"></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
</div>
</div>
</body>
-</html>
\ No newline at end of file
+</html>
14 years, 7 months
[luci/demodata2] Little changes to cluster list and root controller.
by Jan Pokorný
commit d66679bd0473014805d791d3646191b291348190
Author: Jan Pokorny <jpokorny(a)redhat.com>
Date: Wed Oct 21 20:07:16 2009 +0200
Little changes to cluster list and root controller.
`Add' button on cluster list page now displays prepared form.
Root's index redirects to homebase as in master branch.
There is a template shared between cluster related forms -- add existing and create.
luci/controllers/cluster.py | 40 +++++++++++++++++++++++------------
luci/controllers/root.py | 23 +++++++++++---------
luci/lib/app_strings.py | 3 ++
luci/templates/cluster.html | 2 +-
luci/templates/cluster_create.html | 33 -----------------------------
luci/templates/cluster_forms.html | 31 +++++++++++++++++++++++++++
luci/templates/homebase.html | 2 +-
7 files changed, 75 insertions(+), 59 deletions(-)
---
diff --git a/luci/controllers/cluster.py b/luci/controllers/cluster.py
index 9464282..74e9491 100644
--- a/luci/controllers/cluster.py
+++ b/luci/controllers/cluster.py
@@ -48,6 +48,7 @@ from luci.controllers.decorators import *
from luci.lib.app_strings import Title, FlashMsg
from luci.lib.helpers import relativeUrlList2Str
from luci.widgets.create_cluster_form import create_cluster_form
+from luci.widgets.add_existing_form import create_add_existing_form
__all__ = ['ClusterController', 'ClusterCmdController']
@@ -179,29 +180,38 @@ class ClusterApplyCommands(ApplyCommands):
class ClusterCmdController(SubController, SubControllerApplyMixin):
"""Subcontroller handling commands related with `clusters' part."""
- @forPageShowWithUrlCorrection('luci.templates.cluster_create')
+ @forPageShowWithUrlCorrection('luci.templates.cluster_forms')
def create(self, name=''):
"""Display form for cluster creation."""
+ Title.usePart('CLUSTER_CREATE')
+
tmpl_context.form = create_cluster_form
-
+
base_url_str = relativeUrlList2Str(scheme.APP_PREFIX, scheme.CLUSTERS)
cmd_url_str = relativeUrlList2Str(scheme.APP_PREFIX,
base2CmdCtrl(scheme.CLUSTERS))
return dict(base_url=base_url_str,
- cmd_url=cmd_url_str, page='create_cluster')
+ cmd_url=cmd_url_str,
+ page='create_cluster')
- @forImmediateRedirect(allowed_methods='GET')
- def add(self):
- """Handle addition of a new cluster."""
+ @forPageShowWithUrlCorrection('luci.templates.cluster_forms')
+ def add_existing(self):
+ """Display form for existing cluster addition."""
+
+ Title.usePart('CLUSTER_ADD_EXISTING')
+
+ tmpl_context.form = create_add_existing_form
- flash('It should be a dialog to add new cluster here instead,'
- 'but not implemented yet...', status='info')
- # following code is only my experiment, how to continue:
- #tmpl_context.form = create_fdom_add_form
- #return dict()
+ base_url_str = relativeUrlList2Str(scheme.APP_PREFIX, scheme.CLUSTERS)
+ cmd_url_str = relativeUrlList2Str(scheme.APP_PREFIX,
+ base2CmdCtrl(scheme.CLUSTERS))
+
+ return dict(base_url=base_url_str,
+ cmd_url=cmd_url_str,
+ page='add_existing')
@forImmediateRedirect(allowed_methods='POST')
@@ -230,11 +240,12 @@ class ClusterCmdController(SubController, SubControllerApplyMixin):
stderr.write('nodes: %s\n' % str(node_list))
if not cluster_name:
- flash(l_('No cluster name was given'))
+ flash(l_('No cluster name was given'), 'error')
return dict(redir_target=base_url_str)
if len(cluster_name) > 15:
- flash(l_('Cluster names must be less than 16 characters long'))
+ flash(l_('Cluster names must be less than 16 characters long'),
+ 'error')
return dict(redir_target=base_url_str)
for node in nodes:
@@ -258,7 +269,8 @@ class ClusterCmdController(SubController, SubControllerApplyMixin):
break
if len(errors) > 0:
- flash(l_('The following errors occurred: %s') % str(errors))
+ flash(l_('The following errors occurred: %s') % str(errors),
+ 'error')
return dict(redir_target=base_url_str)
ret = send_batch_to_hosts(node_list, 10, rq.create_cluster,
diff --git a/luci/controllers/root.py b/luci/controllers/root.py
index 1ad9480..477e011 100644
--- a/luci/controllers/root.py
+++ b/luci/controllers/root.py
@@ -21,10 +21,8 @@ from luci.model import DBSession, metadata
from luci import model
from luci.widgets.add_system_form import create_add_system_form
-from luci.widgets.add_existing_form import create_add_existing_form
from luci.widgets.add_user_form import create_add_user_form
from luci.widgets.manage_systems_form import create_manage_systems_form
-from luci.widgets.create_cluster_form import create_cluster_form
__all__ = ['RootController']
@@ -74,29 +72,32 @@ class RootController(BaseController):
# Homebase pages.
- @expose('luci.templates.homebase')
+ @expose()
# Uncomment this (or replace with more suitable decorator/predicate)
# to apply access control.
#(a)require(predicates.not_anonymous())
def index(self):
"""Handle the front-page."""
- return dict(page='homebase', homebasepage='homebasepage')
+ redirect ('/homebase')
@expose('luci.templates.homebase')
# Uncomment this (or replace with more suitable decorator/predicate)
# to apply access control.
#(a)require(predicates.not_anonymous())
- def addsystem(self):
- tmpl_context.form = create_add_system_form
- return dict(page='homebase', homebasepage='addsystem')
+ def homebase(self):
+ """Handle the front-page."""
+ return dict(page='homebase', homebasepage='homebasepage')
+
+ # -- Start of section of methods that might be moved somewhere
+ # into more specific subcontrollers.
@expose('luci.templates.homebase')
# Uncomment this (or replace with more suitable decorator/predicate)
# to apply access control.
#(a)require(predicates.not_anonymous())
- def addexisting(self):
- tmpl_context.form = create_add_existing_form
- return dict(page='homebase', homebasepage='addexisting')
+ def addsystem(self):
+ tmpl_context.form = create_add_system_form
+ return dict(page='homebase', homebasepage='addsystem')
@expose('luci.templates.homebase')
# Uncomment this (or replace with more suitable decorator/predicate)
@@ -114,6 +115,8 @@ class RootController(BaseController):
tmpl_context.form = create_manage_systems_form
return dict(page='homebase', homebasepage='managesystems')
+ # -- End of section of methods that might be moved somewhere
+ # into more specific subcontrollers.
@expose('luci.templates.about')
def about(self):
diff --git a/luci/lib/app_strings.py b/luci/lib/app_strings.py
index 95424dd..e8869b3 100644
--- a/luci/lib/app_strings.py
+++ b/luci/lib/app_strings.py
@@ -46,6 +46,9 @@ class Title:
CLUSTERS = _('clusters')
CERTAIN_CLUSTER = _('cluster {cluster}'), 'cluster'
+ CLUSTER_CREATE = _('create cluster')
+ CLUSTER_ADD_EXISTING = _('add an existing cluster')
+
GLOBAL_RES = _('resources')
CERTAIN_GLOBAL_RES = _('resource {global_res}'), 'global_res'
diff --git a/luci/templates/cluster.html b/luci/templates/cluster.html
index ea568a3..1be3c18 100644
--- a/luci/templates/cluster.html
+++ b/luci/templates/cluster.html
@@ -19,7 +19,7 @@
<div class="toolbar">
<!--<input type="submit" name="${apply_cmds.UPDATE}" value="${_('update')}" class="toolbar_button" id="tb_update"/>!-->
<a href="${tg.url(cmd_url + '/create')}" class="toolbar_button" id="tb_create">Create</a>
- <a href="${tg.url(cmd_url + '/add')}" class="toolbar_button" id="tb_add">Add</a>
+ <a href="${tg.url(cmd_url + '/add_existing')}" class="toolbar_button" id="tb_add">Add</a>
<input type="submit" name="${apply_cmds.DELETE}" value="${_('Delete')}" class="toolbar_button" id="tb_delete"/>
</div>
diff --git a/luci/templates/cluster_forms.html b/luci/templates/cluster_forms.html
new file mode 100644
index 0000000..61d84c8
--- /dev/null
+++ b/luci/templates/cluster_forms.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<xi:include href="master.html" />
+
+<head>
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+ <title py:content="''">Template: Forms connected with clusters</title>
+</head>
+
+<body>
+ <div class="mainpage"
+ py:choose="page">
+
+ <div py:when="'add_existing'">
+ <h2>Add an Existing Cluster</h2>
+ <p>Enter credentials for a node from the cluster you wish to add to the cluster management interface.</p>
+ <div py:replace="tmpl_context.form()">Input Form</div>
+ </div>
+
+ <div py:when="'create_cluster'">
+ <h2>Create a New Cluster</h2>
+ <div py:replace="tmpl_context.form()">Input Form</div>
+ </div>
+
+ </div>
+</body>
+</html>
diff --git a/luci/templates/homebase.html b/luci/templates/homebase.html
index 8f6e420..23fdee1 100644
--- a/luci/templates/homebase.html
+++ b/luci/templates/homebase.html
@@ -16,7 +16,7 @@
<h2>Take action</h2>
<div class="actions">
<a href="/addsystem">Add a System</a><br/>
- <a href="/addexisting">Add an Existing Cluster</a><br/>
+ <a href="/clusters_cmd/add_existing">Add an Existing Cluster</a><br/>
<a href="/managesystems">Manage Systems</a><br/>
<a href="/adduser">Add a User</a><br/>
</div>
14 years, 7 months