Skip to main content

Monitor Extension Development

Monitors implement the interface that allows GURDARA to communicate with external processes that monitor the status of the test target. One Monitor may allow GURDARA to obtain information from target processes during a crash such as the register values, stack dump, and disassembly of the faulty code; another may provide the means to alert in case a specific message appears in the log files of the target service.

Monitor Properties

Just like with other extensions, the properties.json file of the Monitor describes the accepted or expected properties of the Monitor. The properties file is a dictionary, where each key represents the name of a property. The value of each parameter key is an object that describes the property.

The most basic properties file that only the mandatory name property can be seen below.

{
"name": {
"type": "str",
"mandatory": true,
“display”: {
"display_name": "Monitor Name",
"description": "The name of the monitor",
"visible": false
}
}
}

The name property is a special one, and each Monitor must define it in the properties file. The value is not rendered on the user interface (due to visible the attribute set to false). However, Engines must know which monitor to use for a given test; thus the name of the monitor is defined using this property. The user interface populates this property automatically.

Another example: If a Monitor would like to allow users to enable debug mode, the properties file could be extended, as shown below.

{
"name": {
"type": "str",
"mandatory": true,
“display”: {
"display_name": "Monitor Name",
"description": "The name of the monitor",
"visible": false
}
},
"debug": {
"type": "bool",
"default": false,
“display”: {
"display_name": "Debug Mode",
"description": "Enable to see debug messages on console"
}
}
}

The supported options of the properties are summarized by the table below.

PropertyDescription
typeThe type of the property. The value of the type property can be either a string representing a Python type or a list of strings representing multiple Python types. The supported Python types are: str, int and bool.
mandatoryDefines whether it is required to define the property.
defaultThe default value of the property. Non-mandatory properties must have a default value set.
display.conditionTo be used to define a condition to be evaluated in order to determine whether the property should be rendered or not. Conditions can refer to the actual value of properties and compare it to a specific value, for example: protocol == "tls" and value <= 1. In the first example when the Project Configuration Page renders the driver properties, it will query the current value of the protocol field and, if its value is tls, only then the property will be rendered. In the second example, the property will be only rendered if the value property’s current value is less than 1. Please note, setting multiple conditions for a single property is not supported in any form at the moment.
display.display_nameThe short name of the property, as shown on the user interface.
display.descriptionThe description of the property, as shown on the user interface.
display.multilineApplicable to text fields only. Setting it to true will result in the render of a multiline text area.
display.visibleThe value of this property only has an impact on how the user interface renders the property. In case its value is false, no form control will be rendered for the property.
display.hideSetting to true will prevent the property from being displayed.

Even though the core monitor module implements a Python thread (threading.Thread), it also acts similarly to a Python dictionary (dict); thus, each method of the Monitor, including __init__ can access the properties the monitor has received via self.get("property_name_here"). However, please note this is only possible after calling the init method of the core monitor class.

Monitor Methods

All methods are documented in the Monitor source files generated by the SDK.

Example

You can find Monitor examples on GitLab.

A generic example example below implements a Monitor that demonstrates how to signal the Engine that:

  • An issue was detected
  • The target has been restarted and testing can continue
import copy
import time
from guardara.sdk.monitor.monitor import MonitorInterface

class monitor(MonitorInterface):

def __init__(self, properties, request_queue, response_queue):
MonitorInterface.__init__(self, properties, request_queue, response_queue)
# Set a variable to hold status information. If an issue is found, it's
# value will be set to a string that describes the nature of the issue.
self.status = None
# A variable that signals whether the target under test is ready to be
# tested.
self.ready = False

def do_start(self):
"""
Simulate successful monitor start by returning `True`. We also set
`self.target` to the current timestamp. We will use this to simulate
events such as:
- An issue was detected
- The target has been restarted
"""
print("[DEBUG] MONITOR: START")
self.target_time = time.time()
return True

def do_stop(self):
print("[DEBUG] MONITOR: STOP")
return True

def do_status(self):
"""
Return the current status. If `None` is returned, the Engine will
know no issue has been detected.
If we return `True`, the engine will know it has to wait until the
monitor collects all report data.
If we return a string, the Engine will handle it as being the
report data.

It is critical that once the method returned the report data,
subsequent calls of the method should return `None`.
"""
status = copy.deepcopy(self.status)
if isinstance(self.status, str):
self.status = None
return status

def do_is_ready(self):
return self.ready

def do_shutdown(self):
print("[DEBUG] MONITOR: SHUTDOWN")

def custom_loop(self):
"""
After:
- 5 seconds from test start/resume:
We signal the Engine immediately that an issue was found. By
returning `True` via `do_status` we not only signal but we
indicate that the Engine has to wait until the monitor collects
all the data needed for the report.
- 10 seconds from test start/resume:
We return the report data via the `do_status` method letting
the Engine now the report data has been collected.
- 2 seconds after providing the report data to the engine we
simulate a target restart and signal to the engine that the
target is ready.
"""
if time.time() > self.target_time + 5:
self.status = True
self.ready = False
if time.time() > self.target_time + 10:
self.status = "Report data here"
if time.time() > self.target_time + 12:
self.status = None
self.ready = True
self.target_time = time.time() + 10