The WatchCub Framework consists of several parts interacting with each other.
Config Parser
The config parser (CP) is responsible for performing the following:
-
parsing a config file
-
syntax checking
CP is expected to build a tree-like structure, describing the parameters/values concerning each test in config file. Parameters/values are figured out directly or through inheritance (see
Configuration).
Dependancy Graph
The dependancy graph is build upon dependancies among different tests. In every moment it maintains a queue of test ready for execution. See dependancy graph alogorithms for more details.
Test Dispatcher
This component of the framework is responsible for building the dependancy graph and then provide a thread-safe method GetTest to plugin instances which they use to obatain a test for execution. The logic of test dispatching is hidden in the component, so on later stage some optimizations/load balancings are possible at this stage of framework functioning.
Test Result Manager
This compoenent handles all the database work + guarantees the results from test executions are stored properly. It provides a thread-safe method ReturnResult to all the plugin instances which they use to return the result from some test.
This method stores results in buffer in order not to slow down the calling threads. Once per minute results are flushed to database this being done in some different thread run espcecially for the Result Manager. If no database server is available, Result Manager stores the SQL queries in some text file. When next minute the flush is run again, the stored queries are flushed, in case the DB server is up already.
Apart from that Result Manager provides a method SynchronizeTestWithDB, which takes a Test object and makes sure it is properly described in database and then sets its ID according to what's in DB. This is done in order to have full information what test has been run, when happened that and what results it got.
Dynamic Plugin Loader
This component is responsible for loading .so modules in which the plugins live and constructing plugin instances. It is a wrapper of the Linux dynamic library interfaces (dlopen, dlsym, etc...). Plugin loading is done once per plugin. After loading into process address space a handle to the plugin module is stored and the symbol get_plugin_instance is resolved. Every plugin module should provide this interface. The Dynamic Plugin Loader again stores a pointer to this function and later on every DynamicPluginLoader::GetInstance( some_plugin ) returns a plugin instance constructed through this function.
Tester Threads
These are threads initialized with plugin instances, and then provided with Test Dispatcher and Test Result Manager objects. From the moment these threads are being Run() they use the TestDispatcher::GetTest to get tests for execution and TestResultManager::ReturnResult to return results from their execution.
Tester threads are responsible to perform as much tests as they can/are configured to. Thus a test which can perform asynchronously multiple tests may obtain multiple tests from the Test Dispatcher. Asynchronous tests are executed with the finite state machine type plugins.
The framework can be configured to launch multiple instances of a plugin. This means that the plugins must be reentrant and provide some locking mechanisms over tests in order not to execute a single tests multiple times by different tester threads.
Basic Framework Algorithm
Framework Initialization
-
Use CP to parse configuration file
-
Build Test objects based upon the tree-like structure returned from the CP
-
Load plugin modules (through DynamicPluginLoader)
- Construct the TestDispatcher and TestResultManager objects.
- Construct and inialize the Cub Threads with plugins instances initialized with TestDispatcher + TestResultManager objects
- Run the Cub Threads.