Packet Serial Parser
Serial console parsing framework provide ability to detect certain logs appearance on the console and report it back to the test. Example of such logs may be “Kernel panic”, “Something crashed”, “U-Boot bla-bla”. The test (if exists at the moment of the event) will be notified ASAP and can take appropriate actions. Examples of actions may include diagnostics gathering, some smart logging, DUT reboot and not touching the DUT. Also it is possible just save serial console output into the TE logs.
How to launch
To use the framework of serial console parsing make sure that the two following steps done.
Build options
To compile the agents with support of serial console parsing framework should be added option –with-serialparse to agent type options:
TE_TA_TYPE([linux], [], [unix], [--with-serialparse], [], [], [], [])
Configurator objects
The TE has auxiliary configuration file cs.conf.serial with configurator objects for the serial parser management. The file should be included to the config:
<xi:include href="cs.conf.serial" parse="xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
To use event handlers the instance /local: should be added and value of the instance /local:/tester:/enable: should be set to 1. The last to launch the thread on the Tester to handle events.
<?xml version="1.0"?> <history> <add> <instance oid="/local:"/> </add> <set> <instance oid="/local:/tester:/enable:" value="1"/> </set> </history>
Simple use cases
There are two ways to configure and manage the serial console parsers: using the configurator and using the Test API. See the examples below for the both methods.
Logging of the serial console
By default the logging is enabled for all active parsers. So to launch the logging should be added and enabled a parser instance. Level of the logs by default is WARN.
Configurator
To logging serial console messages to the main log, insert the following code to the configuration file. The code launches a new thread on the agent Agt_A. The thread establishes connection with a serial console named console_name by port 3105 and gathers messages from the console. my_parser is name of the parser.
<add> <instance oid="/agent:Agt_A/parser:my_parser" value="console_name"/> </add> <set> <instance oid="/agent:Agt_A/parser:my_parser/port:" value="3105"/> <instance oid="/agent:Agt_A/parser:my_parser/enable:" value="1"/> </set>
User can enable, disable or set of message level value of the logging:
<set> <instance oid="/agent:Agt_A/parser:my_parser/logging:/level:" value="RING"/> <instance oid="/agent:Agt_A/parser:my_parser/logging:" value="1"/> </set>
There is the variable logging sets to 1, it is to enable logging (0 - disable). The the log level sets to RING.
Test API
The second way to launch logging of the serial console is a using of the Test API. See the following code example to launch the same parser as before, but from a test :
#include "tapi_serial_parse.h" tapi_parser_id *pars; pars = tapi_serial_id_init("Agt_A", "console_name", "my_parser"); tapi_serial_parser_add(pars); tapi_serial_logging_enable(pars, "WARN"); tapi_serial_parser_del(pars); tapi_serial_id_cleanup(pars);
By default port for connection to the conserver is 3109. User can change any connection configurations in the parser id structure. For more detailed description see the Test API section.
RCF
RCF API also can be used to launch logging thread. In this case the thread will be launched without any participation of Configurator. See the following example.
<?xml version="1.0"?> <rcf> <ta name="Agt_A" type="linux" fake="no" synch_time="no" rebootable="no" rcflib="rcfunix"> <conf name="host">bilbo</conf> <conf name="port">18001</conf> <conf name="sudo"/> <thread name="serial_console_log" when="rcfuser"> <arg value="rcfuser"/> <arg value="WARN"/> <arg value="100"/> <arg value="3105:user:console_name"/> </thread> </ta> </rcf>
Event handling
To handle any events, one or more event and handler instances should be added on the Tester subtree. For detailed description of handlers see the Tester section. The value of the handler instance is a path to the executable (binary or shell command). To detect events to agent should be added parser instance. The parser should contain event instance. Use a name of one of the events declared in the Tester subtree as value of the agent event instance, in the examples it is my_event value. Also should be added pattern instances. Patterns used to detect events. An event happens if a serial log message matches at least one of the patterns, associated with the event. Write a pattern to the value of the pattern instance.
Configurator
There is example how to add a parser with events via configuration file:
<?xml version="1.0"?> <history> <add> <instance oid="/local:"/> <instance oid="/local:/tester:/event:my_event"/> <instance oid="/local:/tester:/event:my_event/handler:external_handler" value="/home/andrey/work/trunk/handlers/new_handler"/> <instance oid="/local:/tester:/event:my_event/handler:internal_handler"/> <instance oid="/agent:Agt_A/parser:my_parser" value="console_name"/> <instance oid="/agent:Agt_A/parser:my_parser/event:ag_ev" value="my_event"/> <instance oid="/agent:Agt_A/parser:my_parser/event:ag_ev/pattern:1" value="fooo"/> <instance oid="/agent:Agt_A/parser:my_parser/event:ag_ev/pattern:2" value="bar"/> </add> <set> <instance oid="/local:/tester:/enable:" value="1"/> <instance oid="/local:/tester:/event:my_event/handler:internal_handler/internal:" value="1"/> <instance oid="/local:/tester:/event:my_event/handler:internal_handler/signal:" value="SIGINT"/> <instance oid="/agent:Agt_A/parser:my_parser/port:" value="3105"/> <instance oid="/agent:Agt_A/parser:my_parser/enable:" value="1"/> </set> </history>
The instance name of pattern object is index number, should be used only integer numbers greater than zero.
Test API
The second way to handle events is using the Test API. See the following code example to launch the same parser as before, but from a test:
#include "tapi_serial_parse.h" ... tapi_parser_id *pars; ... pars = tapi_serial_id_init("Agt_A", "console_name", "my_parser"); pars->port = 3105; tapi_serial_tester_event_add("my_event"); tapi_serial_handler_ext_add("my_event", "external_handler", 0, "/home/andrey/work/trunk/handlers/new_handler"); tapi_serial_handler_int_add("my_event", "internal_handler", 0, SIGINT); tapi_serial_parser_add(pars); tapi_serial_parser_event_add(pars, "ag_ev", "my_event"); tapi_serial_parser_pattern_add(pars, "ag_ev", 1, "fooo"); tapi_serial_parser_pattern_add(pars, "ag_ev", 2, "bar"); tapi_serial_parser_del(pars); tapi_serial_tester_event_del("my_event"); tapi_serial_id_cleanup(pars);
Architecture of the framework
Agent
To listen serial console, perform the logging of the received data, to parse data for events a new thread starts on the agent. The serial console parser thread is responsible for logs gathering. The Configurator performs configure and launch it. It listens for the data from the conserver running on the host and receives arbitrary amount of data. The thread parses a data and searches one of events. When event is detected, the thread changes the event state in the appropriate instance. Optional, the thread can to print data from the serial console in the main log.
Tester
The Tester has a separate thread too. It should periodically poll status of the parsers events from the Configurator, when an event occurs the thread calls an appropriate handler or a series of handlers.
Handlers
When an event has been detect, the Tester calls a sequence of handlers. A handler can be internal or external, this indicates the /local/tester/event/handler/internal instance. The handler subtree has priority field, that is used to control of the handlers sequence. The higher priority - the sooner the handler performed.
If a handler is internal and an event occurred, then the Tester should send a signal specified in the handler subtree. The event will be processed by test itself.
An external handler is an executable program. It can to perform any actions to handle the event, but it must return one of the following values:
0 : continue handlers execution
1 : stop handlers execution
2 : stop both handlers and test execution
3 : stop handlers execution, kill the test and stop tests sequence execution.
The end user, specifying full path, can use any handler programs or scripts, that are located anywhere. But if a short name is used, the handler is searched in the default handlers directory.
By default the handlers directory is RUN_DIR/handlers. The directory of handlers can be changed by user.
Configuration
Configurator
Two configuration subtrees provided to manage the serial parser framework. The first subtree is /local/tester, it is used to declare Tester events. There is also performed binding of some handlers to the events.
OID |
Type |
Description |
---|---|---|
/local/tester |
RW none |
Tester subtree. |
/local/tester/enable |
RW int |
Enable the Tester thread to listen for events. By default: disabled. |
/local/tester/period |
RW int |
Period for polling of events status. By default: 100 milliseconds. |
/local/tester/location |
RW str |
Directory with handlers for short named handlers. By default: RUN_DIR/handlers. |
/local/tester/event |
RC none |
Subtree of a Tester event. |
/local/tester/event/handler |
RC str |
Event handler. Use a path to the executable as a value. |
/local/tester/event/handler/priority |
RW int |
Handler priority. By default: 0. |
/local/tester/event/handler/signal |
RW int |
Signal number. By default: SIGINT. The list of supported signals: SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGKILL, SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, SIGSTKFLT. The value ‘none’ means that the signal will not be sent. |
/local/tester/event/handler/internal |
RW int |
Indicates that it is an internal handler. By default: external. |
A new subtree of the agent is /agent/parser. It is used to configure and launch a parser thread for a serial consoles on the agent.
OID |
Type |
Description |
---|---|---|
/agent/parser |
RC str |
The parser subtree. Use a name of serial console as value. |
/agent/parser/port |
RW int |
Conserver port corresponds to the parameters of conserver launch. By default: 3109. |
/agent/parser/user |
RW str |
Conserver user name. By default: tester. |
/agent/parser/enable |
RW int |
Start/stop the parser thread. |
/agent/parser/interval |
RW int |
Intreval of polling messages from the console. |
/agent/parser/reset |
RW int |
Reset status for all events. |
/agent/parser/event |
RC str |
Declared in the Tester subtree as a value. |
/agent/parser/event/pattern |
RC str |
Pattern string. |
/agent/parser/event/counter |
RW int |
Counter of the happened event. |
/agent/parser/event/status |
RW int |
Status of the event. |
/agent/parser/logging |
RW int |
Enable logging of the console messages in the main log. By default: enabled. |
/agent/parser/logging/level |
RW str |
Level of the message for logging. By default: WARN. |
Configuration exmaple:
<?xml version="1.0"?> <history> <add> <instance oid="/local:"/> <instance oid="/local:/tester:/event:new_event"/> <instance oid="/local:/tester:/event:new_event/handler:1" value="~/work/trunk/handlers/myserialhandler"/> <instance oid="/local:/tester:/event:new_event/handler:2" value="ext_handler_short_name"/> <instance oid="/local:/tester:/event:new_event/handler:3"/> <instance oid="/agent:Agt_A/parser:new_parser" value="console_name"/> <instance oid="/agent:Agt_A/parser:new_parser/event:ag_ev" value="new_event"/> <instance oid="/agent:Agt_A/parser:new_parser/event:ag_ev/pattern:1" value="fooo"/> <instance oid="/agent:Agt_A/parser:new_parser/event:ag_ev/pattern:2" value="bar"/> </add> <set> <instance oid="/local:/tester:/enable:" value="1"/> <instance oid="/local:/tester:/period:" value="200"/> <instance oid="/local:/tester:/location:" value="/home/andrey/work/trunk/handlers"/> <instance oid="/local:/tester:/event:new_event/handler:1/priority:" value="3"/> <instance oid="/local:/tester:/event:new_event/handler:2/priority:" value="2"/> <instance oid="/local:/tester:/event:new_event/handler:3/priority:" value="1"/> <instance oid="/local:/tester:/event:new_event/handler:3/internal:" value="1"/> <instance oid="/local:/tester:/event:new_event/handler:3/signal:" value="SIGINT"/> <instance oid="/agent:Agt_A/parser:new_parser/port:" value="3105"/> <instance oid="/agent:Agt_A/parser:new_parser/user:" value="tester"/> <instance oid="/agent:Agt_A/parser:new_parser/interval:" value="100"/> <instance oid="/agent:Agt_A/parser:new_parser/logging:" value="1"/> <instance oid="/agent:Agt_A/parser:new_parser/logging:/level:" value="WARN"/> <instance oid="/agent:Agt_A/parser:new_parser/enable:" value="1"/> </set> </history>
Test API
The Test API provides the possibilities to add, delete, configure and manage the parser threads and event handlers. The complete list and detailed description functions of the Test API you can find here lib/tapi/tapi_serial_parse.h
.
Test API uses a tapi_parser_id structure to configure and control a parser:
typedef struct tapi_parser_id { const char *ta; /**< Test agent name */ const char *name; /**< The parser name */ const char *c_name; /**< Serial console name */ const char *user; /**< A user name for the conserver or NULL. In case if NULL, used default value 'tester'. */ int port; /**< Port of the console */ int interval; /**< Interval to poll data from the conserver. Use -1 for default value. */ } tapi_parser_id;
It is possible to initialize the parser id structure using the TAPI. In this case the structure should be cleaned after use, because TAPI function allocates memory. Use the tapi_serial_id_cleanup() to cleanup the id structure. By default the user field value is NULL, it means in logs will use the default user - tester. Default port is 3109, interval - 100 milliseconds. See the following example.
#include "tapi_serial_parse.h" tapi_parser_id *pars; /* Initialize identifier*/ pars = tapi_serial_id_init("Agt_B", "console_name", "my_parser"); /* Cleanup identifier */ tapi_serial_id_cleanup(pars);
Test API using example:
#include "tapi_serial_parse.h" tapi_parser_id pars; int pat_i; pars.ta = "Agt_A"; pars.name = "new_parser"; pars.c_name = "console_name"; pars.user = NULL; pars.port = 3109; pars.interval = -1; /* value -1 is used to set default value of the polling period */ /* Add and initialize parser elements */ tapi_serial_tester_event_add("new_event"); tapi_serial_handler_ext_add("new_event", "ext_h", 5, "ext_handler_short_name"); tapi_serial_handler_int_add("new_event", "int_h", 4, SIGINT); tapi_serial_parser_add(&pars); tapi_serial_parser_disable(&pars); tapi_serial_parser_enable(&pars); tapi_serial_logging_enable(&pars, NULL); tapi_serial_parser_event_add(&pars, "tapi_event", "new_event"); pat_i = tapi_serial_parser_pattern_add(&pars, "tapi_event", "Segmentation fault"); /* Remove the initialized elements */ tapi_serial_parser_pattern_del(&pars, "tapi_event", pat_i); tapi_serial_parser_event_del(&pars, "tapi_event"); tapi_serial_logging_disable(&pars); tapi_serial_parser_del(&pars); tapi_serial_handler_del("new_event", "ext_h"); tapi_serial_handler_del("new_event", "int_h"); tapi_serial_tester_event_del("new_event");