Table of Contents
Although WebConsole provides numerous features to control and monitor a server cluster, there many times when you want to extend its functionality. This part of the document describes how to do that.
WebConsole is built upon an existing web development framework (TurboGears). The list below describes TurboGears' components of interest:
-
TurboGears
Rapid web application development framework.
Component can be found at http://www.turbogears.org/.
Documentation can be found at http://docs.turbogears.org/1.0.
-
MochiKit
A set of JavaScript libraries to enhance existing JavaScript functionality and provide simple mechanisms of performing common JavaScript operations.
Component can be found at http://mochikit.com/.
Documentation can be found at http://mochikit.com/doc/html/MochiKit/index.html.
-
KID Templates
Template language that provides the ability to integrate Python code into HTML to generate dynamic web pages.
Component can be found at http://kid-templating.org/.
-
CherryPy
Web server component of TurboGears.
Component can be found at http://www.cherrypy.org/.
Documentation can be found at http://docs.cherrypy.org/.
-
SQLObject
Relational database Python wrapper that abstracts database concepts (such as tables, rows and columns) into object-oriented concepts (such as classes, instances and attributes).
Component can be found at http://www.sqlobject.org/.
Documentation can be found at http://www.sqlobject.org/SQLObject.html.
The referenced documentation will differ based on the kind of functionality that you are trying to achieve within WebConsole. The sections below outline some common modifications that you might wish to make to WebConsole, and a brief description of what is required. The also include references to the appropriate component documentation that would be used while modifying the tool.
This is possibly the easiest modification that you might want to make to WebConsole.
There are roughly two steps to add a new page:
-
Create a template KID file.
This file displays the dynamic content generated in whatever format we choose. The content is generated by the method created in the step below.
-
Add a method to
controllers.py
.The method will be called when the page is accessed, and generates the content to be passed to the template file.
Below is a simple stub template file that is enough to test if the code is hooked up correctly, before writing the template layout code.
For our example, this template file will be saved as
web_console/log_viewer/
templates/delete.kid
.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <?python layout_params[ "moduleHeader" ] = "Log Viewer" ?> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" py:layout="'../../common/templates/layout.kid'" py:extends="'../../common/templates/common.kid'"> <div py:def="moduleContent()"><script type="text/javascript"> PAGE_TITLE = 'Delete a Log'; </script> This page will be able to delete logs. `<p>Page accessed: ${accessTime}</p> </div> </html>
Example KID template file ‐
web_console/log_viewer/templates/delete.kid
This tag is necessary because the template inherits its layout
from If you remove the <div> element and access the page, an exception should be produced, stating that "name 'moduleContent' is not defined". |
The method created in controllers.py joins the act of accessing a web page in the browser to processing the data and passing it to the template KID file.
An @expose decorator must be specified for the method that will tie the template KID file to the new method, with the forward slashes replaced by periods.
Add the excerpt below to the LogViewer
class:
# This will only allow users who have logged in to access the page @identity.require( identity.not_anonymous() ) @expose( template = "log_viewer.templates.delete" ) def delete( self, **kw ): return dict( accessTime=time.ctime() )
Note that the name of the added method can be accessed directly,
since it has been exposed. To access the page, try to connect to
http://<machinename>
:8080/log/delete.
Finally, if you wish to add the page as a link in the left-hand
navigation links, under the module heading, then add the line below in
the __init__
method of
controllers.py
:
self.addPage( "Delete Logs", "delete" )
Example controllers.py
‐ Addition to the
__init__ method
Creating a basic module is a relatively straightforward procedure. Outlined below are the steps required to get a new module working within WebConsole. However, to extend its functionality, it is strongly recommended that you refer to the TurboGears documentation website, and the existing WebConsole modules' documentation.
The Python Console module is the simplest one in WebConsole, and thus the best starting point for grasping how you might extend a module once the basic framework is operational.
The steps below create a module called Devel:
-
Create the directory
web_console/devel
andweb_console/devel/templates
. -
In each of the directories above, create an empty file
__init__.py
. -
Add the module to
controllers.py
.To make the module accessible from WebConsole, the root controller has to be notified of its existence. To do this, at the end of the
__init__
method ofweb_console/ root/controllers.py
, add the excerpt below (you should see similar lines for the other modules above it):import web_console.devel.controllers self.devel = web_console.devel.controllers.Devel(self, "Devel Tools", "devel","/static/images/console.png", lambda: not isAdmin() )
Example
controllers.py
‐ Addition to the__init__
method -
Create the controllers.py for the new module.
Below is an extremely basic stub module that makes the index page available, and links it to the template KID file
web_console/devel/templates/index.kid
:from turbogears.controllers import (expose, validate) from turbogears import identity from web_console.common import module class Devel( module.Module ): def __init__( self, *args, **kw ): module.Module.__init__( self, *args, **kw ) @identity.require( identity.not_anonymous() ) @expose( template="devel.templates.index" ) def index( self ): return dict()
Example
controllers.py
for the new module -
Create the template page to use when the module is accessed.
Place the text below in
web_console/devel/templates/index.kid
.<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" py:layout="'../../common/templates/layout.kid'"> <div py:def="moduleContent()"> The Development Module </div> </html>
File
web_console/devel/templates/index.kid
After these modifications, the module Devel Tools will be displayed in WebConsole's left-hand navigation menu.
The action menu supports two types of functionality:
-
Redirecting upon selection
-
Running JavaScript upon selection
The behaviour is defined in
web_console/common/util.py
script, in
ActionMenuOptions.addRedirect()
and
ActionMenuOptions.addScript()
methods.
To add an action menu item to a cluster component type, edit the
web_console/common/caps.py
script, and for the
particular cluster process type, add a call to either
addRedirect
or
addScript
.
For example, in order to add a menu item called Clone, which only for CellApps redirects to a different page, the following should be added to the get method in caps.py:
if isinstance( o, cluster.CellAppProcess ): addRedirect( "Clone", "/cc/clone", params = dict( ), help = "Clone this process" )
Example caps.py ‐
Addition to the get()
method
To enable the detection of a new component process type, it is
necessary to add a Python class in
bigworld/tools/server/pycommon/process.py
to
uniquely identify that process.
In the example below, we add a new component process type for an SMS component, so that WebConsole can display a Send SMS action.
First, a simple stub class for the SMS component must be created. To keep all the different process definitions together, search for the class definition for ReviverProcess, then add the following text just after it:
class SMSProcess( Process ): def __init__( self, machine, mgm ): Process.__init__( self, machine, mgm )
Example process.py
‐ Definition of SMSProcess
class
The new class then needs to be associated with an MGM message ‐ in cluster.py, in the Process.getProcess method, edit the name2proc hash and add the mapping from the component network name to the class type:
... "client": ClientProcess, "message_logger": MessageLoggerProcess, "sms": SMSProcess }
Example cluster.py ‐ Addition to the Process.getProcess method
It is now possible to add an action menu item, just as described in section Adding a Menu Item for an Existing Component Type.