bw logo

Chapter 35. Web Console

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:

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.

35.1. Adding a Page to a Module

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.

35.1.1. Create a Template KID 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()"> 1
  
    <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

1

This tag is necessary because the template inherits its layout from web_console/common/templates/layout.kid, which displays the module list in the left hand side of the page, and then fills the main portion of the page by calling moduleContent().

If you remove the <div> element and access the page, an exception should be produced, stating that "name 'moduleContent' is not defined".

35.1.2. Edit controllers.py

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

35.2. Adding a Module

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:

  1. Create the directory web_console/devel and web_console/devel/templates.

  2. In each of the directories above, create an empty file __init__.py.

  3. 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 of web_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

  4. 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

  5. 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.

35.3. Add an Action Item to ClusterControl

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.

35.3.1. Adding a Menu Item for an Existing Component Type

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

35.3.2. Adding a Menu Item for a New Component Type

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.