Monitor the OH exchange and organization patterns, services, and nodes. This container may be run locally using Docker, pushed to a Docker registry, and published to any Open Horizon exchange.
org
-[email protected]
url
-com.github.dcmartin.open-horizon.hznmonitor
version
-0.0.1
3094
- Web service for HTML at/
and CGI scripts at/cgi-bin
3095
-hznmonitor
service status; returnsapplication/json
- NONE
HZNMONITOR_EXCHANGE_APIKEY
- API key for exchangeHZNMONITOR_EXCHANGE_ORG
- Horizon organization identifierHZNMONITOR_EXCHANGE_URL
- URL for exchangeHZNMONITOR_KAFKA_APIKEY
- API key for Kafka message broker(s)HZNMONITOR_KAFKA_BROKER
- Kafka broker list; default providedHZNMONITOR_KAFKA_ADMIN_URL
- Kafka admin URL; default providedHZNMONITOR_KAFKA_TOPIC
- Topic to listen; default"startup"
HZNMONITOR_MQTT_HOST
- Hostname (IP address, FQDN) for MQTT brokerHZNMONITOR_MQTT_PASSWORD
- Password for MQTT brokerHZNMONITOR_MQTT_USERNAME
- Username for MQTT brokerAPACHE_HOST
- default:"localhost"
APACHE_PORT
- default:3094
APACHE_ADMIN
- default:"[email protected]"
LOG_LEVEL
- specify level of logging; default:"info"
; options belowLOGTO
- specify place to log; default:"/dev/stderr"
; use""
for"${TMPDIR}/${0##*/}.log"
LOG_LEVEL
- specify level of logging; defaultinfo
; options include (debug
andnone
; currently ignored)DEBUG
- default:false
emerg
- Emergencies - system is unusable.alert
- Action must be taken immediately.crit
- Critical Conditions.error
- Error conditions.warn
- Warning conditions.notice
- Normal but significant condition.info
- Informational.debug
- Debug-level messages
This service provides both HTML and JSON output for status of a Open Horizon organization; the default index.html
page displays information on the exchange configured and the patterns, services, and nodes listed in the exchange. There are six (6) sections in the Web user-interface:
- Matrix - A
N
xN
matrix with each cell representing a node in the exchange. - Catalog - Listing of all services organized by their label (e.g.
apache
) - Nodes - Listing of all nodes in exchange for organization
- Services - Listing of all services in exchange for organization
- Patterns - Listing of all patterns in exchange or organization
- Exchange - Details of exchange
The nodes in the exchange are displayed in a matrix, sorted from most recent to oldest entries based on lastHeartbeat
, colored from dark green to dark red, with the interior cell indicating the node's status. Nodes which have been registered on a device have the number of agreements and the running and total services and are colored from green through yellow to red. Nodes which have never been registered are colored white and include the age of the node. For example:
And for visualizing many node (e.g. 5000+):
A listing of services in the exchange organized by the label
attribute; service URL, version, and architecture(s) are listed, including container size, number of layers, age, and container pulls, for example for all services with the label
value apache
:
Nodes are listed from most recent updatate to oldest; each node is described, for example:
Services are listed from most recent updatate to oldest; each service is described, for example:
The status of the designated exchange is displayed, including global statistics (e.g. number of nodes), for example:
This service provides common-gateway-interface (CGI) scripts to provide information about the exchange, organization, patterns, services, and nodes. Each script is accessible through the path /cgi-bin/<script>
; the following is the list of available scripts:
exchange
- the exchange configured; sampleservices
- the organization's services; samplepatterns
- the organization's patterns; samplestatus
- a node status; samplelabels
- return services grouped by label; samplenodes
- the organization's nodes; sampleusers
- the organization's users; samplesummary
- summary statistics from Kafka messages on topicstartup
; sample; seestartup
EXAMPLE
% curl -sSL localhost:3094/cgi-bin/exchange
Produces the following JSON output:
{
"msg": "Exchange server operating normally",
"numberOfUsers": 84,
"numberOfNodes": 175,
"numberOfNodeAgreements": 74,
"numberOfNodeMsgs": 3,
"numberOfAgbots": 3,
"numberOfAgbotAgreements": 100,
"numberOfAgbotMsgs": 0,
"dbSchemaVersion": 17,
"org": "[email protected]",
"url": "http://exchange:3090/v1/"
}
On a supported device, e.g. RaspberryPi Model 3B+ with Sony Playstation3 Eye camera:
Configure and install the device appropriately:
Collect required exchange information and encode as environment variables; get the apiKey.json
file by downloading from the IBM Cloud IAM service. Change the HZN_ORG_ID
to an appropriate organization value.
HZN_ORG_ID="<YOUR_ORGANIZATION_ID>"
HZN_EXCHANGE_APIKEY=$(jq -r '.apiKey' ~/apiKey.json)
Create a pattern.json
file containing the services array for the pattern; for example:
{
"label": "hznmonitor",
"description": "monitor horizon organization",
"services": [
{
"serviceUrl": "com.github.dcmartin.open-horizon.hznmonitor",
"serviceOrgid": "${HZN_ORG_ID}",
"serviceArch": "amd64",
"serviceVersions": [
{
"version": "0.0.1"
}
]
},
{
"serviceUrl": "com.github.dcmartin.open-horizon.hznmonitor",
"serviceOrgid": "${HZN_ORG_ID}",
"serviceArch": "arm",
"serviceVersions": [
{
"version": "0.0.1"
}
]
},
{
"serviceUrl": "com.github.dcmartin.open-horizon.hznmonitor",
"serviceOrgid": "${HZN_ORG_ID}",
"serviceArch": "arm64",
"serviceVersions": [
{
"version": "0.0.1"
}
]
}
]
}
Generate a public/private key-pair for signing:
hzn key create ${HZN_ORG_ID} $(whoami)@$(hostname)
PRIVATE_KEY_FILE=*-private.key
PUBLIC_KEY_FILE=*-public.pem
Publish the pattern in the exchange for the organization; for example:
hzn exchange pattern publish -o "${HZN_ORG_ID}" -u ${HZN_USER_ID:-iamapikey}:${HZN_EXCHANGE_APIKEY} -f pattern.json -p ${PATTERN} -k ${PRIVATE_KEY_FILE} -K ${PUBLIC_KEY_FILE}
Confirm pattern publishing; there may be more than one pattern in the organization; look for hznmonitor
:
% hzn exchange pattern list -o ${HZN_ORG_ID} -u ${HZN_USER_ID:-iamapikey}:${HZN_EXCHANGE_APIKEY}
[
"<YOUR_ORGANIZATION_ID>/hznmonitor"
]
Create a userinput.json
file containing the environment variables for each service; for example:
{
"global": [],
"services": [
{
"org": "${HZN_ORG_ID}",
"url": "com.github.dcmartin.open-horizon.hznmonitor",
"versionRange": "[0.0.0,INFINITY)",
"variables": {
"HZNMONITOR_EXCHANGE_URL": "http://exchange:3090/v1/",
"HZNMONITOR_EXCHANGE_ORG": "<YOUR_ORGANIZATION_ID>",
"HZNMONITOR_EXCHANGE_APIKEY": "<YOUR_EXCHANGE_APIKEY>",
"HZNMONITOR_KAFKA_APIKEY":"<YOUR_KAFKA_APIKEY>",
"HZNMONITOR_MQTT_HOST":"<YOUR MQTT BROKER IP>",
"HZNMONITOR_MQTT_USERNAME":"<username>",
"HZNMONITOR_MQTT_PASSWROD":"<password>",
"HZNMONITOR_PERIOD": "30",
"LOG_LEVEL": "info",
"LOGTO": "",
"DEBUG": "false"
}
]
}
Specify pattern (PATTERN
), device identifier (ID
) and device password (PW
):
PATTERN=${HZN_ORG_ID}/hznmonitor
ID=$(hostname)
PW=$(read -p "Enter password: " -s)
Use the hzn
command-line-interface (CLI) application to register the device for the pattern, for example:
PATTERN="hznmonitor"
hzn register ${HZN_ORG_ID} -u ${HZN_USER_ID:-iamapikey}:${HZN_EXCHANGE_APIKEY} ${PATTERN} -f userinput.json -n "${ID}:${PW}"
Inspect the device to determine proper node configuration, for example:
% hzn node list
{
"id": "test-amd64-1",
"organization": "<YOUR_ORGANIZATION_ID>",
"pattern": "<YOUR_ORGANIZATION_ID>/hznmonitor",
"name": "<YOUR_NODE_ID>",
"token_last_valid_time": "2019-05-15 06:56:53 -0700 PDT",
"token_valid": true,
"ha": false,
"configstate": {
"state": "configured",
"last_update_time": "2019-05-15 06:57:09 -0700 PDT"
},
"configuration": {
"exchange_api": "http://exchange:3090/v1/",
"exchange_version": "1.80.0",
"required_minimum_exchange_version": "1.73.0",
"preferred_exchange_version": "1.75.0",
"architecture": "amd64",
"horizon_version": "2.23.1"
},
"connectivity": {
"firmware.bluehorizon.network": true,
"images.bluehorizon.network": true
}
}
Verify operation of device as node running pattern of services, for example:
% hzn service list
[
{
"url": "com.github.dcmartin.open-horizon.hznmonitor",
"org": "${HZN_ORG_ID}",
"version": "0.0.1",
"arch": "amd64",
"variables": {
}
}
]
Copy this repository, change to the repository directory (e.g. ~/gitdir
), and then use the make command; see below:
% mkdir ~/gitdir
% cd ~/gitdir
% git clone http://github.com/dcmartin/open-horizon
% cd open-horizon/hznmonitor
% make build
% make start
After the service has started the status can be inspected; run the following command to check status (and yield check.json
residual):
% make check
Produces a JSON status payload; the status
attribute is a BASE64 encoded HTML web service status page:
{
"hznmonitor": {
"pid": 44,
"status": "PCFET0NUWVBFIEhUTUwgUFVCTElDICItLy9XM0MvL0RURCBIVE1MIDMuMiBGaW5hbC8vRU4iPgo8aHRtbD48aGVhZD4KPHRpdGxlPkFwYWNoZSBTdGF0dXM8L3RpdGxlPgo8L2hlYWQ+PGJvZHk+CjxoMT5BcGFjaGUgU2VydmVyIFN0YXR1cyBmb3IgbG9jYWxob3N0ICh2aWEgMTI3LjAuMC4xKTwvaDE+Cgo8ZGw+PGR0PlNlcnZlciBWZXJzaW9uOiBBcGFjaGUvMi40LjM5IChVbml4KTwvZHQ+CjxkdD5TZXJ2ZXIgTVBNOiBwcmVmb3JrPC9kdD4KPGR0PlNlcnZlciBCdWlsdDogQXByICAzIDIwMTkgMTU6NTI6NTcKPC9kdD48L2RsPjxociAvPjxkbD4KPGR0PkN1cnJlbnQgVGltZTogTW9uZGF5LCAyOS1BcHItMjAxOSAxNDo1NTowMiA8L2R0Pgo8ZHQ+UmVzdGFydCBUaW1lOiBNb25kYXksIDI5LUFwci0yMDE5IDE0OjU1OjAyIDwvZHQ+CjxkdD5QYXJlbnQgU2VydmVyIENvbmZpZy4gR2VuZXJhdGlvbjogMTwvZHQ+CjxkdD5QYXJlbnQgU2VydmVyIE1QTSBHZW5lcmF0aW9uOiAwPC9kdD4KPGR0PlNlcnZlciB1cHRpbWU6IDwvZHQ+CjxkdD5TZXJ2ZXIgbG9hZDogMC41OSAwLjE5IDAuMTA8L2R0Pgo8ZHQ+VG90YWwgYWNjZXNzZXM6IDAgLSBUb3RhbCBUcmFmZmljOiAwIGtCIC0gVG90YWwgRHVyYXRpb246IDA8L2R0Pgo8ZHQ+Q1BVIFVzYWdlOiB1MCBzMCBjdTAgY3MwPC9kdD4KPGR0PjwvZHQ+CjxkdD4xIHJlcXVlc3RzIGN1cnJlbnRseSBiZWluZyBwcm9jZXNzZWQsIDQgaWRsZSB3b3JrZXJzPC9kdD4KPC9kbD48cHJlPldfX19fLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4KLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLgouLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uCi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi48L3ByZT4KPHA+U2NvcmVib2FyZCBLZXk6PGJyIC8+CiI8Yj48Y29kZT5fPC9jb2RlPjwvYj4iIFdhaXRpbmcgZm9yIENvbm5lY3Rpb24sIAoiPGI+PGNvZGU+UzwvY29kZT48L2I+IiBTdGFydGluZyB1cCwgCiI8Yj48Y29kZT5SPC9jb2RlPjwvYj4iIFJlYWRpbmcgUmVxdWVzdCw8YnIgLz4KIjxiPjxjb2RlPlc8L2NvZGU+PC9iPiIgU2VuZGluZyBSZXBseSwgCiI8Yj48Y29kZT5LPC9jb2RlPjwvYj4iIEtlZXBhbGl2ZSAocmVhZCksIAoiPGI+PGNvZGU+RDwvY29kZT48L2I+IiBETlMgTG9va3VwLDxiciAvPgoiPGI+PGNvZGU+QzwvY29kZT48L2I+IiBDbG9zaW5nIGNvbm5lY3Rpb24sIAoiPGI+PGNvZGU+TDwvY29kZT48L2I+IiBMb2dnaW5nLCAKIjxiPjxjb2RlPkc8L2NvZGU+PC9iPiIgR3JhY2VmdWxseSBmaW5pc2hpbmcsPGJyIC8+IAoiPGI+PGNvZGU+STwvY29kZT48L2I+IiBJZGxlIGNsZWFudXAgb2Ygd29ya2VyLCAKIjxiPjxjb2RlPi48L2NvZGU+PC9iPiIgT3BlbiBzbG90IHdpdGggbm8gY3VycmVudCBwcm9jZXNzPGJyIC8+CjwvcD4KCgo8dGFibGUgYm9yZGVyPSIwIj48dHI+PHRoPlNydjwvdGg+PHRoPlBJRDwvdGg+PHRoPkFjYzwvdGg+PHRoPk08L3RoPjx0aD5DUFUKPC90aD48dGg+U1M8L3RoPjx0aD5SZXE8L3RoPjx0aD5EdXI8L3RoPjx0aD5Db25uPC90aD48dGg+Q2hpbGQ8L3RoPjx0aD5TbG90PC90aD48dGg+Q2xpZW50PC90aD48dGg+UHJvdG9jb2w8L3RoPjx0aD5WSG9zdDwvdGg+PHRoPlJlcXVlc3Q8L3RoPjwvdHI+Cgo8dHI+PHRkPjxiPjAtMDwvYj48L3RkPjx0ZD40NzwvdGQ+PHRkPjAvMC8wPC90ZD48dGQ+PGI+VzwvYj4KPC90ZD48dGQ+MC4wMDwvdGQ+PHRkPjA8L3RkPjx0ZD4wPC90ZD48dGQ+MDwvdGQ+PHRkPjAuMDwvdGQ+PHRkPjAuMDA8L3RkPjx0ZD4wLjAwCjwvdGQ+PHRkPjEyNy4wLjAuMTwvdGQ+PHRkPmh0dHAvMS4xPC90ZD48dGQgbm93cmFwPjE5Mi4xNjguMzIuMjozMDk0PC90ZD48dGQgbm93cmFwPkdFVCAvc2VydmVyLXN0YXR1cyBIVFRQLzEuMTwvdGQ+PC90cj4KCjwvdGFibGU+CiA8aHIgLz4gPHRhYmxlPgogPHRyPjx0aD5TcnY8L3RoPjx0ZD5DaGlsZCBTZXJ2ZXIgbnVtYmVyIC0gZ2VuZXJhdGlvbjwvdGQ+PC90cj4KIDx0cj48dGg+UElEPC90aD48dGQ+T1MgcHJvY2VzcyBJRDwvdGQ+PC90cj4KIDx0cj48dGg+QWNjPC90aD48dGQ+TnVtYmVyIG9mIGFjY2Vzc2VzIHRoaXMgY29ubmVjdGlvbiAvIHRoaXMgY2hpbGQgLyB0aGlzIHNsb3Q8L3RkPjwvdHI+CiA8dHI+PHRoPk08L3RoPjx0ZD5Nb2RlIG9mIG9wZXJhdGlvbjwvdGQ+PC90cj4KPHRyPjx0aD5DUFU8L3RoPjx0ZD5DUFUgdXNhZ2UsIG51bWJlciBvZiBzZWNvbmRzPC90ZD48L3RyPgo8dHI+PHRoPlNTPC90aD48dGQ+U2Vjb25kcyBzaW5jZSBiZWdpbm5pbmcgb2YgbW9zdCByZWNlbnQgcmVxdWVzdDwvdGQ+PC90cj4KIDx0cj48dGg+UmVxPC90aD48dGQ+TWlsbGlzZWNvbmRzIHJlcXVpcmVkIHRvIHByb2Nlc3MgbW9zdCByZWNlbnQgcmVxdWVzdDwvdGQ+PC90cj4KIDx0cj48dGg+RHVyPC90aD48dGQ+U3VtIG9mIG1pbGxpc2Vjb25kcyByZXF1aXJlZCB0byBwcm9jZXNzIGFsbCByZXF1ZXN0czwvdGQ+PC90cj4KIDx0cj48dGg+Q29ubjwvdGg+PHRkPktpbG9ieXRlcyB0cmFuc2ZlcnJlZCB0aGlzIGNvbm5lY3Rpb248L3RkPjwvdHI+CiA8dHI+PHRoPkNoaWxkPC90aD48dGQ+TWVnYWJ5dGVzIHRyYW5zZmVycmVkIHRoaXMgY2hpbGQ8L3RkPjwvdHI+CiA8dHI+PHRoPlNsb3Q8L3RoPjx0ZD5Ub3RhbCBtZWdhYnl0ZXMgdHJhbnNmZXJyZWQgdGhpcyBzbG90PC90ZD48L3RyPgogPC90YWJsZT4KPGhyIC8+CjxhZGRyZXNzPkFwYWNoZS8yLjQuMzkgKFVuaXgpIFNlcnZlciBhdCBsb2NhbGhvc3QgUG9ydCAzMDk0PC9hZGRyZXNzPgo8L2JvZHk+PC9odG1sPgo="
},
"date": 1556549701,
"hzn": {
"agreementid": "906360d575ee15646ef9752dbc2514047efbc8c55cec3a0a0e91574fe544fbb3",
"arch": "amd64",
"cpus": 1,
"device_id": "davidsimac.local",
"exchange_url": "http://exchange:3090/v1",
"host_ips": [
"127.0.0.1",
"192.168.1.27",
"192.168.1.26",
"9.80.94.82"
],
"organization": "[email protected]",
"ram": 1024,
"pattern": null
},
"config": {
"tmpdir": "/tmp",
"logto": "/dev/stderr",
"log_level": "info",
"debug": false,
"org": "[email protected]",
"exchange": "http://exchange:3090/v1/",
"db": "https://515bed78-9ddc-408c-bf41-32502db2ddf8-bluemix.cloudant.com",
"username": "515bed78-9ddc-408c-bf41-32502db2ddf8-bluemix",
"conf": "/etc/apache2/httpd.conf",
"htdocs": "/var/www/localhost/htdocs",
"cgibin": "/var/www/localhost/cgi-bin",
"host": "localhost",
"port": "3094",
"admin": "[email protected]",
"pidfile": "/var/run/apache2.pid",
"rundir": "/var/run/apache2",
"services": null
},
"service": {
"label": "hznmonitor",
"version": "0.0.1",
"port": 3095
}
}
Releases are based on Semantic Versioning, and use the format
of MAJOR.MINOR.PATCH
. In a nutshell, the version will be incremented
based on the following:
MAJOR
: Incompatible or major changes.MINOR
: Backwards-compatible new features and enhancements.PATCH
: Backwards-compatible bugfixes and package updates.