This document contains general information, reference information and examples designed to help the user understand the moodss application and the programmer write modules for it.
Quite often, one needs to monitor changing data, whether it comes from a system, such as the different processes running on a UNIX server, or from a network, such as the volume and distribution of traffic that runs through it.
Most often, such data can be organized in a table with rows of information, each column representing a different kind of data. For example, in the case of processes running on a computer system, rows might be sorted according to their unique process identifier, with columns containing values such as CPU usage, memory usage, owner's name, time of creation, ...
The software used to view this type of information comes in different forms and shapes. UNIX users might be familiar with the top application which presents rows of process data as lines of text, whereas RMON (Remote MONitoring) SNMP software usually uses multiple windows with graphical displays, curves, pie charts, multiple configuration dialog boxes, even 3D visualization modules to visualize network traffic, connection matrices, ...
In most cases, data comes from one or more tables. A common interface, graphical with menus, drag'n'drop capability, table widgets, textual and graphical data viewers such as multiple line graphs, bar and pie charts, could be used. The user could then sort table rows, select one or more cells, rows, columns, create views such as other tables, charts, ... best suited to the way data should be presented. Once optimized, the data viewers layout and configuration could be saved for later reuse as a dashboard. In effect, what is needed is a spreadsheet tailored to dynamic data processing.
Moodss (Modular Object Oriented Dynamic SpreadSheet) is an attempt at filling these needs. It is composed of a main part (the core) and an unlimited number of modules, loaded as the application is launched or while it is running, each module interfacing to a specific type of data. The core is written in the Tcl language (at http://www.tcl.tk/) using object oriented techniques thanks to the stooop package (at http://jfontain.free.fr/). The module function is to describe the data that it is also in charge of retrieving and formatting. Modules can be written in a scripting language (Tcl, Perl or Python) or use dynamically linked libraries written in the C language (modules are packages in the Tcl sense, so any language that can interface with Tcl is supported).
Main features:
Modules are loaded when moodss is started or dynamically at a later time. Several modules can be handled concurrently. This way, you may monitor data coming from any number of heterogeneous sources. Modules are specified in the command line or dynamically loaded, and can be unloaded at any time.
Since module data access is entirely customizable (through C code, Perl, Python, Tcl, HTTP, ...) and since several modules can be loaded at once, applications for moodss become limitless. For example, comparing a remote database server CPU activity and traffic load from a network probe on the same graph becomes possible.
As features are added to moodss, different ways of viewing data will be made available while the module structure will stay the same. The goal of moodss is to become a nice feature packed generic way of monitoring any device. Moodss can be used to monitor any type of data, since the simplest cases can fit in one table with a single row, with the most complicated requiring loading several multiple table modules.
As moodss is written in Tcl and uses well supported extensions (Tktable and BLT), it will run on Tcl/Tk supported platforms: UNIX and Windows (I do not know if Tktable and BLT are available for the MacIntosh). Obviously, some modules may be specific to a platform, but the core is guaranteed to run on them all.
After reading and understanding this document, you should be able to write your own modules in order to monitor the data that you are interested in.
Moodss is free software. You can redistribute it and/or modify it under the terms described in the COPYRIGHT file.
If you are using a Linux Redhat system (7.0 or above), then use the moodss rpm file (available at http://jfontain.free.fr/) for installation. It requires the tcl, tk, blt and tktable rpms also available at the same location (see INSTALL (UNIX) and install.txt (Windows) files for more information). You are then all set for using the included Linux modules or develop your own modules.
For the current version (17.1), the following packages must be installed before attempting to install moodss (make sure to check the INSTALL file for the latest information):
Eventually, if you want to use the database history browsing feature, either of the following:
The XML, DOM, pie widgets, stooop and scwoop libraries are included in the self contained moodss application file. Therefore, it is not required to install the tclXML, tclDOM, stooop, scwoop and tkpiechart packages, unless you want to work on the moodss source code itself. However, should you want to get more information on those extensions, you will find the latest versions:
at http://jfontain.free.fr/, and:
at http://tclxml.sourceforge.net/.
Finally, if you want to develop your own modules in a language other than Tcl, such as Perl or Python, you will need:
also at http://jfontain.free.fr/.
The moodss application is composed of the core software and one or several modules. Modules are implemented as Tcl packages and thus usually comprise a Tcl, Perl or Python source file and a pkgIndex.tcl file as required by the Tcl package implementation.
The core loads one or more modules, whose names are passed as command line parameters, come from a save file or are dynamically loaded, and starts displaying module data in one or more tables. The tables are then updated at the frequency defined by the poll time, which the user may change, or asynchronously for the relevant modules. For example, to launch moodss with the random module, just type (on a UNIX machine):
$ moodss random |
All the module code and data are kept in a separate namespace. The module data is stored is a single array including some configuration data used when the module is loaded by the core, and variable data (displayed in the application table and eventual graphical viewers). If a module is synchronous, it must start updating its data when requested by the core. If a module is asynchronous, its data may be updated at any time. The synchronous or asynchronous nature is specified in the configuration data for the module.
The initial data tables represent the first data views, from which any number of cells can be selected. Data viewers can be created by dragging and dropping cells into a graph, bar chart, pie chart, statistics and values tables, free text or thresholds sites. In turn, these viewers can display more table cells, which when dropped into the viewer, result in the creation of corresponding data graph lines, chart bars, pie slices, table rows or text cells. Cells or rows can be removed from existing viewers, by simply selecting them and dropping them in the eraser iconic site (in the shape of a pencil eraser).
Any viewer can be mutated (its type changed) by dragging from a viewer icon and dropping into it. For example, create a stacked data bar viewer from several cells, then drag the 3D pie icon into it. Any viewer can also be destroyed in one shot by dropping the eraser icon into it.
Any draggable data can be dropped in any valid drop site at any time. It is thus possible to drag several data cells from any table or any viewer into other ones, the thresholds interface, the eraser, ... even if the data comes from different modules.
All data viewers can be moved and resized at will with the help of a simple internal window manager.
The current configuration (existing pages, loaded modules, tables and viewers coordinates, sizes, poll time, main window size, ...) can be saved in a file at any time, so that an identical dashboard can be relaunched at will.
Note that all configuration and preferences data is written to disk in XML 1.0 format, to follow standards, and so it can eventually be edited using a plain text or specialized editor.
Immediately after launch, module(s) is(are) loaded and initialized, with corresponding messages displayed in the message area, as follows:
Soon after, tabular data is displayed in one or more table widgets with the module identifier as title, automatic scroll bars, between the menu bar, the tool bar, the drop sites with graphical viewers, statistics and values tables, free text, thresholds and eraser icons, pages tabs and a message area, as one can see below:
If there are several pages (views), notebook tabs appear below the canvas area (see above).
The message area is used to display status information, such as when the data is being updated, and help information, as the user moves the mouse pointer over sensitive areas, such as table column headers. Contextual help on all menu items is also activated as traversal occurs, either using the mouse or the keyboard, resulting in a short explicative string appearing in the message area and related to the active (highlighted) menu item. Further help is provided through widget tips (also known as balloons) when appropriate (on data table column headers, for example), and of course the Help menu.
The message area header label also serves as a thresholds indicator for all the thresholds activated in the application. The label takes the color of the most important active threshold (according to its importance level). If there are several active thresholds of the same most important level, but with different colors, then the colors are shown in sequence, 1 per second. A widget tip also shows the most important thresholds summaries (also see page tabs and message).
The window title shows the name of the loaded module(s) along with the poll time, if not in database mode.
When several modules of the same type are loaded (for example, CPU statistics on a group of servers), the initial data tables feature the module name followed by an instance number (module<N>), or the module identifier generated from the module code (cpustats(host.domain.org) for example). A lone module keeps his unmodified name as table titles.
Any data displayed in a table can be sorted (provided that the related module allows it) at any time by simply clicking on a column title. Clicking on the same column title again sorts the data in the opposite order, thus toggling between increasing and decreasing orders.
When sorting, the selected column is used as a reference, meaning that all rows will be rearranged so that the selected column appears sorted, with values either increasing or decreasing.
A little triangular indicator is placed to the right of the reference column title label, pointing up or down depending on whether the sorting order is decreasing or increasing.
If the module allows it (most modules use automatic column sizing), table columns can be interactively resized by holding the first mouse button down on a column border. The mouse cursor is changed to an horizontal double arrow on column borders to show this capability.
Aside from the main tables, graphical and textual viewers can be created for monitoring table cell data over time. Viewers can also be deleted, data views (such as pie slices, curves, ...) can be added or removed from existing viewers, ... These functions are all implemented using the drag and drop functionality.
For all viewers, if a module identifier string is required (provided by the module, several instances of the same module, ...) for proper cell identification, that string will be placed first in the label. For example, data cells originating from the third instance of the random module would be labeled: "random<3>: data cell label".
Graphical viewers available at this time are BLT graph viewers (see images below), side-by-side bars charts, overlapped bars charts, stacked bars charts, 2D pie charts and 3D pie charts. Graph viewers feature an automatic cross hair which follows the mouse pointer movements inside the plotting area. Corresponding coordinates are updated in real time in the main window message area.
When displaying database history data, the graph viewers display all data samples for the selected time range, whereas all the other viewers only display the values at the time selected by the right side cursor in the database module instances viewer (see database section).
There are 3 textual viewers.
The statistics table displays for each row the cell label, the current, average, minimum and maximum values since the row was created. Data cells can be inserted one or several at a time through a simple drop. Rows can be deleted by selecting any cell in the row then dropping any number of them into the eraser drop site. Data cells with missing data (could be no longer available if coming from a vanished process, for example) display the ? character. When displaying database history data, the average, minimum, and maximum values are calculated for the selected time range.
The free text viewer is an editable Tk text widget with any number (including zero) of embedded data cell windows. Data cells can be inserted one or several at a time through a simple drop, as with the other viewers. New data cell windows are inserted at the current insertion cursor position. Data cells can be deleted by selecting then dropping any number of them into the eraser drop site. They can also be deleted using the keyboard Delete and Backspace keys, which also work on the regular text, as well as the expected other key bindings. When dropping data cells, each data cell window is preceded by a relevant label text for the cell, which can later be edited at any time.
The values table displays for each row the cell label and the current values of the cell data. It behaves exactly as the statistics table, but without the average, minimum and maximum columns. It can be used in dashboards to display cells that are hidden when the corresponding module tables are iconified.
Here is a screen shot of loaded ps and cpustats modules with several graphical viewers:
Here is another shot featuring a free text viewer with loaded cpustats and memstats modules:
Dropping cells into the thresholds drop site in the main window, or selecting the edit menu thresholds entry results in the following dialog box being displayed. While displayed, user interaction with other parts of the user interface remains possible, so that more cells can be dropped into the thresholds dialog box table itself.
More information on thresholds.
When not in database mode, the title bar shows the currently loaded modules and the current poll time, if any (there could be only asynchronous modules loaded), as the following image shows:
As in the exit menu, if there are unsaved changes, the user is given the opportunity to save them to a file before all existing modules are unloaded, along with any viewers.
This menu entry allows the user to open an existing saved configuration file (also useful for editing moomps configuration files) to replace the currently loaded modules and viewers with those described in the file to open.
As in the exit menu, if there are unsaved changes, the user is given the opportunity to save them to a file before opening the new file.
Note that all existing modules will be unloaded, along with any viewers, prior to loading the configuration from the open file, thus acting as if the open file was directly and initially loaded when moodss was started.
The current application configuration (including existing data viewers) can be saved in a file, which achieves a dashboard functionality. The format used for saving is XML, as the following saved file extract shows:
<?xml version='1.0'?> <!DOCTYPE moodssConfiguration> <moodssConfiguration> <version>16.8</version> <date>12/26/02</date> <time>21:29:38</time> <configuration graphNumberOfIntervals="100" canvasBackground="#fff4ff" canvasWidth="1152" pieLabeler="peripheral" canvasHeight="864" graphMinimumY=""> <viewerColors> <item>#feffff</item> ... |
Once moodss has been launched with one or several modules and tables have been moved, resized, viewers created, moved and resized, the current configuration can be saved in a .moo file, and later reused by passing the corresponding file name with the -f (--file) command line switch.
The following information is saved in the file (which is human readable):
When using this menu for the first time, and if a file name was not specified in the command line, the file selector dialog box appears so that the user may choose a file name (with a .moo extension).
Once a file name has been specified (either through the command line or the file selector dialog box), that file name is reused whenever the File Save menu is used.
This menu behaves as expected, with the user always having to choose a file name using the file selector dialog box (see Save menu above).
This menu may be used at any time to change the current save file name.
You can dynamically load a module by simply selecting its name from the available list discovered when the selection window opens. When the module is selected, its options appear and any number of them can be filled (options ending with password show * characters when the user types in the option value). The module documentation can be simply accessed by clicking on the Help button.
Once the new module, or new instance of an already loaded module, is loaded, the application behaves as if the module had been loaded from the start. Use the Close button when done loading modules.
This dialog box allows the user to:
Once a module is unloaded, the module data tables disappear, but the viewers containing cells pointing to the module data remain, simply showing that the data is now invalid.
Note: if a module is reloaded, or unloaded then loaded again, viewers that monitored the unloaded module data will not resume monitoring the newly loaded module data since internally Tcl traces on variables were removed.
When selected, the module current options appear (options ending with password show * characters in the option value). The selected module documentation can be simply accessed by clicking on the Help button.
Use the Close button when done.
View the loaded modules and their options, without the possibility of unloading a module. This menu entry is available for example when moodss is started in read-only mode (see Command line).
A module can be picked from a list of the currently loaded modules. Eventually, the module current options appear (options ending with password show * characters in the option value). The selected module documentation can be simply accessed by clicking on the Help button. Use the Close button when done.
Both the moodss GUI application and the moomps daemon have the capability of storing history over time of any module data cells, by saving their values in a database (also see the preferences database and moomps sections, and the frequently asked questions).
From this menu, it is possible to thoroughly browse the history database, while keeping a user interface (drag'n'drop, viewers, help, ...) behaving as in real time monitoring.
This menu (or its associated button in the tool bar) opens the database instances dialog box (provided database access is properly configured), and shows (with eventually a small delay depending on the speed of the database used) all the module instances, as the following screen shot shows:
Note that you will be given the opportunity to eventually save your configuration if you were monitoring in real time with some loaded modules, prior to switching to database mode (deferred time).
A tree shows all the module names, with instances containing data cells stored in the database (also see the database edit menu). Below each module name, all the instances for that module are shown when clicking on the + tiny icon. Finally, below each instance, all the data cells can be found, with eventually their comments in parentheses.
Clicking on the SQL button while a data cell is selected show the SQL query that can be used to retrieve all the data samples for that cell. That can help when developing an external application accessing the database, or can simply show the extent of time for that cell history.
To actually load an instance into the moodss GUI, either select the desired instance and then click the OK button (which closes the dialog box), or drag any number of instances (but one at a time) into the database module instances viewer (as seen in the picture below) while keeping the dialog box opened.
For each instance, the available extent of time for all data cells histories for that instance is shown on the time scale of the database module instances viewer. If an instance contains only data cells with empty histories, an endless dotted line is shown (as seen above for the random module). The module options for each instance can be displayed in a widget tip by keeping the mouse pointer over an instance name.
For each displayed instance, a corresponding data table is also displayed (see the snmp data table in the screen shot above). For each data cell, the corresponding comment (if it exists, see the database edit menu) or the data cell name is displayed in the left column, while the last values are displayed on the right side. The original module help for data is preserved and can be accessed via a widget tip, which appears when the mouse pointer is placed on top of a data cell label.
From then on, all the functionality that you are used to in real time mode is available to you (expect for the thresholds settings which only make sense while monitoring in real time). As an example, in the screen shot above, the WAN input and output traffic cells were dropped in a stacked graph viewer, which after a delay depending on the database engine speed and the amount of stored history data, displayed the complete history for both cells.
A specific range within the time extent can be set using the blue cursors in the database module instances viewer (just drag them with the mouse and refresh the display by clicking on the refresh button or using the refresh view menu).
Alternately, the View Database Range menu can be used to precisely set the date and time limits from a dialog box.
All the views are then updated, with the viewers either showing the complete history or the value at the right cursor, depending on their type (the statistics table viewer shows the average, minimum, maximum, ... values for the selected time range).
Module instances can be unloaded by dragging from their name in the database module instances viewer into the eraser drop site.
Starts recording data history over time in the database (defined in preferences) for the cells specified in the database interface.
You may also use the associated button in the tool bar.
Data storage can be stopped at any time using the Stop menu. Then data history can be displayed and manipulated using the Load menu.
Note: when data history is being recorded, if the tool bar is visible, the VCR like record button is disabled and surrounded by a bright red ring (see tool bar screen shot).
Stops recording data history in the database, process started using the Start menu.
You may also use the associated button in the tool bar.
You can print the canvas area in postscript to a printer or a file, which you may choose using the classical file browser. Please note that only the strictly visible area, without the scrollbars, will be printed.
You can choose the printout orientation (portrait or landscape), palette (color, gray scale or monochrome) and the paper size.
Due to widget and architecture limitations, the printout is pixel based. As a benefit, it is extremely WYSIWYG :-).
Also, because of design limitations, printing is disabled on Windows platforms.
The printout is sized according to the following rules:
When the print menu entry is selected, a dialog box appears, as can be seen in the screen shot below (which also shows the preview window in the background):
Depending on the print configuration (see Preferences), either a print command line entry or a printer selection list appears below the informational message (note: successfully tested against the Berkeley LPR print spooler and the Common UNIX Printing System (CUPS)). Hitting the OK button sends the data to the selected printer or through the specified command line.
The preview functionality uses gs (also known as ghostscript), which must be installed (note: version 5.50 or above is required, as 5.10 is buggy). If gs cannot be found or executed, the preview button is grayed. If the gs version is below 5.20, the button is also grayed and a window tip explaining the cause is displayed when the mouse pointer is located above the button.
When the gs utility is available and of the correct version, the preview button can be depressed, in which case the print dialog temporarily disappears (so that the application main window is not obscured), and reappears along with the preview window, once the printout view has been calculated.
The preview window also features a zoom menu for resizing. If the user so desires, one or more printing parameters, such as page size, orientation, ... can be changed while the preview window remains visible. Hitting the preview button then results in both the print dialog box and the preview window to temporarily disappear while the new page look is calculated. After a short while, they both reappear with the preview updated accordingly, and having kept the same zoom ratio.
Also note that due to the implementation of the graphical layer on UNIX systems, any window or object obscuring the canvas area will also be printed, but this is unlikely, as the moodss application window will be in front when the print menu item is selected.
Use this menu to gracefully quit the moodss application. You may also use the window manager to close the application.
If there are unsaved changes (configuration, viewers created, tables or viewers moved, stacked, ...), the user is given the opportunity to save them to file (see File Save menu). The file selector dialog box is used if no save file name is currently known by the application.
Opens the thresholds dialog box.
A list of threshold entries is displayed in a table with the following columns:
When a row is selected in the table above, the corresponding data cell current value and original label are displayed next to an editable list of email addresses.
To enter a new address, simply hit the <Return> of <Enter> key in any filled cell. An empty cell is then created at the bottom of the list. To delete an address, simply empty it using the usual <Backspace> or <Delete> editing keys.
When a threshold event occurs, an alert message will be sent to each recipient (also see preferences). Any error occurring when attempting to send mail will be visible in the trace module.
You may also include a screen shot (in JPEG format) of the moodss window at the time the threshold occurred, by ticking the check button, next to the little computer monitor icon.
Note: the screen shot may turn out to be black, depending on whether the moodss window was visible when the threshold occurred, due to the X Window System implementation. More work needs to be done in that area to solve that problem.
When selecting another row in the thresholds table, or closing the dialog box using the OK button, the syntax of the displayed emails is checked and errors eventually reported in a message box. It is then recommended to test that emails actually get sent and to the right recipients, using the test functionality described below.
Below, an editable text area can be used to enter a UNIX script or Windows command script, invoked when the threshold is crossed. See script section below for passing arguments to the script. The output of the script (if any and including errors) can be viewed in the test trace area underneath the script area. Errors are eventually visible in the trace module data table if displayed.
Help is provided through widget tips on the table title cells, and a help button, which directly opens the main help window at the relevant section.
Creating threshold entries is done through the drag'n'drop mechanism by dropping data cells into the thresholds drop icon or into the thresholds dialog box table itself (provided it is open, of course). Any number of thresholds can be set on the same data cell.
Selecting a row is done by clicking on any cell of the row. The row is then highlighted.
Below the thresholds table, the following buttons, that act on the selected row, can be found:
The following threshold types are supported:
Depending on the source data cell type, specific internal comparison techniques are used.
For the ASCII type, the current cell value can be lexicographically less than, equal to, or greater than the threshold value. Empty threshold values are allowed. The strings are compared in a case-insensitive manner.
The dictionary type is handled as the ASCII type, with case ignored except as a tie-breaker and if there are embedded numbers, the numbers compare as integers, not characters.
For the clock type, the cell and threshold values are first converted to seconds, then compared.
When the threshold type is , the condition occurs when the data cell value cannot be determined, and only in such a case. It is the only type of threshold that can be triggered by the void nature of a data cell.
This condition can happen when a data cell (of any type) no longer exists (for example, when its row has disappeared), or the data cell is of numeric type, still exists but contains void data (displayed as ?).
For other types of data cells, the displayed value when the data cell becomes void should be documented in the module help, and setting a threshold on such a cell being void should instead be done using the equal () threshold type on the specified value.
If a non-transparent color has been selected (see previous section), whenever the threshold condition occurs, all displays of the monitored data cell change color (in data tables or in viewers), and return to their normal color when the threshold condition no longer exists.
When several thresholds are placed on the same data cell with different colors, and they all trigger, the behavior is undefined at this time (but the most recent threshold is likely to prevail).
Whenever a threshold is triggered (the manner of which depends on the type), the corresponding script is passed to the UNIX shell interpreter (the sh UNIX command), after variable substitution.
Substitution occurs prior to script invocation if the script contains any % characters. Each % and the character following it is replaced with information from the threshold occurrence. The replacement depends on the character following the %, as defined in the following list:
Any error returned by the script is visible in the trace module.
When a row is selected, it is possible to drag from the current cell value display, into the thresholds table itself to create a new entry for the same cell, or into any viewer icon or existing viewer.
When the dialog box is displayed, existing thresholds are sorted according to their importance level with the most important on top. If several thresholds of the same type and same importance level are set on the same cell, the highest (according to the cell data type) threshold is displayed first.
When new thresholds are interactively added to the dialog box, they are displayed at the bottom of the thresholds table, but will be sorted as the others the next time the dialog box is opened.
While the thresholds dialog box is visible, threshold conditions are not checked by the software, which means no cell color changes, no email alerts, ... can occur.
Internally, threshold conditions are checked in reverse order compared to the displayed order, that is the most important thresholds last. As a consequence, color gradients on a cell are automatically achieved.
For example, if you set the 3 following up thresholds of the same importance level (does not matter in this case, say info) on the same cell:
you will get a color gradient effect, as the cell value approaches 100, it will turn yellow, orange then red.
You can also force color gradients on a cell by combining importance levels and colors.
For example, if you set the 3 following up thresholds on the same cell:
you will get an importance color gradient effect as the cell value approaches 100.
There are still a lot of features to implement: please see the TODO file.
When selected, this menu launches the configuration dialog box, as described in Configuration.
Creates a new page after the existing pages. If there are no pages, that is all modules are displayed in the sole main window canvas, those contents are placed in the first page just created. The page is then displayed, its tab opened in editing mode so the label can be immediately set by the user.
To delete a page, just grab its tab and drop it in the eraser drop site. Note that a page must be empty of any table of viewer before it can be deleted, except for the last remaining page, the deletion of which results in only its tab being removed.
Allows the creation of empty viewers of any type (graph chart, stacked graph chart, overlap bar chart, side bar chart, stacked bar chart, 2D pie chart, 3D pie chart, statistics table, values table or free text).
This menu is only visible when not running in read-only mode (see Command line).
Opens the database dialog box, a drop site for modules data cells. The cells dropped in the database dialog box will be monitored by moodss (see the Database Start menu) or the moomps daemon (provided of course that the configuration file (see Save menu) is used by the moomps service).
When the moomps service is running, any number of data cells can be monitored over time, using a SQL database as storage mean. The cells values are stored in the database, which can then be used from, for example, a spreadsheet software. Using such tools, it becomes possible, for example, to create history graphs, presentations, ... using the moodss database as data source. (more information can be found in the moomps documentation).
For example, if you want to monitor CPU usage over time on a Linux server, load the cpustats module, open the database dialog box, drag and drop the user, system and nice cells in the database interface and finally save the configuration in a .moo file within the moomps configuration directory.
Note that only cells coming from an original module table can be dropped in the database window. If you attempt to drop a cell from say a statistics table, the cell will not appear in the database window and an error message will be displayed in the main application window message area.
When selected, this menu launches the preferences dialog box, as described in Preferences.
The format used for saving the preferences is XML, as the following preferences file extract shows:
<?xml version='1.0'?> <!DOCTYPE moodssPreferences> <moodssPreferences> <version>16.8</version> <date>12/26/02</date> <time>21:29:31</time> <traceNumberOfRows>20</traceNumberOfRows> <printToFile>0</printToFile> ... |
On UNIX platforms, the preferences file is named .moodssrc and is saved in the current user home directory, with write and read rights strictly restricted to the current user, as sensitive information, such as database passwords, can be stored in the preferences file.
Immediately refreshes display of all loaded asynchronous modules.
The View menu (may not exist, see below) contains the Poll Time entry which when selected launches the corresponding dialog box, as shown below:
The user can select a new poll time among the module choices from a spin entry widget, or directly type in a new value, as long as it is not smaller than the module minimum poll time, in which case a warning message is displayed.
When several modules are used, the minimum poll time is the greater of the minimum poll times of all modules. The default poll time (used when moodss is started) is the greater of the default poll times of all modules. The available choices in the poll time dialog box is the combination of all modules poll times.
The Poll time menu entry is available only when needed, which is not the case if all the loaded modules are asynchronous. If this case, the Options menu itself is not displayed.
This menu is only visible when not running in read-only mode (see Command line).
Allows the specification of precise date and time limits for database views, loaded using the File Database Load menu.
Note that once you have validated your choice, the dialog box closes and all database related views are automatically refreshed (it is not necessary to manually use the refresh view menu or button).
Opens a separate window containing the 20 latest informational, error, ... messages (if any) internally generated by the loaded modules. This menu entry when selected again closes the window.
A resident trace module is actually used to fill the contents of that window, which does not prevent loading other instances of the trace module in the main window, as can be done with any other module.
Shows or hides the tool bar.
This menu launches an embedded HTML viewer with this very document minus the sections related to module development.
This menu features one sub menu per module, which when selected displays the module's help data.
Displays version, author, extension authors, license and basic information about moodss.
This common user interface part makes some menu actions direct by using buttons with embedded icons. Help is provided through widget tips (balloons).
The contents of the tool bar change depending on whether a module is loaded, a save file has been used, ... and always fit to the current context. The tool bar can be hidden or displayed at the user convenience using the Tool Bar menu.
The largest part of the main window is the canvas area, used to display and manage data tables, viewers and icons. Its background color can be changed in configuration and preferences. An internal window manage handles all the operations within the canvas.
All data viewers can be moved and resized thanks to handling areas in the data viewer borders. When moving the mouse pointer over these areas, the mouse cursor changes to indicate the possible action. Corner handles allow resizing in both X and Y axis. Handles in the middle of the sides allow resizing in either the X or Y axis direction. All other areas can be used for moving the data viewer as shown by the quadruple arrow cursor.
Clicking on any part of the border changes the stacking order: the viewer being clicked on either goes below (eventually becomes hidden) the other viewers, or becomes fully visible (on top, eventually hiding other viewers). You can also circulate between tables and viewers by using the Tab (forward) and Shift-Tab (backward) keystrokes, which successively bring the next object to the top in the stacking order.
When moving or resizing, the message area displays the current coordinates or size in real time as the mouse is being moved.
Further description of this small window manager functionality is useless, as it behaves like a basic window manager (let me know if it does not).
Module tables can be minimized by clicking on the down arrow button of the right side of a table title area. The resulting icons are arranged at the bottom left of the canvas area, from left to right. Icons can be moved simply by clicking and holding the left mouse button. Double clicking on an icon results in the corresponding table to become visible again at its original position.
Once moved by the user, a table icon icon will remember its coordinates. That is if the table is restored then minimized again, the icon will be placed at its latest position.
If there are several pages displayed (see New Page menu for creating pages), the pages tabs are visible near the bottom of the main window (by default, but the tabs can also be placed on top using the pages section in preferences).
A page tab label can be changed at any time while the application is running. Double-click on the chosen tab, the text background then changes and the insertion cursor is displayed. Note that the whole text is initially selected to make it easy to replace the label by immediately typing. Once done, press the Return key to confirm or the Escape key to abort editing the tab label, in which case it returns to its previous value.
Viewers and tables can easily be moved across pages by using the internal window manager. Move the table or viewer as you normally would but keep going into the destination page tab until the black drop border appears around the tab label, then drop. The table or viewer is placed at the upper left corner of the destination page.
If a page contains any table or viewer containing data cells with active thresholds, then the page tab background takes the color of the most important threshold (according to its importance level). If there are several active thresholds of the same most important level, but with different colors, then the colors are shown in sequence, 1 per second, as the page tab background.
Additionally, up to 3 of the most important threshold summaries are displayed in a widget tip (or balloon) when the mouse cursor lies over the page tab (see picture above).
The message area header label also serves as a thresholds indicator, just like a page tab, but for all the thresholds activated in the application.
The behavior is identical to a page tab, with the most important thresholds colors displayed in sequence if necessary, and a widget tip showing the most important thresholds summaries.
At the bottom of the main window, the message area is used to display various help and information strings for the user. For example, every time that a module is updated, when a menu entry is being activated, or to instruct the user on how to edit a page tab.
Drag and drop in moodss tries to behave as in popular GUIs. For example, to create a graphical plot, one must first select one or more data cells in a data table, hold down the first mouse button (the left one for a right handed user) while dragging over to the left-most icon below the menu bar (when dragging an object, as the mouse pointer passes over possible drop sites, they are highlighted with a thin black border for user feedback). Releasing the mouse button at this time results in the creation of a BLT graph viewer.
Only valid drop sites for the data being dragged are highlighted when the mouse cursor passes over them, thus guaranteeing error free operations (if there are no bugs, that is :).
In summary, data cells can be dragged from any table or any viewer into any viewer drop site icon, any viewer or the eraser.
All icons right below the menu bar are valid drop sites for data cells (several may be dropped at the same time). From left to right:
For example, a graph viewer with 1 curve is created by dropping 1 data cell into the graph viewer icon.
Once a viewer exists, it also acts as a drop site for data cells, which may be dragged from any table or other viewers. Dropping one or more cells directly in the viewer results in corresponding lines, bars, slices or rows being created and automatically updated. Each new graphical element is assigned a new and different color.
You may delete one or more viewer elements (graph lines, bar chart bars, pie charts slices, statistics or values table rows or free text cell window) from a viewer by selecting them (using the first mouse button) through their labels. Several elements can be selected by depressing the control key as the first mouse button is pressed. The selection can also be extended by depressing the shift key along with the first mouse button. The pie slices can also be directly selected by clicking on the slices themselves.
Then dragging from the viewer to the eraser drop site (the pencil eraser) on the upper right side of the main window and releasing the first mouse button result in the corresponding viewer elements to be destroyed. When there are no remaining elements, the viewer itself (graph, bar chart, pie, values or statistics table) can be destroyed by dropping it into the eraser site. The free text viewer can only be deleted this way when completely emptied of any text and data cell window.
Any viewer can be deleted in one shot by dropping from the eraser icon into it.
Yet another way is to move the viewer (as usual, using the internal window manager handles) into the eraser.
Any viewer also acts as a drop site for viewer type data, which allows viewer mutation by just dropping from the new viewer type icon into the existing viewer. It is much quicker than destroying the existing viewer and create a new one of the new type, while remembering which data cells were monitored by the viewer of the old type.
When mutating, if some cells in the current viewer no longer exist (they may belong to a disappeared statistics table), they are not made a part of the new viewer, and a warning message is flashed to the user in the message area.
A page tab is a drop site for any table or viewer, which allows transferring them between pages. Proceed by moving the table or viewer (as usual, using the internal window manager handles) into another page tab (wait until you see the drop black outline around the tab label to release the mouse button). The viewer can then be found in the upper left corner of the target page.
A table is obviously a drag site. One or more cells can be dragged at once after selection, using the traditional single/shift/control mouse click technique.
Any viewer is also a drag site. It requires selecting one or more viewer elements before initiating the drag operation from any selected element in the viewer. If there are no selected elements, dragging is impossible: the mouse cursor is not changed into the drag circular cursor.
If a viewer contains no elements, then the viewer itself can be dragged and dropped into the eraser.
All viewer icons (below the menu bar) are drag sites for viewer type data, which allows quick viewer mutation (see mechanism description in Drop sites).
The eraser icon is also a drag site of the killing action type, which allows viewer destruction in one shot.
A viewer or a table is also a drag site when being moved via its window manager handles:
Launching moodss is very simple:
$ moodss |
or
$ wish moodss |
if the Tcl/Tk wish interpreter is not found in your PATH. You can then dynamically load modules from the File menu.
You can also just pass one or more data module names as parameters, as in:
$ wish moodss random |
or, for 2 modules at once:
$ wish moodss ps cpustats |
You can specify the same module more than once and with different arguments in the command line:
$ wish moodss ps -r host.domain ps -r otherhost.domain |
When several modules of the same type are passed as argument, the initial data tables feature the module name followed by a number as title. For example "ps<2>". If the module provides an identifier string, that text will be used instead, as in "ps(host.domain)". Data cell labels include the module identifier or numbered module name as well, for proper identification.
You may eventually specify a poll time in seconds using:
$ wish moodss -p 25 random |
Note that when all the specified modules are asynchronous, the poll time option specifies the preferred interval for graph viewers.
You can also specify pages tab labels as a list, using:
$ wish moodss --tabs '"Servers 1" "Servers 2" Workstations' |
which would create 3 tabs. Note that it is not required to specify tab labels when loading from a save file, as they are saved along. Once created in this way, tabs can be edited in place (see user interface).
Once saved through the File Save menus (for example in save.moo), the configuration can be retrieved using:
$ wish moodss -f save.moo |
which would result in the same modules being loaded, the same viewers displayed at the same positions and sizes, the same poll time being used, as well at the same application window size. New modules data displays can be added at any latter time to existing dashboards by specifying modules on the command line after the -f (--file) switch / value pair.
Command line options include:
Moodss command line options must appear before any module name appears on the command line, so as not to interfere with the module options.
In debug mode, when errors occur within the module namespace body or initialize procedure, the error message (either in text output when loading the module from the command line, or in a message window when dynamically loading the module) is followed by a Tcl stack trace of what was in progress when the error occurred (see the Tcl error manual page for further information).
Module themselves can take options (if programmed to do so, see module initialization), through command line arguments placed right after the module name and before the next module name, if any.
For example, the following command:
$ moodss -p 15 random --asynchronous arp --remote jdoe@foo.bar --numeric route --numeric |
causes the random module to update asynchronously, the arp module to collect data from the foo.bar host under the jdoe login name and not attempt to lookup symbolic names for hosts, with the last module route doing the same.
Note the setting the application poll time to 15 seconds does not interfere with the module options.
The moodss core checks the validity of module options according to the information provided by the module programmer. Any invalid option / value combination for the module is detected, reported on the standard error channel before the application exits.
Finally, it is always possible to determine the valid options for a module, using the following command:
$ moodss module --help |
All configuration parameters can be set using the following interface:
The Configuration dialog box allows the user to change global settings for the current view (also called a dashboard). This data is stored along when saving the configuration to a file (see Save menu).
Changing configuration choices do not affect the Preferences choices, which are used as initial values the first time the user modifies the configuration. Configuration settings have a higher priority than preferences settings, but are lost when not filed.
Configuration entry is done through pages organized in a browsable hierarchical tree, always visible on the left side of the configuration dialog box (see picture above).
After selection of the category from the tree on the left, a related dialog interface appears, which may or may not allow immediately applying new data values.
Clicking on the OK button results in the current configuration data to be saved in memory. It will be stored in the save file when requested (see File Save and Save As menus).
Specific help can always be accessed by clicking on the bottom Help button when in a configuration page.
The canvas is the data viewers background area, and its configuration (size, color, ...) can be changed as described below.
The canvas width and height can be changed so that all the different tables and viewers that the dashboard comprises can fit nicely within the viewing space.
The default is the current screen size.
The size is immediately updated when clicking on the Apply button.
The canvas color can be changed for your viewing pleasure...
The color is immediately updated when clicking on the Apply button.
The viewers are used for viewing table data, and their configuration (colors, ...) can be changed as described below.
These are the colors of viewer elements. For each viewer that needs different colors for displaying data cell elements (such as graphs, pie charts, ...), each new element uses the next color in the sequence (wraps around if necessary).
Advice: adjacent colors should be very different, colors should be visible on a black background.
Note: in a future version, creating, deleting and moving colors in the sequence will be possible.
The number of samples (on the X axis) can be changed for data graph viewers. The more samples, the wider the X axis and thus the longer the visible data time span.
By default, the Y axis scales automatically with its minimum and maximum values set to those of all plotted data points, for maximum precision. Optionally, a value of 0 can be fixed for the Y axis minimum.
Note: these settings will not be used for existing graphs but for newly created ones.
The labeler type for data pie viewers can be changed as follows:
The selected type will not be used for existing pies but for newly created ones.
The preferences dialog box is used for application-wide settings.
It is very similar to the configuration dialog box, except that it also includes canvas printing and email servers settings.
It allows the user to change application wide settings, saved in a global file (known as an rc file to UNIX people), used for initialization when the application is started. Since this file is stored in the user home directory (on a UNIX system), it provides a way for a user to customize the look, behavior, ... of the moodss application in a permanent manner.
Preferences choices, when applied (either by choosing Apply or OK) also affect configuration settings.
On UNIX systems, preferences data is saved in each user home directory under the rc file named .moodssrc.
On Windows, data is saved in C:\.moodssrc (by default, but depends on the HOME variable).
After selection of the category from the tree on the left, a related dialog interface appears.
Help is provided for each interface, which may or may not allow immediately applying new data values.
Clicking on the OK button results in the preferences data to be written to the rc file.
The canvas area can be printed in the Postscript data format.
The default behavior when printing can be set to either a printer or a file, but can always be overridden in the print dialog box launched when actually printing.
Various parameters, such as orientation, palette and paper size can be set.
The print command typically reads the Postscript data from its standard input and redirects it to the specified printer (for example, on UNIX, 'lpr -Pacme' will print the canvas area on the "acme" printer.
By default, the command is set to 'lpr -P%P', which by including the %P generic printer name tag allows the user to pick the printer from a list (drawn from the /etc/printcap database) at printing time.
When printing to a file, you may choose its default location and name with the file browser. Note that it can be overridden in the print dialog box launched when actually printing.
When pages are used (see New Page menu), the user can choose whether the pages tabs are positioned on the top or the bottom side of the pages.
When a threshold event occurs, an email alert message can be sent (see thresholds).
Proper identification is required so that the originator of the message (the From address field in an email message) is known. Use your own email address or another email address (such as moodss@your.domain.com) that you control.
In any case, the From address field must contain a valid email address.
Your user name is used by default.
When closing the dialog box or moving to another section, the syntax of the email address is checked and errors eventually reported in a message box.
Sending email requires at least one Outgoing mail SMTP server. Input the one that you use for sending your emails (check your browser or email software current configuration), or any other valid SMTP address (consult your system administrator in case of doubt).
You may enter additional servers that will be used as backups of the main server in case it fails. Keep pressing the <Enter> key to append servers to the list. To remove a server from the list, simply empty its list cell using the usual editing keys.
The local host (127.0.0.1 address) is used by default as the main SMTP server.
When a threshold condition occurs, a summary is sent to the trace module (visible via its window if it is displayed or by its table(s) if it is loaded as a module). This is the behavior by default, which can be turned off from this preferences page.
The number of rows in the trace window can be set (it is 20 by default).
Note: the new value will not be taken into account immediately but the next time the application is started.
The data in this page is for the moomps daemon usage.
If you want the moomps daemon to store data cell values in a history database, set the following options:
(only available on UNIX platforms)
The moomps daemon preferences file location can be changed from the /etc/moomps/rc default value. You must have write rights to the chosen file and its directory, as determined for the current user (generally root as traditionally used for configuring daemons). Error messages are appropriately displayed when this is not the case.
When the preferences are validated, all moomps relevant data will be written to the chosen file in XML format. You may enter no file name by clearing the textual entry space.
The daemon preferences file is saved with write and read rights strictly restricted to the current user, as sensitive information, such as database passwords, can be stored in it.
The minimal module is as bare bone as possible and can be used as a starting point for developing new modules.
The Perl version of the minimal module.
The Python version of the minimal module.
A module for the MySQL database server (documentation).
A module for the MySQL database server (documentation).
A module for the MySQL database server (documentation).
A module for the MySQL database server (documentation).
A module for the MySQL database server (documentation).
A module for the MySQL database server (documentation).
A module for the MySQL database server (documentation).
The random module code contains a lot of comments, providing some extra documentation about module coding.
Documentation.
The Perl version of the random module.
The Python version of the random module.
The trace module can also be displayed in a separate window (see trace menu). Also see the trace module documentation.
All examples are drawn from the random Tcl and Random Perl sample modules.
A module is a package, it must have a name and a version.
Note: version number management is important as the module major version number is taken into account when storing data cells history in a database (can be done from both the moodss GUI application and the moomps daemon). Modules developpers must insure that no major changes occur between minor releases of the module. In other words, if there is a change in the module data structure, such as adding a new column, changing the order of columns, changing column data, such as label or type (message and anchor can be altered without problems), then a new major version number must be used, to preserve database integrity (a new major version number results in the new data to be stored with new, thus different identifiers).
package provide random 1.1 |
package Random; ... BEGIN { ... our $VERSION = 1.1; } |
__version__ = 1.1 |
Module names can be any combination of the following characters (minus the characters not allowed by the specific module language, of course):
All module procedures and data are kept in a specific namespace bearing the module name.
namespace eval random { array set data { ... } proc update {} { ... } } |
our %data; our @data; ... sub update() { ... |
form = {} data = [] ... def update(): ... |
The update function is not needed when the module is asynchronous.
Note: at this time, it is required for Perl and Python modules, as they cannot be asynchronous.
The module configuration defines the data table column headers, help information, ... This data never changes during the lifetime of the application.
namespace eval random { array set data { updates 0 0,label name 0,type ascii 0,message {user name} 1,label cpu 1,type real 1,message {cpu usage in percent} 2,label disk 2,type integer 2,message {disk usage in megabytes} 3,label command 3,type dictionary 3,message {most time consuming command} 3,anchor left pollTimes {10 5 20 30 60 120 300} sort {1 decreasing} indexColumns {0 3} helpText {This is a simple demonstration module ...} } ... } |
our %data; $data{updates} = 0; $data{columns}[0] = { label => 'name', type => 'ascii', message => 'user name' }; $data{columns}[1] = { label => 'cpu', type => 'real', message => 'cpu usage in percent' }; $data{columns}[2] = { label => 'disk', type => 'integer', message => 'disk usage in megabytes' }; $data{columns}[3] = { label => 'command', type => 'dictionary', message => 'command name', anchor => 'left' }; $data{pollTimes} = [10, 5, 20, 30, 60, 120, 300]; $data{sort} = {1 => 'decreasing'}; $data{indexColumns} = [0, 3]; $data{helpText} = 'This is a simple demonstration module ...'; ... |
form = {} form['updates'] = 0 form['columns'] = [ {'label': 'name', 'type': 'ascii', 'message': 'user name'}, {'label': 'cpu', 'type': 'real', 'message': 'cpu usage in percent'}, {'label': 'disk', 'type': 'integer', 'message': 'disk usage in megabytes'}, {'label': 'memory', 'type': 'integer', 'message': 'memory usage in kilobytes'}, {'label': 'command', 'type': 'dictionary', 'message': 'command name', 'anchor': 'left'} ] form['pollTimes'] = [10, 5, 20, 30, 60, 120, 300] form['sort'] = {1: 'decreasing'} form['indexColumns'] = [0, 4] form['helpText'] = 'This is a simple demonstration module ...' ... |
The updates member is a counter used to keep track of the number of times that the module data was updated, and is also used by the core to detect when module data display should be updated (see variable data for more information).
The label members ($data(n,label) in Tcl, $data{columns}[n]{label} in Perl, form['columns'][n]['label'] in Python, with n the column number), define the text to be displayed as column titles. There must be as many label members as they are columns. The titles must contain no ? character.
The type members ($data(n,type) in Tcl, $data{columns}[n]{type} in Perl, form['columns'][n]['type'] in Python) define the type of the corresponding column data. Valid types are simply those that the Tcl lsort command can handle: ascii, dictionary, integer and real, plus the clock type, which accepts any format that the Tcl clock format command can handle (see the trace module for an example). There must be as many type members as they are columns.
The message members ($data(n,message) in Tcl, $data{columns}[n]{message} in Perl, form['columns'][n]['message'] in Python) define the text of the help message to be displayed in a floating yellow window (widget tip, balloon, also see User Interface) as the user moves the mouse pointer over column titles. It can be composed of only a few words or multiple formatted lines. There must be as many message members as they are columns.
The anchor member ($data(n,anchor) in Tcl, $data{columns}[n]{anchor} in Perl, form['columns'][n]['anchor'] in Python) is optional. Column data is either centered by default, tucked to the left or right side of the column. Valid values are center, left or right.
Note that column numbers start at 0. There must be no hole in the column numbers sequence.
The pollTimes member is a list of valid poll times (in seconds) for the module. The list is not ordered, as its first element represents the default poll time value to be used when the moodss application starts. This value may be overridden by a command line argument. The smallest value in the list is used by the core as the lowest possible poll time and checked against when the user enters a new value through the poll time dialog box. The list must not be empty.
Note that the list is also used by moodss as a set of possible choices in the dialog box used to set the new poll time. The user may still directly input any value as long as it is greater than or equal to the minimum value.
If the module is asynchronous (data can be updated at any time and not in response to update procedure invocations (no polling required)), the pollTimes member must be a single negative integer value representing the preferred time interval for viewers that require one (only graphs at this point). For example, if you wish graph viewers to have a display interval of 10 seconds, use:
namespace eval random { array set data { ... pollTimes -10 ... } ... } |
In this case, the graph viewers time range (knowing that they feature 100 time points) would be 1000 seconds. I guess that the value that you should specify as the pollTimes member should be the expected average update interval for your asynchronous data. Note that the graphical viewers x axis always display properly labeled absolute time ticks in any case.
When several asynchronous modules are loaded with no synchronous modules, the interval used for all relevant viewers is the average (in absolute value) of all module intervals. For example, if you load 2 asynchronous modules, one with a pollTimes member of -10 and the other of -20, then a 15 seconds interval value is retained. Note that the interval can be forced through the --poll-time command line argument.
If at least one synchronous module is loaded concurrently with any number of asynchronous modules, the actual application poll time (the one that can be set with the then available poll time dialog box) is used.
The indices member is an optional list that specifies the table columns that should be displayed. If not specified, all the table columns are visible.
The sort entry is optional. It defines the index of the column which should be initially used as a reference for sorting the data table rows, and in which order (increasing or decreasing) the rows should be sorted. The column index for sorting works like the -index Tcl lsort command option, that is rows are sorted so that that specific column appears sorted in the specified order. The specified column must be visible (see indices member documentation above).
The indexColumns list specifies the columns required to uniquely identify a row in the table. In database talk, it represents the table key. To maintain backward compatibility, it is optional and defaults to 0, the leftmost column. The index columns are used when creating data viewer elements: their label is built by concatenating the key value for the cell row with the cell column title. The key value is the concatenation of the index column values for the cell. When specified, all the columns in the list must be visible (see indices member documentation above).
The helpText member specifies a text of any length, to be displayed when the user requests help information on the current module from within the help menu. The text can be HTML formatted or plain. HTML formatted help requires the <HTML> and <BODY> tags to be present, while tables and frames are not supported (and many other tags: stick to formatted text at the moment). The core will render HTML formatted or plain text in the module help window according to the help text contents.
The views member is optional. If specified, it defines one or more views to be used in place of the default view. One table will be displayed per view. For each view, 1 member must be defined: indices, the sort member being optional (syntax and usage are identical to the default table members). A swap optional member may be used if the table data is to be displayed with columns and rows swapped, which is generally the case when the data table has 1 or 2 rows, or a fixed number of rows (see memstats module for an example). The swap value is a boolean and must be either 0 or 1.
namespace eval random { array set data { ... views { {indices {0 1 3 4} sort {1 decreasing} swap 1} {indices {0 2 4} sort {2 decreasing}} } ... } ... } |
$data{views} = [ {indices => [0, 1, 3, 4], sort => {1 => 'decreasing'}, swap => 1}, {indices => [0, 2, 4], sort => {2 => 'decreasing'}} ]; |
form['views'] = [ {'indices': [0, 1, 3, 4], 'sort': {1: 'decreasing'}, 'swap': 1}, {'indices': [0, 2, 4], 'sort': {2: 'decreasing'}} ] |
The switches member is optional. A switch is a single letter or a string prepended with the - or + sign. The boolean value (0 or 1) specifies whether the switch takes an argument. If the switches member exists, an appropriate initialize procedure must be provided by the module (see Initialization). The core will take care of parsing the command line and reject any invalid switch / value combination for the module. The switches value may not be changed in the initialize procedure.
Note: if you wish to pass a sensitive password as an option to the module, use a special option name (-passwd or -password or --passwd or --password) so that the core knows when not to display or store readable characters, for example when entering a password in a dialog box, or storing password value in the moomps daemon data cells history database.
For example, a recent random module configuration is as follows:
array set data { updates 0 0,label name 0,type ascii 0,message {user name} 1,label cpu 1,type real 1,message {cpu usage in percent} 2,label disk 2,type integer 2,message {disk usage in megabytes} 3,label memory 3,type integer 3,message {memory usage in kilobytes} 4,label command 4,type dictionary 4,message {command name} pollTimes {10 5 20 30 60 120 300} sort {1 decreasing} indexColumns {0 4} helpText {...} views { {indices {0 1 3 4} sort {1 decreasing}} {indices {0 2 4} sort {2 decreasing}} } switches {-a 0 --asynchronous 0} } |
our %data; $data{updates} = 0; $data{columns}[0] = {label => 'name', type => 'ascii', message => 'user name'}; $data{columns}[1] = {label => 'cpu', type => 'real', message => 'cpu usage in percent'}; $data{columns}[2] = {label => 'disk', type => 'integer', message => 'disk usage in megabytes'}; $data{columns}[3] = {label => 'memory', type => 'integer', message => 'memory usage in kilobytes'}; $data{columns}[4] = {label => 'command', type => 'dictionary', message => 'command name'}; $data{pollTimes} = [10, 5, 20, 30, 60, 120, 300]; $data{sort} = {1 => 'decreasing'}; $data{indexColumns} = [0, 4]; $data{helpText} = '...'; $data{views} = [ {indices => [0, 1, 3, 4], sort => {1 => 'decreasing'}}, {indices => [0, 2, 4], sort => {2 => 'decreasing'}} ]; $data{switches} = {'-a' => 0, '--asynchronous' => 0}; |
form = {} form['updates'] = 0 form['columns'] = [ {'label': 'name', 'type': 'ascii', 'message': 'user name'}, {'label': 'cpu', 'type': 'real', 'message': 'cpu usage in percent'}, {'label': 'disk', 'type': 'integer', 'message': 'disk usage in megabytes'}, {'label': 'memory', 'type': 'integer', 'message': 'memory usage in kilobytes'}, {'label': 'command', 'type': 'dictionary', 'message': 'command name'} ] form['pollTimes'] = [10, 5, 20, 30, 60, 120, 300] form['sort'] = {1: 'decreasing'} form['indexColumns'] = [0, 4] form['helpText'] = '...' form['views'] = [ {'indices': [0, 1, 3, 4], 'sort': {1: 'decreasing'}}, {'indices': [0, 2, 4], 'sort': {2: 'decreasing'}} ] form['switches'] = {'-a': 0, '--asynchronous': 0} |
The identifier member is optional. It is string that uniquely identifies this module. If set, it is displayed by the core in the initial data tables title area and data cell labels in viewers. This feature can be used for example in modules that gather data from a remote host: in such a case, the identifier could be set to dataType(hostName) (the ps module uses ps(host) string). The allowed character set for the identifier is identical to the allowed set for a module name.
Note that the identifier member is usually set in the module initialize procedure, as it usually depends on the module options.
The resizableColumns is optional. It is a boolean value (0 or 1) which specifies whether displayed data table(s) columns can be manually resized by the user with the mouse. Not available on a per view basis. If not present or set to 0, all the module data columns are automatically sized according to their content.
The initialize procedure, if it exists, is invoked by the core before any update occurs (when the update procedure is invoked if the module is synchronous).
In order for a module to accept command line arguments, the initialize procedure must exist (more information below). Otherwise, if the module can take no arguments, it stays optional. In that case, it is rather redundant with in-line module code (outside of any module function or procedure). When the module takes no arguments, so does the initialize procedure if it exists.
The initialize procedure is mandatory when the module supports command line arguments, and in such a case takes option values as arguments.
Let us use the following command line as example:
$ moodss random --asynchronous --other-option value -x 1234 |
options(--asynchronous) = options(--other-option) = value options(-x) = 1234 |
switches {-a 0 --asynchronous 0 --other-option 1 -x 1} |
$options{--asynchronous} = 1 $options{--other-option} = value $options{-x} = 1234 |
$data{switches} = {'-a' => 0, '--asynchronous' => 0, '--other-option' => 1, '-x' => 1}; |
options['--asynchronous'] = 1 options['--other-option'] = value options['-x'] = 1234 |
form['switches'] =\ {'-a': 0, '--asynchronous': 0, '--other-option': 1, '-x': 1} |
In all cases, data members other than updates and switches can be set or updated in the initialize procedure, and successfully taken into account by the core.
Example for a module that take arguments, where a module identifier is generated according to the -i or --identify command line switches:
proc initialize {optionsName} { upvar $optionsName options variable data if {[info exists options(-i)]||[info exists options(--identify)]} { # generate a random module identifier: set data(identifier) "random [expr {int(rand()*100)}]" } } |
sub initialize(%) { my %option = @_; if ($option{'-i'} || $option{'--identify'}) { # generate a random module identifier: my $identifier = int(rand(100)); $data{identifier} = "random $identifier"; } } |
def initialize(options): global form identify = 0 try: identify = options['--identify'] except: try: identify = options['-i'] except: pass if identify: form['identifier'] = 'randpy ' + str(randint(0, 100)); |
The core waits for the initialize procedure to be completed before initializing the next module. For modules likely to initialize slowly and/or susceptible to initialization failure, it is advised to allow the user interface to be updated in the meantime:
proc initialize {optionsName} { ... set file [open "| /usr/bin/rsh -nl $remote(user) $remote(host) cat /proc/net/arp"] fileevent $file readable {set ::arp::remote(busy) 0} vwait ::arp::remote(busy) ... } |
Similar techniques must be used in such cases within the module update procedure, so that the user interface and eventual modules running concurrently are not prevented to update. The remote capable moodss modules contain various coding techniques achieving that functionality, which I am sure you can improve on.
If a procedure named terminate exists in the module namespace, it is invoked when the module is unloaded dynamically. That procedure must take no arguments. For example:
proc terminate {} { # do some cleanup } |
sub terminate() { # do some cleanup } |
def terminate(): # do some cleanup pass |
The tabular data (variable data) that the module code must update is stored in the data array (same as the module configuration data in Tcl, named after the module configuration data hash in Perl, and after the module configuration data list in Python).
In case of a synchronous module, the core invokes the module update procedure (which obviously must exist) when it is time to refresh the data display (tables and eventually graphical viewers). At this time, the update procedure may update the tabular data straight away (synchronous operation) or launch a request for later data update (asynchronous operation (only available in Tcl)).
In case of an asynchronous module, variable data may be updated at any time. The update procedure may not exist.
For all module types, it actually does not matter when the data is updated. The core will know that fresh data is available when the updates array member is set (actually incremented as it also serves as a counter for the number of updates so far).
It is the module programmer's responsibility to increment this counter right after all tabular data has been updated.
For example, retrieving information for the processes running on a machine is a local operation that can be achieved in a reasonably small amount of time. In such a case, data would be updated immediately and the updates variable incremented at the same time.
But if the data has to be retrieved from across a network, waiting for it to come back would cause a delay that the user would certainly notice, as the application would not respond to mouse or keyboard input during the whole time that it would take to fetch the whole data. In such cases, it is easier to let the update procedure return immediately without setting the updates variable, which would be incremented at a later time, only when the data would become available.
For example, in Tcl, when waiting for data to come across a network connection, the fileevent command could be used on a non blocking channel, where the script to be evaluated when the channel becomes readable would increment the updates array member.
In Perl, this is not yet possible, since the Perl interpreter is dormant outside of the update() function call (a solution to this problem is being studied).
In Python, it is possible to use threads, but at this time, there is no way to pass the information back to the Tcl interpreter outside of the Tcl interpreter driven call of the update function (a solution to this problem is being studied).
proc update {} { variable data array set data " 0,0 john 0,1 1234 0,2 4567 0,3 cc 1,0 william 1,1 8901 1,2 2345 1,3 xedit 2,0 anny 2,1 6789 2,2 0123 2,3 ps 4,0 peter 4,1 4567 4,2 8901 4,3 ls 6,0 laura 6,1 2345 6,2 6789 6,3 emacs 3,0 robert 3,1 1234 3,2 5678 3,3 top " incr data(updates) } |
our @data; ... sub update() { @data = ( ['john', 1234, 4567, 'cc'], ['william', 8901, 2345, 'xedit'], ['anny', 6789, 0123, 'ps'], ['peter', 4567, 8901, 'ls'], ['laura', 2345, 6789, 'emacs'], ['robert', 1234, 5678, 'top'] ); } |
data = [] ... def update(): global data data = [ ['john', 1234, 4567, 'cc'], ['william', 8901, 2345, 'xedit'], ['anny', 6789, 0123, 'ps'], ['peter', 4567, 8901, 'ls'], ['laura', 2345, 6789, 'emacs'], ['robert', 1234, 5678, 'top'] ] |
The column number must start from 0 up to the total number of columns minus 1 (no holes are allowed in the column sequence).
The row number can take any positive integer value (between 0 and 2147483647) and be defined in any order, as long as it is unique during the lifetime of the module data. If a new row is created, it must take a value that was never used: the index of a row that has disappeared cannot be reused. Row numbers need not be consecutive.
When all rows (or only those table cells that have changed) have been updated, the updates member array must be incremented so that the core knows that it can update the table data display.
The Tcl random module source code can be made to function asynchronously: please look into the random.tcl file.
A module is a package in the Tcl sense. When writing a module, you must then provide a pkgIndex.tcl file along with the module code file, placed in the module directory. The pkgIndex.tcl file is very simple, as the following example shows:
package ifneeded random 1.1 "source [file join $dir random.tcl]" |
package ifneeded Random 1.1 "source [file join $dir Random.pm]" |
package ifneeded randpy 1.1 "source [file join $dir randpy.py]" |
Modules can be installed at any valid place that the Tcl core allows (look at the pkg_mkIndex manual page for more information).
When you unpack moodss, you will find the sample modules in sub directories. The current directory (.) is appended to the auto_load global list variable so that sample modules can be found when moodss is run from the unpacking directory.
For example, if you unpacked moodss in /home/joe/moodss-X.x/, you will find the random module package in /home/joe/moodss-X.x/random/ so that the following will work:
$ cd /home/joe/moodss-X.x/ $ wish moodss random |
You can install your new modules in the default location: /usr/local/lib/ on UNIX. For example, if you move the files in /home/joe/moodss-X.x/random/ to /usr/local/lib/random/, moodss will still be able to find the random module (again, look at the pkg_mkIndex manual page for more information).
Please take a look at the INSTALL file for the latest information on how to install the moodss application itself.
You may want to inform the user of the module activity. You may use the message area (called the messenger, across the bottom of the application main window) through the following API:
pushMessage "error: message..." popMessage flashMessage "warning: message..." numberOfSeconds |
Your message can be any kind of string (1 line only: it should fit in a reasonably wide main window), and numberOfSeconds being optional and defaulting to 1. Note that messages sent to the message area are also displayed in the trace module table(s) if it(they) exist(s) (see below). In such cases, popping messages has no effect on trace module tables.
One should use the message area facilities with discretion, as it is already used by the application core for informing the user of modules loading, initialization, updates, context sensitive help, ... My advice is to use it for important messages or errors pertinent to the module itself.
The module name should not appear at the beginning of the messages as it is automatically prepended internally (see pushMessage above).
I suggest using the importance level (as in thresholds) as the header. Possible values are, in increasing importance order: debug, info, notice, warning, error, critical, alert, emergency. Look for examples in included modules.
You also have the option of using trace tables (also see trace module) through the following API:
traceMessage "critical: message..." |
The difference is that messages from the module are displayed in the trace module tables if they exist (see trace, there can be more than 1 trace module loaded), remain visible to the user for a longer period of time, and can be multi-line.
Finally, displaying separate (toplevel) message windows using Tk is of course always possible (if you load Tk in the module). Keep in mind that the core, not being aware of the module event that has taken place, will continue to invoke the module update procedure (unless the module is asynchronous, of course). In such a case, you may want to use an internal (to the module) busy flag so that the update procedure immediately returns until the user acknowledges the informational message. Other strategies are of course possible (let me know if you have one that you think should appear here as an example).
In order to allow clean error handling with module loading either by command line arguments or dynamically while the application is running, the following rules need be followed:
In the first 2 cases, specifying the module name in the error message is not necessary as the core handles it.
Moodss modules can also be written in the Perl language.
An embedded Perl interpreter is used for each loaded module, thus achieving complete independence between modules.
The tclperl library is required (available on my homepage).
Complete documentation for programming Perl modules can be found in the module development section, and as comments inside the Random.pm module source file.
Moodss modules can also be written in the Python language.
An embedded Python interpreter is used for each loaded module, thus achieving complete independence between modules.
The tclpython library (version 3.0 or above, for Python 2.2 or above) is required (available on my homepage as source and Redhat rpms).
Complete documentation for programming Python modules can be found in the module development section, and as comments inside the randpy.py module source file.
When data to be displayed is an array of unrelated values, the only solution is to organize the data in a table with a single row and 1 column per value (which does not prevent the use of several views for displaying the data).
In such a case, no index column is required and as a matter of fact rather gets in the way. The trick is to use an empty column 0, use as index by the core by default, and to prevent it from being displayed by using 1 or more views.
Please look at the cpustats module code for a working example.
In the module data array, you cannot leave or set a numeric cell empty when no data is available. Use the ? character (also used in statistics tables) instead.
A: Here is the road map from start to finish:
A: Those tables hold the module data and therefore can only be erased by unloading the corresponding module. They are easily recognizable since they are the only ones with a window bar including the module name.
A: What the module does when loading is really dependent on the module implementation itself. It should be described in the module documentation (accessible through the Help menu). A message appears in the moodss main window message area when each module is loading. Note that this message can be fugitive even if the module initialization takes a while and the module does it in the background (as it should so that other modules get a chance to also initialize).
If there are any errors, they will be reported in the message area. I suggest opening the trace window or loading the trace module is you want to precisely keep track of those eventual errors.
A: There are several ways:
A: Move it (as usual, using the internal window manager handles) into another page tab (wait until you see the drop black outline around the tab label to release the mouse button). The viewer can then be found in the upper left corner of the target page.
Please look at the TODO file (at least present in the source distribution).
I welcome any suggestion for new features that you may need in your specific use of moodss.
For downloading Tcl software (such as stooop, scwoop, tkpiechart, ...), many Redhat rpm packages, visit my home page at http://jfontain.free.fr/.
Please report bugs and comments to comp.lang.tcl or jfontain@free.fr.