Additional Servlets and monitoring for Selenium Grid 3
The servlet com.xing.qa.selenium.grid.hub.Console
implements an endpoint that returns the information given by the
regular console as JSON (and adds some extra infos).
java -cp selenium-standalone<version>.jar:selenium-api.jar org.openqa.grid.selenium.GridLauncherV3 \
-servlets com.xing.qa.selenium.grid.hub.Console -role hub
For windows users:
java -cp selenium-standalone<version>.jar;selenium-api.jar org.openqa.grid.selenium.GridLauncherV3 \
-servlets com.xing.qa.selenium.grid.hub.Console -role hub
This will add new URL endpoint to the selenium grid hub: http://localhost:4444/grid/admin/Console/*
Supported HTTP methods: GET
Returned content type: application/json
A call to http://localhost:4444/grid/admin/Console will return a full status report on:
- version and status of the hub server
- list of nodes and their installed selenium version and available/utilized browsers
The call http://localhost:4444/grid/admin/Console/requests
will just return a list of the pending requests of the connected nodes.
The call http://localhost:4444/grid/admin/Console/simple
will just return a map of pending request count, free browsers count, and in-use browsers count, for each browser name.
The servlet com.xing.qa.selenium.grid.hub.ForceDeregister
implements an endpoint to forcibly deregister a given node from the Hub's GridRegistry. This immediately cleans up an test sessions that were using the node and then removes it from the Grid.
This can be used as a workaround for SeleniumHQ/selenium#8055 where when a node disappears from the network (such as an EC2 Instance in an Auto-Scaling Group being terminated), the Console and hub status endpoint hang for seconds or minutes while attempting to contact the node.
Include the servlet in the -servlets
parameter as shown above.
This will add a new URL endpoint to the hub, http://HUB_ADDRESS:HUB_PORT/grid/admin/ForceDeregister
Supported HTTP methods: GET
Returned content type: text/plain
(use HTTP status codes to determine result)
A call to http://HUB_ADDRESS:HUB_PORT/grid/admin/ForceDeregister?id=NODE-ID will immediately deregister the node with id NODE-ID
.
The ID of nodes can be determined either from the stock Grid Console or from the Console servlet. It is generally the URL to reach the node, in the form http://hostname_or_ip:PORT
When a node is not started with the default class DefaultRemoteProxy
but with -proxy com.xing.qa.selenium.grid.node.MonitoringWebProxy
the hub will report metrics on the operating system and node operation to a InfluxDB server.
The Configuration of the reporting destination is done by environment variables:
Variable | Default | Description |
---|---|---|
IFXDB_HOST |
localhost |
InfluxDB server hostname |
IFXDB_PORT |
8086 |
InfluxDB server port |
IFXDB_DB |
selenium-grid |
Name of database for the collected data |
IFXDB_USER |
root |
InfluxDB username |
IFXDB_PASSWD |
root |
InfluxDB password |
The MonitoringWebProxy reports the following series and values to InfluxDB:
Serie | Content |
---|---|
node.utilization.measure |
measures the utilization of available selenium sessions |
node.errors |
reports session errors |
session.cap.provided.finish.measure |
tracking of provided capabilities at end of session |
session.cap.provided.start.measure |
tracking of provided capabilities at selenium session start event |
session.cap.provided.timeout.measure |
tracking of provided capabilities at session timeout event data |
session.cap.requested.finish.measure |
tracking of requested capabilities at end of session |
session.cap.requested.start.measure |
tracking of requested capabilities at selenium session start event |
session.cap.requested.timeout.measure |
tracking of requested capabilities at session timeout event data |
session.cmd.command.measure |
tracking of sent commands |
session.cmd.result.measure |
tracking of command results |
session.event.measure |
measuring of selenium events (start, finish, timeout) |
Field | Type | Content |
---|---|---|
host |
String |
hostname of remote node |
used |
int |
number of used slots at sampling time |
total |
int |
number of total available slots |
normalized |
float |
normalized usage (used/total) {0..1} |
Field | Type | Content |
---|---|---|
host |
String |
hostname of remote node |
error |
String |
error class name |
message |
String |
error message |
Field | Type | Content |
---|---|---|
host |
String |
hostname of remote node |
ext_key |
String |
external key of session |
int_key |
String |
internal key of sesson |
inactivity |
long |
milliseconds of inavctivity |
forwarding |
boolean |
true, if forwarding request |
orphaned |
boolean |
true, if orphaned session |
capability |
String |
name of capability |
val |
any |
value of capability (String, numerical or boolean) |
Field | Type | Content |
---|---|---|
host |
String |
hostname of remote node |
ext_key |
String |
external key of session |
int_key |
String |
internal key of sesson |
inactivity |
long |
milliseconds of inavctivity |
forwarding |
boolean |
true, if forwarding request |
orphaned |
boolean |
true, if orphaned session |
capability |
String |
name of capability |
val |
any |
value of capability (String, numerical or boolean) |
Field | Type | Content |
---|---|---|
host |
String |
hostname of remote node |
ext_key |
String |
external key of session |
int_key |
String |
internal key of sesson |
inactivity |
long |
milliseconds of inavctivity |
forwarding |
boolean |
true, if forwarding request |
orphaned |
boolean |
true, if orphaned session |
capability |
String |
name of capability |
val |
any |
value of capability (String, numerical or boolean) |
Field | Type | Content |
---|---|---|
host |
String |
hostname of remote node |
ext_key |
String |
external key of session |
int_key |
String |
internal key of sesson |
inactivity |
long |
milliseconds of inavctivity |
forwarding |
boolean |
true, if forwarding request |
orphaned |
boolean |
true, if orphaned session |
capability |
String |
name of capability |
val |
any |
value of capability (String, numerical or boolean) |
Field | Type | Content |
---|---|---|
host |
String |
hostname of remote node |
ext_key |
String |
external key of session |
int_key |
String |
internal key of sesson |
inactivity |
long |
milliseconds of inavctivity |
forwarding |
boolean |
true, if forwarding request |
orphaned |
boolean |
true, if orphaned session |
capability |
String |
name of capability |
val |
any |
value of capability (String, numerical or boolean) |
Field | Type | Content |
---|---|---|
host |
String |
hostname of remote node |
ext_key |
String |
external key of session |
int_key |
String |
internal key of sesson |
inactivity |
long |
milliseconds of inavctivity |
forwarding |
boolean |
true, if forwarding request |
orphaned |
boolean |
true, if orphaned session |
capability |
String |
name of capability |
val |
any |
value of capability (String, numerical or boolean) |
Field | Type | Content |
---|---|---|
host |
String |
hostname of remote node |
ext_key |
String |
external key of session |
int_key |
String |
internal key of sesson |
inactivity |
long |
milliseconds of inavctivity |
forwarding |
boolean |
true, if forwarding request |
orphaned |
boolean |
true, if orphaned session |
cmd_method |
String |
HTTP method of command |
cmd_action |
any |
Command URL called on remote |
cmd |
String |
Request body of command |
Field | Type | Content |
---|---|---|
host |
String |
hostname of remote node |
ext_key |
String |
external key of session |
int_key |
String |
internal key of sesson |
inactivity |
long |
milliseconds of inavctivity |
forwarding |
boolean |
true, if forwarding request |
orphaned |
boolean |
true, if orphaned session |
cmd_method |
String |
HTTP method of command |
cmd_action |
any |
Command URL called on remote |
cmd |
String |
Request body of command |
Field | Type | Content |
---|---|---|
host |
String |
hostname of remote node |
ext_key |
String |
external key of session |
int_key |
String |
internal key of sesson |
inactivity |
long |
milliseconds of inavctivity |
forwarding |
boolean |
true, if forwarding request |
orphaned |
boolean |
true, if orphaned session |
type |
String |
type of session event (start, finish, timeout) |
The class com.xing.qa.selenium.grid.hub.Prioritizer
implements a Session prioritizer for the Selenium hub that
assigns a higher priority to tasks coming from an CI server (see Custom Capabilities for details)
Add the following property to your JSON hub configuration file:
{
"prioritizer": "com.xing.qa.selenium.grid.hub.Prioritizer",
}
### Capability Matcher
The class `com.xing.qa.selenium.grid.hub.ConfigurableCapabilityMatcher` implements a CapabilityMatcher that (w/o further configuration)
mimicks the behaviour of the default capability matcher with a few notable exceptions:
1. Custom capability matchers can be added and reused for different capabilities.
2. The version matcher for comparing requested browser versions is more versatile than the exact match offered by the
default capability matcher.
#### Using the new capability matcher
Add a property capability matcher to your JSON config for configuring the hub:
```JSON
{
"capabilityMatcher": "com.xing.qa.selenium.grid.hub.ConfigurableCapabilityMatcher",
}
the capability matcher is configured via a env variable, SELENIUM_MATCHERS
that has the following structure:
SELENIUM_MATCHERS=<capability>:<matcher name>[,<capability>:<matcher name>]...
If no configuration is given, the default mapping is used:
SELENIUM_MATCHERS=platform:platform,browserName:exact,version:rvm
Any other capabilities may be specified but will not be evaluated by the CapabilityMatcher. Capabilities starting with
an underscore (_
) are considered to be grid-internal properties and will be generally exempt from matching.
A specified value of ""
, " "
, "*"
or anything that translates to null
will be considered as ANY
value.
| Name | Description | +----------+-----------------------------------------------------------------------+ | exact | Matches if required and provided capability match exactly. | | platform | Same behaviour as the default matcher wrt. platform names | | rvm | "Ruby Version Matcher" that compares versions like rubygems does this |
The exact matcher has no further configuration.
The platform matcher has no further configuration. It tries to resolve any version passed in to a valid platform and tries to match this against the provided capabilities of a node.
Version specification in the requested capabilities.
| spec | meaning |
+---------+------------------------------------------------------------------------+
| x.y
| Version has to match x.y exactly |
| ~>x.y
| Version matches x.y
, x.y.z
excluding version <x.y
and >(x+1).y
|
| >x.y
| Version matches any version greater than x.y (e.g. x.(y+1)
|
| <x.y
| Version matches any version less than x.y (e.g. x.(y-1)
|
| >=x.y
| Version matches any version greater than x.y (e.g. x.(y+1)
|
| <=x.y
| Version matches any version greater than x.y (e.g. (x-1).(y+1)
|
Any Part of a version that can not be converted to a numerical value will be compared as exact string match (e.g. x.y.beta
).
To implement additional capability matchers you can either extend this project or keep the capability matchers in your own, separate JAR file.
Implementing a capability matcher consists of two steps:
- Implementing the capability matcher.
- Providing registration information to make the capability matcher configurable.
Each capability matcher has to implement the interface com.xing.qa.selenium.grid.hub.capmat.CapMat
, which is a simple,
single method interface:
public interface CapMat {
public boolean matches(Object requested, Object provided);
}
The capability matcher has to provide a no-argument constructor to be usable.
To make the capability matcher available for use, you have to provide a file named capabilityMatchers
in the root of your
jar/classpath.
The file is a pretty standard Java properties file mapping a name that is used to reference the capability matcher later on to the class name of the capability matcher to instantiate.
Here is the file content of the file registering the default matchers:
rvm: com.xing.qa.selenium.grid.hub.capmat.RubyVersionMatcher
platform: com.xing.qa.selenium.grid.hub.capmat.PlatformMatcher
exact: com.xing.qa.selenium.grid.hub.capmat.ExactMatcher
After extending the registration file in this project or adding your external jar containing the file you can map your matcher to a capability (name) in the Matcher configuration.
| Property | Value | Description |
+----------+-------+-------------------------------------------------------+
| _CI
| bool | Indicates that the session is coming from a CI server |
- The configuration is somewhat elaborate to avoid messing with the core selenium configuration. It might be an idea to add a configuration file on its own or to extend the selenium node configuration as the configuration by environment might hit a limit at some point (esp. with the ConfigurableCapabilityMatcher).