ClusterControl  Version1.7.6
Controller SDK
The RPC v2 services

Table of Contents

The RPC v2

This is the documentation of the RPC v2, a new RPC interface for the Cmon Controller that exists beside the original (or v1) interface. The two versions of the RPC interface are independent from each other, one can be used without the services of the other.

Please note that the RPC v2 API calls are available only using secure (TLS) connections, encryption is mandatory. The RPC v2 also uses an authentication call, very little of the calls are available to be used without authenticating first.

By default the encrypted communication can be used through port 9501 and the RPC v2 requests are server served on the /v2 path (e.g. /v2/clusters).

List of RPC v2 paths:

  • /v2/auth: Authenticaton.
  • /v2/alarm: Information about alarms.
  • /v2/users: Handling Cmon users.
  • /v2/backup: Creating and restoring backups.
  • /v2/clusters: Information about clusters.
  • /v2/host: Information about hosts.
  • /v2/stat: Statistical information, measurements.
  • /v2/jobs: Creating and monitoring jobs.
  • /v2/maintenance: Maintenance periods.
  • /v2/metatype: Information about types used by the controller.
  • /v2/config: Configuration.
  • /v2/process: Processes.
  • /v2/log: Logging.
  • /v2/imperative: Maintaining and running scripts.
  • /v2/discovery: Collecting real-time data.

Here is an example showing a typical RPC v2 request:

{
"cluster_name": "ft_galera_18262",
"operation": "getClusterInfo",
"request_id": 2,
"request_created": "2017-06-22T06:13:46.393Z",
}

Most of the requests contain at least the following fields:

  • operation: This field identifies what kind of action the controller should take to serve the request.
  • request_id: Optional field that will be sent back with the reply.
  • request_created: This is an optional field which will be sent back to the client with the reply.
  • cluster_name: Many of the requests are closely related to one cluster and that cluster can be identified by the cluster ID or the cluster ID.
  • cluster_name: Many of the requests are closely related to one cluster and that cluster can be identified by the cluster ID or the cluster ID.

The RPC v2 reply usually contain many fields, but some fields can be found in all the replies. Here is an example showing some of the fields that is always sent with the reply:

{
"request_id": 2,
"request_created": "2017-06-22T06:13:46.339Z",
"request_processed": "2017-06-22T06:13:46.385Z",
"request_status": "Ok"
"request_user_id": 3
}
  • messages: A list of strings with human readable messages describing what happened while processing the request.
  • error_string: A human readable error message describing the error if an error happened. Should a request result in more than one message strings the "messages" field will be sent.
  • request_id: If the request held this field it will be simply sent back in the reply.
  • request_created: This is just a field that sent back by the controller without modification.
  • request_processed: The timestamp shows what time the reply was created.
  • request_status: A string showing if the request was successfuly processed and the error code if not. The possible values are:
    • Ok: The request was successfully processed.
    • InvalidRequest: Something was fundamentally wrong with the request.
    • ObjectNotFound: The referenced object (e.g. the cluster) was not found.
    • TryAgain: The request can not at the moment processed.
    • UnknownError: The exact error could not be identified.
    • AccessDenied: The authenticated user has insufficient rights.
    • AuthRequired: The client has to authenticate first.
  • request_user_id: The numeric ID of the authenticated user. Using this field the client can identify who sent the request.

Alarms

getStatistics

The "getStatistics" can be used to get statistical information about alarms. Most importantly this is a quick way to check if there are active alarms on the cluster.

{
"cluster_id": 1,
"operation": "getStatistics",
"request_created": "2017-08-02T05:22:29.566Z",
"request_id": 3
}
  • cluster_id: The numerical ID of the cluster to check. The cluster can also be identified by the cluster name.
  • cluster_name: The name of the cluster to check. The cluster ID can also be used to identify the cluster.
  • cluster_ids: Multiple cluster can also be requested in one call.
{
"alarm_statistics": [
{
"class_name": "CmonAlarmStatistics",
"cluster_id": 1,
"critical": 2,
"warning": 1
} ],
"request_created": "2017-08-02T05:22:29.566Z",
"request_id": 3,
"request_processed": "2017-08-02T05:22:29.615Z",
"request_status": "Ok",
"request_user_id": 3
}

FIXME: There is not much information we send back, maybe we could enhance this.

getAlarm

Get information about one particular alarm.

{
"alarm_id": 1,
"operation": "getAlarm",
"request_created": "2017-08-02T04:52:33.983Z",
"request_id": 3
}
  • alarm_id: The numerical ID of the alarm to return.
  • cluster_id: This field is not required for the call, but if provided it will control which cluster to read.
  • cluster_name: This field is not required for the call, but if provided it will control which cluster to read.
{
"alarm":
{
"alarm_id": 1,
"class_name": "CmonAlarm",
"cluster_id": 1,
"component": 3,
"component_name": "Cluster",
"counter": 1,
"created": "2017-08-02T04:50:52.000Z",
"ignored": 0,
"measured": 0,
"message": "System time is drifting between servers and the distance between the highest system time and lowest is more than one minute.",
"recommendation": "Synchronize the system clock on the servers using e.g NTP or make sure NTP is working. Time drifting may lead to unexpected failures or problems, and makes debugging hard.\n\nThe last seen host time values (in controller's time-zone):\n192.168.1.127: Aug 02 06:49:55\n192.168.1.199: Aug 02 06:51:10\n",
"reported": "2017-08-02T04:50:52.000Z",
"severity": 1,
"severity_name": "ALARM_WARNING",
"title": "System time is drifting",
"type": 40000,
"type_name": "ClusterTimeDrift"
},
"request_created": "2017-08-02T04:52:33.983Z",
"request_id": 3,
"request_processed": "2017-08-02T04:52:34.030Z",
"request_status": "Ok",
"request_user_id": 3
}
  • alarm: A CmonAlarm Class object with the details of one particular alarm.

getAlarms

Get information about multiple alarms. Here is an example request:

{
"cluster_id": 1,
"operation": "getAlarms",
"request_created": "2017-07-27T06:36:03.627Z",
"request_id": 3
}

Here is an example reply.

{
"alarms": [
{
"alarm_id": 1,
"class_name": "CmonAlarm",
"cluster_id": 1,
"component": 3,
"component_name": "Cluster",
"counter": 1,
"created": "2017-07-27T06:33:38.000Z",
"ignored": 0,
"measured": 0,
"message": "System time is drifting between servers and the distance between the highest system time and lowest is more than one minute.",
"recommendation": "Synchronize the system clock on the servers using e.g NTP or make sure NTP is working. Time drifting may lead to unexpected failures or problems, and makes debugging hard.\n\nThe last seen host time values (in controller's time-zone):\n192.168.1.127: Jul 27 08:32:40\n192.168.1.169: Jul 27 08:33:44\n",
"reported": "2017-07-27T06:33:38.000Z",
"severity": 1,
"severity_name": "ALARM_WARNING",
"title": "System time is drifting",
"type": 40000,
"type_name": "ClusterTimeDrift"
},
. . .
{
"alarm_id": 3,
"class_name": "CmonAlarm",
"cluster_id": 1,
"component": 0,
"component_name": "Network",
"counter": 12,
"created": "2017-07-27T06:35:56.000Z",
"host_id": 1,
"hostname": "192.168.1.169",
"ignored": 0,
"measured": 0,
"message": "Server 192.168.1.169 reports: Host 192.168.1.169 is not responding to ping after 3 cycles, the host is most likely unreachable.",
"recommendation": "Restart failed host, check firewall.",
"reported": "2017-07-27T06:35:56.000Z",
"severity": 2,
"severity_name": "ALARM_CRITICAL",
"title": "Host is not responding",
"type": 10006,
"type_name": "HostUnreachable"
} ],
"cluster_id": 1,
"request_created": "2017-07-27T06:36:03.627Z",
"request_id": 3,
"request_processed": "2017-07-27T06:36:03.675Z",
"request_status": "Ok",
"request_user_id": 3
}
  • alarms: A list of CmonAlarm Class objects with the details of the alarms.

FIXME: This call has no "total" field.

ignoreAlarm

Set the state of an alarm.

{
"alarm_id": 5,
"ignore": true,
"operation": "ignoreAlarm",
"request_created": "2018-09-25T08:03:17.416Z",
"request_id": 3
}
{
"alarm":
{
"alarm_id": 5,
"class_name": "CmonAlarm",
"cluster_id": 1,
"component": 0,
"component_name": "Network",
"counter": 1,
"created": "2018-09-25T08:03:17.000Z",
"host_id": 1,
"ignored": 1,
"measured": 0,
"message": "Server 192.168.0.79 reports: SSH failure to 192.168.0.79: No route to host.",
"recommendation": "Please check SSH access to host. The host may also be down.",
"reported": "2018-09-25T08:03:17.000Z",
"severity": 2,
"severity_name": "ALARM_CRITICAL",
"title": "SSH failed",
"type": 10008,
"type_name": "HostSshFailed"
},
"request_created": "2018-09-25T08:03:17.416Z",
"request_id": 3,
"request_processed": "2018-09-25T08:03:17.425Z",
"request_status": "Ok",
"request_user_id": 4
}

User Authentication

The client authentication methods are available on this path (/v2/auth).

There are currently two separate way is available for user authentication, one that uses a public/private key pair and one that uses a password. The kay based authentication has two phases, the password based authentication uses one request.

You may send "with_extended_privileges": true to this request to get extended user information in case of successful authentication.

The password based authentication is implemented using the "authenticateWithPassword" request:

{
"operation": "authenticateWithPassword",
"password": "secret",
"request_created": "2017-06-29T05:46:10.622Z",
"request_id": 1,
"user_name": "system"
}

The key based authentication starts with the client sending an "authenticate" request to the controller:

{
"operation": "authenticate",
"request_created": "2017-06-22T07:50:18.117Z",
"request_id": 1,
"user_name": "worf"
}

The controller replying by sending a challenge to the client:

{
"challenge": "3694b3af-e2f0-e49e-4f32-1549fd824ea9",
"request_created": "2017-06-22T07:50:18.117Z",
"request_id": 1,
"request_processed": "2017-06-22T07:50:18.163Z",
"request_status": "Ok",
"request_user_id": 0
"user_name": "worf"
}

The client then encrypts the challenge string with its private key and sends it back in a new reuquest:

{
"operation": "authenticateresponse",
"request_created": "2017-06-22T07:50:18.169Z",
"request_id": 2,
"signature": "XjiGlQNbq/yN9vzgS5uPjJfKw8DLXGD/uwlw+cec/iC9GJdgs1rZO6GNQzR2Mk6StgopX7gCDQwNVF426v3kGVSKEsQe5BoEJ/RRBfWmxyf1imDzS7O97vwBPw0Ko4TV57jpexLaOrFWTXraEsPnnSZffEpK4hzQnBRWorSCcFbkm3XJQIqNcf6BhpHsW1pZClmojElcXIh6aBkqnQgGF8rwk6rFDRi/vx7kikVLlw4EDFgzedfKfZkxNlbhcz6Z9XMagxPxNShMb/jWY0jWKL7OVVRfgFQ27DycqtMdteXDjn5gCkxO8oT693FMHaDlgjt21Azeny8IFpy1f2ZFIg=="
}

Should the signature match the controller sends back a reply notfying the client that it is now authenticated for the session:

{
"is_superuser": true,
"request_created": "2017-08-23T09:22:36.857Z",
"request_id": 2,
"request_processed": "2017-08-23T09:22:36.906Z",
"request_status": "Ok",
"user":
{
"class_name": "CmonUser",
"email_address": "[email protected]",
"first_name": "Laszlo",
"groups": [
{
"class_name": "CmonGroup",
"group_id": 1,
"group_name": "admins"
} ],
"last_name": "Pere",
"title": "",
"user_id": 3,
"user_name": "admin"
}
}
  • is_superuser: Shows if the authenticated user actually has the superuser privileges.
  • request_status: Shows that the authentication is succeeded and the session now has an authenticated user.
  • request_user_id: The request that produced this reply had no authenticated user ID, so the reply will not have this field.
  • user: The details about the user authenticated for the session.

authenticate

The first step in the process where the client initiates the authentication by sending the "authenticate" request.

{
"operation": "authenticate",
"user_name": "worf"
}

This request should be called with the following arguments:

  • user_name: The user name of the Cmon user.

The controller then will reply back with a security challenge text to the client:

{
"challenge": "3694b3af-e2f0-e49e-4f32-1549fd824ea9",
"request_processed": "2017-06-22T07:50:18.163Z",
"request_status": "Ok",
"request_user_id": 0
"user_name": "worf"
}

The reply contains the following fields:

  • challenge: The string that should be encrypted by the client with the private key.
  • request_user_id: The user ID of the currently authenticated user. If no authentication was finished before this numerical ID is 0.
  • user_name: The name of the user which is going to be authenticated.

The next expected request from client is a 'response' call, with the signature created using the client private key.

passwordReset

This call should be sent twice in order to reset a user's password: once to create and send a "forgot email" password and once for reset the password using the token received in the email. Both requests are the same, the only difference is that the first has no token and password in it while the second has both the token and the new password:

{
"operation": "passwordReset",
"request_created": "2019-05-31T10:14:30.457Z",
"request_id": 1,
"user_name": "system"
}
  • user_name: The name of the user for whom the password reset email will be sent. The email address can not be in the request for sequrity reasons, the controller will use the pre-registered email address of the user or if no such email address is registered the call will fail.
  • base_url: The first part of the URL that will be sent in the email.
{
"new_password": "newpassword",
"operation": "passwordReset",
"request_created": "2019-05-31T10:14:30.540Z",
"request_id": 1,
"password_reset_token": "5a018ede8d9e486aadc700fc241e9590",
"user_name": "system"
}
  • new_password: The new password that will be set for the user.
  • token: The password change token that was received in the email.

authenticateResponse

This method shall be called by the client once it receives a security challenge (in reply to "authenticate" call) from the server.

{
"operation": "authenticateresponse",
"request_created": "2017-06-22T07:50:18.169Z",
"request_id": 2,
"signature": "XjiGlQNbq/yN9vzgS5uPjJfKw8DLXGD/uwlw+cec/iC9GJdgs1rZO6GNQzR2Mk6StgopX7gCDQwNVF426v3kGVSKEsQe5BoEJ/RRBfWmxyf1imDzS7O97vwBPw0Ko4TV57jpexLaOrFWTXraEsPnnSZffEpK4hzQnBRWorSCcFbkm3XJQIqNcf6BhpHsW1pZClmojElcXIh6aBkqnQgGF8rwk6rFDRi/vx7kikVLlw4EDFgzedfKfZkxNlbhcz6Z9XMagxPxNShMb/jWY0jWKL7OVVRfgFQ27DycqtMdteXDjn5gCkxO8oT693FMHaDlgjt21Azeny8IFpy1f2ZFIg=="
}

This method should be called the following arguments:

  • signature: A base64 RSA-SHA256 signature using the client's private key of the 'challenge' text.

In reply backend sends either an error message if authentication failed, or just a success message showing that the authentication is finished and the user is authenticated for the session.

Here is an example for a reply that sent after the authentication succeeded:

{
"request_created": "2017-06-29T13:25:32.885Z",
"request_id": 2,
"request_processed": "2017-06-29T13:25:32.936Z",
"request_status": "Ok",
"user":
{
"class_name": "CmonUser",
"email_address": "[email protected]",
"first_name": "Worf",
"groups": [
{
"class_name": "CmonGroup",
"group_id": 4,
"group_name": "ds9"
} ],
"last_login": "2017-06-29T13:25:21.906Z",
"last_name": "",
"title": "Lt.",
"user_id": 12,
"user_name": "worf"
}
}

authenticateWithPassword

It is possible to authenticate the user using the user's own password. Here is an example showing the "authenticateWithPassword" request:

{
"operation": "authenticateWithPassword",
"password": "secret",
"request_created": "2017-06-29T05:46:10.622Z",
"request_id": 1,
"user_name": "system"
}
  • user_name: The username of the user to authenticate.
  • password: The plain password of the user. Please note that the plain password is not a property of the CmonUser class, it is not stored by the controller, it is only used temporarily.

Here is a reply that sent after the authentication request succeeded:

{
"request_created": "2017-06-29T13:22:58.592Z",
"request_id": 1,
"request_processed": "2017-06-29T13:22:58.645Z",
"request_status": "Ok",
"user":
{
"class_name": "CmonUser",
"first_name": "System",
"groups": [
{
"class_name": "CmonGroup",
"group_id": 1,
"group_name": "admins"
} ],
"last_login": "2017-06-29T13:22:42.624Z",
"last_name": "User",
"user_id": 1,
"user_name": "system"
}
}

And here is a reply that sent about a failed attempt:

{
"error_string": "Wrong username or password.",
"reply_received": "2017-06-29T05:57:27.128Z",
"request_created": "2017-06-29T05:57:27.118Z",
"request_id": 1,
"request_processed": "2017-06-29T05:57:27.193Z",
"request_status": "AccessDenied"
}

Please note that the reply contains no "request_user_id" field because the session is not authenticated and there is no known user to associate with it. It is also worth noting that the error string does not specify what exactly was the problem with the authentication. Since there is no known user we only send a seneric error message. In this case the user was found, but the password did not match, but we are not sending this information with the reply.

Information about Backups

The v2/backup path can be used to get information about backups.

Please note tha creating and restoring a backup is a job and so is not implemented here.

getBackups

An RPC call that returns the backups of a cluster.

{
"ascending": true,
"cluster_id": 1,
"operation": "getBackups",
"request_created": "2017-06-26T09:07:48.374Z",
"request_id": 3
}
{
"backup_records": [
{
"backup": [
{
"db": "all",
"files": [
{
"class_name": "CmonBackupFile",
"created": "2017-06-26T09:07:35.000Z",
"hash": "md5:938bacc9699b362eb1be355eace1281d",
"path": "pg_dump_2017-06-26_110732.sql.gz",
"size": 796,
"type": "full"
} ],
"start_time": "2017-06-26T09:07:35.000Z"
} ],
"backup_host": "192.168.1.198",
"chain_up": 0,
"cid": 1,
"class_name": "CmonBackupRecord",
"compressed": true,
"config":
{
"backupDir": "/tmp",
"backupHost": "192.168.1.198",
"backupMethod": "",
"backupToIndividualFiles": false,
"backup_failover": false,
"backup_failover_host": "",
"ccStorage": true,
"compression": true,
"createdBy": "pipas",
"description": "Backup created by s9s-tools.",
"includeDatabases": "",
"netcat_port": 9999,
"origBackupDir": "/tmp",
"port": null,
"scheduleId": 0,
"set_gtid_purged_off": true,
"storageHost": "",
"throttle_rate_iops": 0,
"throttle_rate_netbw": 0,
"usePigz": false,
"wsrep_desync": false,
"xtrabackupParallellism": 1,
"xtrabackup_locks": false
},
"created": "2017-06-26T09:07:32.000Z",
"created_by": "",
"description": "",
"finished": "2017-06-26T09:07:35.930Z",
"id": 1,
"job_id": 2,
"log_file": "",
"lsn": 0,
"method": "pgdump",
"parent_id": 0,
"root_dir": "/tmp/BACKUP-1",
"schedule_id": 0,
"status": "Completed",
"storage_host": "192.168.1.127",
"total_datadir_size": 38959683,
"verified":
{
"message": "",
"status": "Unverified",
"verified_time": "1969-12-31T23:59:59.000Z"
}
},
. . .
],
"request_created": "2017-06-26T09:07:48.374Z",
"request_id": 3,
"request_processed": "2017-06-26T09:07:48.423Z",
"request_status": "Ok",
"request_user_id": 3,
"total": 2
}

getBackupSchedules

An RPC call to get the backup schedules from the controller.

Please note that the backup schedules are deprecated. Instead of creating scheduled backups creating of scheduled jobs (that create backups) is advisable. This RPC call was only created for backward compatibility.

Here is how a request should look like:

{
"operation": "getBackupSchedules",
"request_created": "2020-01-29T11:01:25.887Z",
"request_id": 3
}

And here is how the reply should look like:

{
"backup_schedules": [ {
"class_name": "CmonBackupSchedule",
"cluster_id": 1,
"enabled": true,
"id": 1,
"job": {
"command": "backup",
"job_data": {
"description": "Backup created by s9s-tools."
},
"scheduleId": 1
},
"lastExec": "1970-01-01T00:00:00.000Z",
"schedule": "0 12 * * 5"
} ],
"debug_messages": [ "RPC V2 authenticated user is 'pipas'." ],
"reply_received": "2020-01-29T11:01:25.893Z",
"request_created": "2020-01-29T11:01:25.887Z",
"request_id": 3,
"request_processed": "2020-01-29T11:01:25.893Z",
"request_status": "Ok",
"request_user_id": 4,
"total": 1
}

scheduleBackup

{
"cluster_id": 1,
"operation": "scheduleBackup",
"request_created": "2020-01-29T10:57:52.916Z",
"request_id": 3,
"schedule":
{
"class_name": "CmonBackupSchedule",
"enabled": true,
"job":
{
"command": "backup",
"job_data":
{
"description": "Backup created by s9s-tools."
}
},
"schedule": "0 12 * * 5"
}
}
{
"debug_messages": [ "RPC V2 authenticated user is 'pipas'." ],
"reply_received": "2020-01-29T10:57:52.927Z",
"request_created": "2020-01-29T10:57:52.916Z",
"request_id": 3,
"request_processed": "2020-01-29T10:57:52.926Z",
"request_status": "Ok",
"request_user_id": 4,
"schedule": {
"class_name": "CmonBackupSchedule",
"cluster_id": 1,
"enabled": true,
"id": 1,
"job": {
"command": "backup",
"job_data": {
"description": "Backup created by s9s-tools."
},
"scheduleId": 1
},
"lastExec": null,
"schedule": "0 12 * * 5"
}
}

deleteBackupRecord

An RPC call to delete an existing backup record.

{
"backup_record":
{
"backup_id": 2
},
"operation": "deleteBackupRecord",
"request_created": "2019-09-20T12:15:50.682Z",
"request_id": 3
}

Cluster Information and Manipulation

Cluster information and cluster wide operations can be found on the path /v2/clusters. Information about hosts, alarms, license, statistics can also be sent back for convenience.

getConfig

This is the "getConfig" call for clusters. Please note that there is an other "getConfig" call for nodes and these two should not be mixed up. There are some similarities since the controller is itself a node, but this is an entirely different call because it is on a different path.

Here is how a getConfig call should look like:

{
"cluster_id": 1,
"operation": "getConfig",
"request_created": "2019-09-25T06:53:50.012Z",
"request_id": 3
}

Here is how a reply to a getConfig call looks like:

{
"configuration": {
"values": [
{
"current_value": "",
"default_value": null,
"description": "An arbitrary identifier string of this controller instance.",
"keys": [ "controller_id" ],
"name": "controller_id",
"sql_keys": [ "CONTROLLER_ID" ]
},
. . .
{
"current_value": null,
"default_value": null,
"description": "Specify a lock file and if present on a node, the node will not recover. It is the responsibilty of the administrator to create/remove the file.",
"keys": [ "node_recovery_lock_file" ],
"name": "node_recovery_lock_file",
"sql_keys": [ "NODE_RECOVERY_LOCK_FILE" ]
}
]
},
"debug_messages": [ "RPC V2 authenticated user is 'pipas'." ],
"reply_received": "2019-09-25T12:21:30.909Z",
"request_created": "2019-09-25T12:21:30.904Z",
"request_id": 3,
"request_processed": "2019-09-25T12:21:30.944Z",
"request_status": "Ok",
"request_user_id": 4
}

As it is shown the "configuration" field holds the cluster configuration and inside it there is a list "values" showing the possible settings. The structure of the data here might be enhanced later, we might want to send back more information.

setConfig

This is the "setConfig" call for clusters. There is a similar call for nodes, but that has slightly different requests and replies.

Here is how a setConfig request should look like:

{
"cluster_id": 1,
"configuration": [
{
"name": "host_stats_collection_interval",
"value": "60"
} ],
"operation": "setConfig",
"request_created": "2019-09-25T07:53:11.644Z",
"request_id": 3
}

Here the changes are passed in a list with names and values. The names can be either any of the keys or any of the sql_keys for the configuration item as they are returned for the getConfig call. So one can issue a getConfig call, study, find the key to be changed and send back for the setConfig request.

One needs write access on the configuration (same as the write access on the cluster) to change the configuration.

Here is how a reply looks like (it is just a standard 'ok' reply anyway):

{
"debug_messages": [ "RPC V2 authenticated user is 'pipas'." ],
"reply_received": "2019-09-25T12:19:52.392Z",
"request_created": "2019-09-25T12:19:52.387Z",
"request_id": 3,
"request_processed": "2019-09-25T12:19:52.430Z",
"request_status": "Ok",
"request_user_id": 4
}

getClusterInfo

This request is for reading information about one cluster. The cluster can be identified by the cluster ID or the cluster name.

  • with_hosts: Send also the list of hosts.
  • with_sheet_info: Send also the information from the sheet manager.
  • with_databases: Send also the databases we found on the cluster.
  • with_license_check: Send also the license info.
  • cluster_id: The numerical ID of the cluster to get. The cluster can also be identified by its name.
  • cluster_name: The name of the cluster to get. The cluster can also be identified by its numerical ID.

getAllClusterInfo

This request can be used to get information about one or more clusters. The clusters can be identified by their cluster ID passed as a list of integers in the request.

{
"operation": "getAllClusterInfo",
"request_created": "2017-06-20T06:19:58.451Z",
"with_hosts": true,
"with_sheet_info": true
}
  • cluster_ids: An optional list of cluster IDs represented as integers to control which clusters should be detailed in the reply. If this argument is not provided the controller will return information about clusters that the user can see (has the proper privileges and location to see).
  • with_hosts: Send also the list of hosts.
  • with_sheet_info: Send also the information from the sheet manager.
  • with_databases: Send also the databases we found on the cluster.
  • with_license_check: Send also the license info.

This call will return information about clusters the authenticated user "can see". This means the return can not hold information about a cluster the user has no read access according to the effective ACL and the owner/group owner of the cluster object. Read access for the folder where the cluster is located is also necessary, so if the user has no read access for the folder will not be visible. And finally the chroot environment is also considered. If the user in an other branch of the CDT and so the cluster is not under "his" subtree the cluster will not be visible.

Here is an example of the reply:

{
"clusters": [
{
"alarm_statistics":
{
"class_name": "CmonAlarmStatistics",
"cluster_id": 1,
"critical": 2,
"warning": 1
},
"class_name": "CmonClusterInfo",
"cluster_auto_recovery": true,
"cluster_id": 1,
"cluster_name": "ft_postgresql_59238",
"cluster_type": "POSTGRESQL_SINGLE",
"configuration_file": "/tmp/cmon_1.cnf",
"group_owner":
{
"class_name": "CmonGroup",
"group_id": 4,
"group_name": "testgroup"
},
"hosts": [
{
"class_name": "CmonPostgreSqlHost",
"clusterid": 1,
"configfile": [ "/etc/postgresql/9.6/main/postgresql.conf" ],
"connected": true,
"container": true,
"data_directory": "/var/lib/postgresql/9.6/main",
"datadir": "/var/lib/postgresql/9.6/main",
"description": "",
"distribution":
{
"codename": "xenial",
"name": "ubuntu",
"release": "16.04",
"type": "debian"
},
"hostId": 1,
"hostname": "192.168.1.177",
"hoststatus": "CmonHostOnline",
"hot_standby": true,
"ip": "192.168.1.177",
"lastseen": 1501216153,
"logfile": "/var/log/postgresql/postgresql-9.6-main.log[0m",
"maintenance_mode_active": false,
"message": "Up and running",
"nodetype": "postgres",
"pid": 5248,
"pidfile": "/var/run/postgresql/9.6-main.pid",
"pingstatus": 0,
"pingtime": 0,
"port": 8089,
"readonly": false,
"received_location": "0/1520838",
"replay_location": "0/1520838",
"role": "master",
"sshfailcount": 0,
"ssl_certs":
{
"server":
{
"ca": "",
"id": 0,
"key": "/etc/ssl/private/ssl-cert-snakeoil.key",
"path": "/etc/ssl/certs/ssl-cert-snakeoil.pem"
}
},
"timestamp": 1501216153,
"unique_id": 1,
"uptime": 220,
"version": "9.6.3",
"wallclock": 1501216175,
"wallclocktimestamp": 1501216110
},
{
"class_name": "CmonHost",
"clusterid": 1,
"configfile": "/tmp/cmon_1.cnf",
"connected": true,
"container": false,
"distribution":
{
"codename": "trusty",
"name": "ubuntu",
"release": "14.04",
"type": "debian"
},
"hostId": 2,
"hostname": "192.168.1.127",
"hoststatus": "CmonHostOnline",
"ip": "192.168.1.127",
"lastseen": 1501216153,
"logfile": "/tmp/cmon_1.log",
"maintenance_mode_active": false,
"message": "Up and running",
"nodetype": "controller",
"pid": 59196,
"pingstatus": 0,
"pingtime": 0,
"port": 9555,
"role": "controller",
"timestamp": 1501216153,
"unique_id": 2,
"uptime": 413,
"version": "1.4.3",
"wallclock": 1501216110,
"wallclocktimestamp": 1501216110
} ],
"info":
{
"cluster.status": 2,
"cluster.statustext": "Cluster started.",
"cmon.domainname": "",
"cmon.hostname": "t7500",
"cmon.running": true,
"cmon.starttime": 1501215924,
"cmon.uptime": 227,
"conf.backup_retention": 31,
"conf.clusterid": 1,
"conf.clustername": "ft_postgresql_59238",
"conf.clustertype": 5,
"conf.configfile": "/tmp/cmon_1.cnf",
"conf.hostname": "192.168.1.127",
"conf.os": "debian",
"conf.statustext": "Configuration loaded.",
"host.1.connected": true,
"host.1.cpu_io_wait_percent": 0,
"host.1.cpu_steal_percent": 0,
"host.1.cpu_usage_percent": 7.92751,
"host.1.cpucores": 16,
"host.1.cpuinfo": [
{
"class_name": "CmonCpuInfo",
"cpucores": 4,
"cpumaxmhz": 2.268e+06,
"cpumhz": 1600,
"cpumodel": "Intel(R) Xeon(R) CPU L5520 @ 2.27GHz",
"cputemp": 54.5,
"hostid": 1,
"physical_cpu_id": 0,
"siblings": 8,
"vendor": "GenuineIntel"
},
{
"class_name": "CmonCpuInfo",
"cpucores": 4,
"cpumaxmhz": 2.268e+06,
"cpumhz": 1600,
"cpumodel": "Intel(R) Xeon(R) CPU L5520 @ 2.27GHz",
"cputemp": 54.5,
"hostid": 1,
"physical_cpu_id": 1,
"siblings": 8,
"vendor": "GenuineIntel"
} ],
"host.1.cpumaxmhz": 2268,
"host.1.cpumhz": 1600,
"host.1.cpumodel": "Intel(R) Xeon(R) CPU L5520 @ 2.27GHz",
"host.1.cputemp": 54.5,
"host.1.devices": [ "/dev/mapper/core1--vg-root" ],
"host.1.free_disk_bytes": 156237512704,
"host.1.hostname": "192.168.1.177",
"host.1.interfaces": [ "eth0" ],
"host.1.ip": "192.168.1.177",
"host.1.membuffer": 0,
"host.1.memcached": 3,
"host.1.memfree": 16450528,
"host.1.memtotal": 16777216,
"host.1.network_interfaces": [
{
"interface_name": "eth0",
"rx_bytes_per_sec": 10438.1,
"tx_bytes_per_sec": 15937.2
} ],
"host.1.pingdelay": -1,
"host.1.pingstatustext": "Creating ICMP socket (to ping '192.168.1.177') failed: Operation not permitted.",
"host.1.port": 8089,
"host.1.rx_bytes_per_second": 10438.1,
"host.1.swapfree": 0,
"host.1.swaptotal": 0,
"host.1.total_disk_bytes": 208033853440,
"host.1.tx_bytes_per_second": 15937.2,
"host.1.uptime": 390,
"host.1.wallclock": 1501216175,
"host.1.wallclocksampled": 1501216110,
"host.2.class_name": "controller",
"host.2.connected": true,
"host.2.cpu_io_wait_percent": 4.14259,
"host.2.cpu_steal_percent": 0,
"host.2.cpu_usage_percent": 178.599,
"host.2.cpucores": 24,
"host.2.cpuinfo": [
{
"class_name": "CmonCpuInfo",
"cpucores": 6,
"cpumaxmhz": 2.661e+06,
"cpumhz": 2660,
"cpumodel": "Intel(R) Xeon(R) CPU X5650 @ 2.67GHz",
"cputemp": 0,
"hostid": 2,
"physical_cpu_id": 0,
"siblings": 12,
"vendor": "GenuineIntel"
},
{
"class_name": "CmonCpuInfo",
"cpucores": 6,
"cpumaxmhz": 2.661e+06,
"cpumhz": 2661,
"cpumodel": "Intel(R) Xeon(R) CPU X5650 @ 2.67GHz",
"cputemp": 0,
"hostid": 2,
"physical_cpu_id": 1,
"siblings": 12,
"vendor": "GenuineIntel"
} ],
"host.2.cpumaxmhz": 2661,
"host.2.cpumhz": 2660,
"host.2.cpumodel": "Intel(R) Xeon(R) CPU X5650 @ 2.67GHz",
"host.2.cputemp": 0,
"host.2.devices": [ "/dev/sda1" ],
"host.2.free_disk_bytes": 1404706975744,
"host.2.hostname": "192.168.1.127",
"host.2.interfaces": [ "eth0" ],
"host.2.ip": "192.168.1.127",
"host.2.membuffer": 232240,
"host.2.memcached": 13097476,
"host.2.memfree": 14108052,
"host.2.memtotal": 49453276,
"host.2.network_interfaces": [
{
"interface_name": "eth0",
"rx_bytes_per_sec": 17268.2,
"tx_bytes_per_sec": 11158.5
} ],
"host.2.pingdelay": -1,
"host.2.pingstatustext": "Creating ICMP socket (to ping '192.168.1.127') failed: Operation not permitted.",
"host.2.port": 9555,
"host.2.rx_bytes_per_second": 17268.2,
"host.2.swapfree": 0,
"host.2.swaptotal": 0,
"host.2.total_disk_bytes": 2125259440128,
"host.2.tx_bytes_per_second": 11158.5,
"host.2.uptime": 2.65439e+06,
"host.2.version": "1.4.3",
"host.2.wallclock": 1501216110,
"host.2.wallclocksampled": 1501216110,
"license.expires": -1,
"license.status": false,
"license.statustext": "No license found.",
"mail.statustext": "Created.",
"netStat.1.eth0.rxBytes": 5238632,
"netStat.1.eth0.txBytes": 33144284,
"netStat.2.eth0.rxBytes": 531337507751,
"netStat.2.eth0.txBytes": 505661940612
},
"job_statistics":
{
"by_state":
{
"ABORTED": 0,
"DEFINED": 0,
"DEQUEUED": 0,
"FAILED": 0,
"FINISHED": 0,
"RUNNING": 0
},
"class_name": "CmonJobStatistics",
"cluster_id": 1
},
"log_file": "/tmp/cmon_1.log",
"maintenance_mode_active": false,
"managed": true,
"node_auto_recovery": true,
"owner":
{
"class_name": "CmonUser",
"email_address": "[email protected]",
"first_name": "Laszlo",
"groups": [
{
"class_name": "CmonGroup",
"group_id": 4,
"group_name": "testgroup"
} ],
"last_login": "2017-07-28T04:29:14.185Z",
"last_name": "Pere",
"title": "",
"user_id": 3,
"user_name": "pipas"
},
"state": "STARTED",
"status_text": "All nodes are operational.",
"vendor": "postgres",
"version": "9.6"
} ],
"reply_received": "2017-07-28T04:29:14.197Z",
"request_created": "2017-07-28T04:29:14.193Z",
"request_id": 3,
"request_processed": "2017-07-28T04:29:14.241Z",
"request_status": "Ok",
"request_user_id": 3,
"total": 1
}

For the "getAllClusterInfo" request the controller is able to send back a lot of information. A full view of the cluster can be implemented using the reply. The following main categories can be found here:

  • cluster: A list of clusters. All the details are categorised under these CmonClusterInfo Class objects.
  • hosts: The list of hosts that are part of the cluster. This piece of information is sent if the request had the "with_hosts" property set to true.
  • info: The information that the sheet manager collected about the cluster.
  • job_statistics: Statistical information about the jobs the cluster has.
  • total: The total number of clusters managed by the controller.

createAccount

This request can be used to create an account on the cluster. Please note that this is not going to be a Cmon User, this RPC is for creating SQL users.

{
"account":
{
"class_name": "CmonAccount",
"grants": "john:ALL;pipas:INSERT",
"host_allow": "1.2.3.4",
"own_database": "laszlo",
"password": "passwd",
"user_name": "laszlo"
},
"cluster_id": 1,
"operation": "createAccount",
"request_created": "2017-01-27T14:03:09.491Z"
}
  • account: A CmonAccount class object that defines the properties of the account that will be created.

    To get a comprehensive list of the properties the controller supports in the CmonAccount class one can run the following command (or check the CmonAccount Class section):

    s9s metatype --list-properties --type=CmonAccount --long
  • cluster_id: The numerical ID of the cluster on which the account will be created. The cluster can also be identified by its name.
  • cluster_name: The name of the cluster on which the new account will be created. The cluster can also be identified by its numerical ID.

updateAccount

This request can be used to update an account on the cluster. Please note that this is not going to be a Cmon User, this RPC is for creating SQL users.

{
"account":
{
"class_name": "CmonAccount",
"grants": "john:ALL;pipas:INSERT",
"host_allow": "1.2.3.4",
"own_database": "laszlo",
"password": "passwd",
"user_name": "laszlo"
},
"cluster_id": 1,
"operation": "createAccount",
"request_created": "2017-01-27T14:03:09.491Z"
}
  • account: A CmonAccount class object that defines the properties of the account that will be updated.

    To get a comprehensive list of the properties the controller supports in the CmonAccount class one can run the following command (or check the CmonAccount Class section):

    s9s metatype --list-properties --type=CmonAccount --long
  • cluster_id: The numerical ID of the cluster on which the account will be updated. The cluster can also be identified by its name.
  • cluster_name: The name of the cluster on which the account will be updated. The cluster can also be identified by its numerical ID.

getAccounts

The getAccounts call can be used to get the accounts from the cluster. Here is how a simple call look like:

{
"cluster_id": 1,
"operation": "getAccounts",
"request_created": "2017-08-10T03:03:49.592Z",
"request_id": 3
}
  • cluster_id: The numerical ID of the cluster to get. The cluster can also be identified by its name.
  • cluster_name: The name of the cluster to get. The cluster can also be identified by its numerical ID.
  • limit: Limits the number of accounts sent back in the reply.
  • offset: The offset of the first account sent back in the reply.

Here is an example of the reply:

{
"accounts": [
{
"class_name": "CmonAccount",
"connections": 1,
"grants": "CREATE USER,CREATE DATABASE,LOGIN,REPLICATION,SUPER;template1:CREATE,TEMPORARY,CONNECT;template0:CREATE,TEMPORARY,CONNECT",
"host_allow": "",
"max_connections": 0,
"password": "",
"user_name": "postgres"
},
. . .
{
"class_name": "CmonAccount",
"grants": "LOGIN;joe:CREATE,TEMPORARY,CONNECT",
"host_allow": "",
"max_connections": 0,
"password": "",
"user_name": "joe"
} ],
"request_created": "2017-08-10T03:03:49.592Z",
"request_id": 3,
"request_processed": "2017-08-10T03:03:49.639Z",
"request_status": "Ok",
"request_user_id": 3,
"total": 5
}
  • accounts: A list of CmonAccount Class objects, the accounts found on the cluster.
  • total: The total number of accounts found on the cluster regardless the number of accounts sent back in the reply.

deleteAccount

This request is for deleting a user account from a cluster.

{
"account":
{
"class_name": "CmonAccount",
"user_name": "pipas"
},
"cluster_id": 1,
"operation": "deleteAccount",
"request_created": "2017-01-30T13:12:30.192Z"
}
  • account: The account that should be deleted. To get a comprehensive list of the properties the controller supports in the CmonAccount class one can run the following command (or check the CmonAccount Class section):
    s9s metatype --list-properties --type=CmonAccount --long
  • cluster_id: The numerical ID of the cluster from which the account should be removed. The cluster can also be identified by the cluster name.
  • cluster_name: The name of the cluster from which the account will be removed. The cluster can also be identified by its numercial ID.

grantPrivileges

The request can be used to grant privileges for a existing user to an existing database.

{
"account":
{
"class_name": "CmonAccount",
"user_name": "pipas"
},
"cluster_id": 1,
"operation": "grantPrivileges",
"privileges": "testCreateDatabase.*:DELETE,TRUNCATE",
}
  • account: The account that will be granted some privileges on some database. To get a comprehensive list of the properties the controller supports in the CmonAccount class one can run the following command (or check the CmonAccount Class section):
    s9s metatype --list-properties --type=CmonAccount --long
  • cluster_id: The numerical ID of the cluster on which the privileges will be granted. The cluster can also be identified by name.
  • cluster_name: The name of the cluster on which the privileges will be granted. The cluster can also be identified by the cluster ID.
  • privileges: The privileges to grant. This property should be a string in PDL format.
{
"messages": [ "Grant processed: GRANT DELETE,TRUNCATE ON DATABASE testCreateDatabase TO pipas" ],
"request_processed": "2017-06-22T08:49:25.548Z",
"request_status": "Ok",
"request_user_id": 3
}

revokePrivileges

The request can be used to revoke privileges from an existing user to an existing database.

{
"account":
{
"class_name": "CmonAccount",
"user_name": "pipas"
},
"cluster_id": 1,
"operation": "revokePrivileges",
"privileges": "testCreateDatabase.*:DELETE,TRUNCATE",
}
  • account: The account from privileges will be revoked on some database. To get a comprehensive list of the properties the controller supports in the CmonAccount class one can run the following command (or check the CmonAccount Class section):
    s9s metatype --list-properties --type=CmonAccount --long
  • cluster_id: The numerical ID of the cluster on which the privileges will be granted. The cluster can also be identified by name.
  • cluster_name: The name of the cluster on which the privileges will be granted. The cluster can also be identified by the cluster ID.
  • privileges: The privileges to grant. This property should be a string in PDL format.
{
"messages": [ "Revoke processed: REVOKE DELETE,TRUNCATE ON DATABASE testCreateDatabase FROM pipas" ],
"request_processed": "2017-06-22T08:49:25.548Z",
"request_status": "Ok",
"request_user_id": 3
}

revokeAllPrivileges

The request can be used to revoke all privileges from an existing user.

{
"account":
{
"class_name": "CmonAccount",
"user_name": "pipas"
},
"cluster_id": 1,
"operation": "revokePrivileges",
}
  • account: The account from all privileges will be revoked. To get a comprehensive list of the properties the controller supports in the CmonAccount class one can run the following command (or check the CmonAccount Class section):
    s9s metatype --list-properties --type=CmonAccount --long
  • cluster_id: The numerical ID of the cluster on which the privileges will be granted. The cluster can also be identified by name.
  • cluster_name: The name of the cluster on which the privileges will be granted. The cluster can also be identified by the cluster ID.
{
"messages": [ "Revoke processed: REVOKE ALL PRIVILEGES ON DATABASE testCreateDatabase FROM pipas" ],
"request_processed": "2017-06-22T08:49:25.548Z",
"request_status": "Ok",
"request_user_id": 3
}

getSqlProcesses

{
"cluster_id": 1,
"operation": "getSqlProcesses",
"request_created": "2020-02-28T06:24:36.355Z",
"request_id": 3
}
{
"debug_messages": [ "RPC V2 authenticated user is 'pipas'." ],
"processes": [
{
"blocked_by_trx_id": "",
"client": "",
"command": "Sleep",
"currentTime": 1582871076,
"db": "",
"duration": 139914683312432,
"host": "",
"hostId": 1,
"hostname": "192.168.0.227",
"info": "",
"innodb_status": "",
"innodb_trx_id": "",
"instance": "192.168.0.227:3306",
"lastseen": 139914683312176,
"message": "",
"mysql_trx_id": 1,
"pid": 1,
"query": "",
"reportTs": 1582871076,
"sql": "",
"state": "wsrep aborter idle",
"time": 664,
"user": "system user"
},
{
"blocked_by_trx_id": "",
"client": "",
"command": "Sleep",
"currentTime": 1582871076,
"db": "",
"duration": 139914683312432,
"host": "",
"hostId": 3,
"hostname": "192.168.0.220",
"info": "",
"innodb_status": "",
"innodb_trx_id": "",
"instance": "192.168.0.220:3306",
"lastseen": 139914683312176,
"message": "",
"mysql_trx_id": 5,
"pid": 5,
"query": "",
"reportTs": 1582871076,
"sql": "",
"state": "",
"time": 130,
"user": "system user"
} ],
"request_created": "2020-02-28T06:24:36.355Z",
"request_id": 3,
"request_processed": "2020-02-28T06:24:36.378Z",
"request_status": "Ok",
"request_user_id": 4
}

createDatabase

This request can be used to create a database on the cluster.

{
"cluster_id": 1,
"database":
{
"class_name": "CmonDataBase",
"database_name": "dbname"
},
"operation": "createDatabase",
"request_created": "2017-06-20T07:24:15.815Z"
}
  • cluster_id: The numerical ID of the cluster on which the new database will be created. The cluster can also be identified by its name.
  • cluster_name: The name of the cluster on which the new database will be created. The cluster can also be identified by its name.
  • database: The database to be created.
{
"database":
{
"class_name": "CmonDataBase",
"database_name": "testDatabase"
},
"messages": [ "Database 'testDatabase' created." ],
"reply_received": "2017-08-10T08:16:17.949Z",
"request_created": "2017-08-10T08:16:17.945Z",
"request_id": 3,
"request_processed": "2017-08-10T08:16:18.098Z",
"request_status": "Ok",
"request_user_id": 3
}

ping

The "ping" request can be used to check the connection to the Cmon Controller or keep it alive. Every ping request will be replied with a simple message that holds some general information about the controller and the connection.

Here is an example showing a ping request and the reply:

{
"operation": "ping",
"request_created": "2017-06-22T11:55:39.854Z",
"request_id": 3
}
{
"authenticated": true,
"package_bugreport": "[email protected]",
"package_name": "cmon",
"package_version": "1.4.3",
"request_created": "2017-06-22T11:55:39.854Z",
"request_id": 3,
"request_processed": "2017-06-22T11:55:39.900Z",
"request_status": "Ok",
"request_user_id": 12,
"rpc_version": "2.0"
}

Configuration

The /v2/config path provides access to various configuration files.

getConfig

This call is for getting the configuration of one specific node in a cluster.
The call looks like this:

{
"hostname": "192.168.1.127",
"operation": "getConfig",
"request_created": "2017-07-28T09:48:30.491Z",
"request_id": 3
}

Access rights: The authenticated user has to have read access on both the host and the cluster to get successful reply on this call.

The reply contains the configuration if the node has some configuration file(s):

{
"config":
{
"className": "CmonClusterConfig",
"files": [
{
"filename": "cmon_1.cnf",
"values": [
{
"filepath": "/tmp/cmon_1.cnf",
"linenumber": 1,
"section": "",
"value": "1",
"variablename": "cluster_id"
},
. . .
{
"filepath": "/tmp/cmon_1.cnf",
"linenumber": 29,
"section": "",
"value": "postgres",
"variablename": "vendor"
} ]
} ],
"host":
{
"class_name": "CmonHost",
"hostname": "192.168.1.127",
"port": 9555
}
},
"files": [
{
"content": "cluster_id=1\ncluster_type=postgresql_single\ncmon_db=ft_install_testdb\ncmon_user=root\ncreated_by_job=1\ngroup_owner=4\nhostname=192.168.1.127\ninit_service_name=postgresql\nlogfile=/tmp/cmon_1.log\nmode=controller\nmonitored_mountpoints=/var/lib/postgresql/9.6/main\nmysql_hostname=127.0.0.1\nmysql_password='p'\nmysql_port=3306\nname='ft_postgresql_63564'\nos=debian\nosuser=pipas\nowner=3\npidfile=/var/run\npostgresql_password=passwd12\npostgresql_server_addresses=192.168.1.178:8089\npostgresql_user=postmaster\nrepl_password=\nrepl_user=\nserver_version=9.6\nssh_identity=\nssh_port=22\ntype=postgresql_single\nvendor=postgres\n",
"filename": "cmon_1.cnf",
"path": "/tmp/cmon_1.cnf",
"syntax": "MySqlConfigSyntax"
} ],
"request_created": "2017-07-28T09:48:30.491Z",
"request_id": 3,
"request_processed": "2017-07-28T09:48:30.541Z",
"request_status": "Ok",
"request_user_id": 3
}
  • config: This contains the configuration of the host.
  • files: One host can have multiple configuration files, so this part contains a list one element for every configuration file.

    FIXME: this is weird, this should not be "files". We have an other field called files.

  • values: This is the parsed version of the configuration. Every item is one configuration value in some configuration file.
  • content: The literal content of the configuration file escaped for JSON format.
  • path: The full path of the configuration file.
  • syntax: The syntax of the configuration file. This can be GenericConfigSyntax, MySqlConfigSyntax, ProxySqlConfigSyntax, HaProxyConfigSyntax, YamlSyntax, UnknownSyntax or MongoConfigSyntax which is the same as YamlSyntax.

setConfig

This call is for changing individual configuration values for a host. Multiple values can be changed by sending one request.

Access rights: The authenticated user has to have write access on both the host and the cluster to get successful reply on this call.

Here is how the request looks like:

{
"configuration": [
{
"name": "log_line_prefix",
"value": "'%m'"
} ],
"hostname": "192.168.1.127",
"operation": "setConfig",
"port": 9555,
"request_created": "2017-07-28T12:06:53.093Z",
"request_id": 3
}
  • cluster-id: This field is not mandatory but might be used to identify the cluster. FIXME: This request does not accept "cluster_name", maybe it should.
  • configuration: A list of values to be changed.
  • hostname: The name of the host where the configuration should be changed.
  • port: If the hostname is not enough to identify the node the sender can provide the port number to identify the node. The port is not mandatory, but might be needed.

The reply will look something like this:

{
"messages": [ "Node restart is required for change to take effect." ],
"request_created": "2017-07-28T12:06:53.093Z",
"request_id": 3,
"request_processed": "2017-07-28T12:06:53.157Z",
"request_status": "Ok",
"request_user_id": 3
}
  • messages: This call can send multiple messages back. Changing the configuration is a complex step and may result in more than one messages.

FIXME: This RPC call is working, but it is not as thoroughly tested as it should be. Various config files may have different formats, quotation, sectors, one host can have multiple config files, they might include each other. These all are hard to test and we don't have all the tests we should have.

Get LDAP Configuration

This call is for reading the LDAP configuration. Access to the LDAP configuration is controlled by the access rights for the "/.runtime/LDAP/configuration" CDT entry, whoever has read access to that CDT entry can get the LDAP configuration through this call.

Here is how a request should look like:

{
"operation": "getLdapConfig",
"request_created": "2017-07-28T09:48:30.491Z",
"request_id": 3
}

And here is a reply containing the LDAP configuration.

{
"effective_privileges": "rwx",
"ldap_configuration":
{
"group_mapping":
{
"cn=ldapgroup,dc=homelab,dc=local":
{
"create_cmon_group": false,
"ldap_group": "cn=ldapgroup,dc=homelab,dc=local"
},
"cn=other,dc=homelab,dc=local":
{
"cmon_group": "others",
"create_cmon_group": true,
"ldap_group": "cn=other,dc=homelab,dc=local"
},
"dc=homelab,dc=local":
{
"cmon_group": "ldap",
"create_cmon_group": true,
"ldap_group": "dc=homelab,dc=local"
}
},
"ldap_admin_dn": "cn=admin,dc=homelab,dc=local",
"ldap_admin_pwd": "p",
"ldap_base_dn": "dc=homelab,dc=local",
"ldap_groups_dn": "",
"ldap_groups_is_recursive": true,
"ldap_is_enabled": true,
"ldap_network_timeout": 5,
"ldap_protocol_version": 3,
"ldap_server_uri": "ldap://192.168.0.167:389",
"ldap_time_limit": 5,
"ldap_timeout": 5,
"ldap_tls_cert_file": "",
"ldap_tls_key_file": "",
"ldap_users_dn": "",
"ldap_users_is_recursive": true
},
"request_processed": "2019-05-07T18:52:43.183Z",
"request_status": "Ok"
}
  • effective_privileges: This field shows what access privileges the authenticated user has. So with this field the caller can immediately decide if the user has the rights to change the settings or not.
  • ldap_configuration: The LDAP configuration. The properties here have the same names that are used in the ldap configuration file. These are documented in the cmon-ldap.cnf(5) man page.

Set LDAP Configuration

This call is for writing the LDAP configuration. Access to the LDAP configuration is controlled by the access rights for the "/.runtime/LDAP/configuration" CDT entry, whoever has write access to that CDT entry can set the LDAP configuration through this call.

Here is how a request should look like:

{
"ldap_configuration":
{
"group_mapping":
{
"cn=ldapgroup,dc=homelab,dc=local":
{
"create_cmon_group": false,
"ldap_group": "cn=ldapgroup,dc=homelab,dc=local"
},
"cn=other,dc=homelab,dc=local":
{
"cmon_group": "others",
"create_cmon_group": true,
"ldap_group": "cn=other,dc=homelab,dc=local"
},
"dc=homelab,dc=local":
{
"cmon_group": "ldap",
"create_cmon_group": true,
"ldap_group": "dc=homelab,dc=local"
}
},
"ldap_admin_dn": "cn=admin,dc=homelab,dc=local",
"ldap_admin_pwd": "p",
"ldap_base_dn": "dc=homelab,dc=local",
"ldap_is_enabled": true,
"ldap_network_timeout": 5,
"ldap_protocol_version": 3,
"ldap_server_uri": "ldap://192.168.0.167:389",
"ldap_time_limit": 5,
"ldap_timeout": 5,
"ldap_tls_cert_file": "",
"ldap_tls_key_file": ""
},
"operation": "setLdapConfig",
"request_created": "2017-07-28T09:48:30.491Z",
"request_id": 3
}
  • ldap_configuration: The LDAP configuration. The properties here have the same names that are used in the ldap configuration file. These are documented in the cmon-ldap.cnf(5) man page. This has the same format as the getLdapConfig call.
  • group-mapping: This is of course not mandatory. The only mandatory fields are "ldap_server_uri", "ldap_base_dn", "ldap_admin_dn" and "ldap_admin_pwd" when the LDAP is enabled, "ldap_is_enabled" when it is disabled.

And here is a reply:

{
"request_processed": "2019-05-08T09:04:35.381Z",
"request_status": "Ok"
}

Controllers and HA

The "/v2/controller" path is for managing controller and operate the Cmon HA subsystem.

ping

This call is created so that the client application can check on a controller even when that controller is not the leader. This call should be as fast as possible and should not do any locking on the Cmon HA subsystem.

{
"operation": "ping",
"request_created": "2020-01-02T10:01:08.035Z",
"request_id": 1
}
{
"cmon_ha_supported": true,
"controller_status": "CmonControllerLeader",
"current_term": 0,
"last_committed_index": 0,
"last_committed_term": 0,
"request_created": "2020-01-02T10:01:08.035Z",
"request_id": 1,
"request_processed": "2020-01-02T10:01:08.044Z",
"request_status": "Ok"
}

JoinRequest

HeartBeat

{
"client_ip_address": "192.168.0.127",
"controller_key": "6a03558f-5d7a-41ed-9af1-9e0ff162bcb8",
"failed_controllers":
{
},
"follower_controllers":
{
"192.168.0.127:10001":
{
"class_name": "CmonController",
"hostname": "192.168.0.127",
"ip": "192.168.0.127",
"port": 10001
},
"192.168.0.127:20001":
{
"class_name": "CmonController",
"hostname": "192.168.0.127",
"ip": "192.168.0.127",
"port": 20001
}
},
"last_committed_index": 6,
"leader_controller":
{
"class_name": "CmonController",
"hostname": "192.168.0.127",
"ip": "192.168.0.127",
"port": 9556
},
"operation": "heartBeat",
"request_created": "2019-03-19T09:43:58.444Z",
"term": 0
}
{
"cmon_ha_supported": true,
"pid": 41918,
"request_created": "2019-03-19T09:43:58.444Z",
"request_processed": "2019-03-19T09:43:58.489Z",
"request_status": "Ok",
"wallclock": 1552988638
}

appendEntries

{
"client_ip_address": "192.168.0.127",
"controller_key": "6a03558f-5d7a-41ed-9af1-9e0ff162bcb8",
"log_entry":
{
"class_name": "CmonHaLogEntry",
"comment": "",
"committed": false,
"executed": false,
"index": 11,
"last_committed_index": 10,
"last_committed_term": 0,
"payload":
{
"files": [
{
"class_name": "CmonFile",
"content": "basedir=/usr\ncdt_path=/\ncluster_id=1\ncmon_db=ft_install_testdb\ncmon_user=root\ncreated_by_job=1\ndb_schema_stats_collection_interval=10800\nenable_is_queries=1\nenable_query_monitor=1\ngalera_version=3.x\ngroup_owner=1\nhostname=192.168.0.127\nlogfile=/tmp/cmon_1.log\nmode=controller\nmonitored_mountpoints=/var/lib/mysql/\nmonitored_mysql_port=3306\nmonitored_mysql_root_password='password'\nmysql_bindir=/usr/bin/\nmysql_hostname=127.0.0.1\nmysql_password='p'\nmysql_port=3306\nmysql_server_addresses=192.168.0.232:3306\nmysql_version=5.6\nname='ft_cmonhabasic_41388'\nos=debian\nowner=1\npidfile=/var/run\nquery_monitor_use_local_settings=false\nssh_identity=\nssh_port=22\nssh_user=pipas\nsudo_opts=sudo -n 2>/dev/null\ntype=galera\nvendor=percona\n",
"full_path": "/tmp/cmon_1.cnf"
} ],
"instruction": "SaveLocalFile"
},
"term": 0
},
"operation": "appendEntries",
"request_created": "2019-03-19T09:48:32.864Z"
}
{
"request_created": "2019-03-19T09:48:32.864Z",
"request_processed": "2019-03-19T09:48:32.909Z",
"request_status": "Ok"
}

requestVote

{
"client_ip_address": "192.168.0.127",
"last_committed_index": 23,
"last_committed_term": 42,
"operation": "requestVote",
"request_created": "2019-02-28T07:32:22.095Z",
"term": 43
}

Discovery

The /v2/discovery path provides a set of calls helping the UI to check various input values during the interaction with the user.

checkClusterName

This call can be used to check if a specific cluster name is acceptable to be used to create a new cluster.

{
"new_cluster_name": "Cluster_01",
"operation": "checkClusterName",
"request_created": "2017-08-15T09:32:29.823Z",
"request_id": 3
}
  • new_cluster_name: The new cluster name the user plans to use to name a new cluster.
{
"is_acceptable": true,
"message": "The cluster name 'Cluster_01' is acceptable.",
"request_created": "2017-08-15T09:32:29.823Z",
"request_id": 3,
"request_processed": "2017-08-15T09:32:29.868Z",
"request_status": "Ok",
"request_user_id": 3
}
  • is_acceptable: Shows if the cluster name is acceptable as a name for a new cluster. If the name is not valid for this purpose (e.g. the name already used for some other cluster) this value will be false.
  • message: Shows why the name is not acceptable. This string is human readable, it can be shown to the user.
  • request_status: Please note that the request status will be "Ok" even if the reply is negative, when the new name is not acceptable. This field shows if the request is processed, it is not the reply.

getSupportedClusterTypes

The getSupportedClusterTypes returns some metadata about the cluster types supported by the controller. This is a very important call, using it the GUI can fill up comboboxes, lists and can make sure that the cluster creation will not fail because the user requested some invalid combination. It is also worth mentioning that if the UI uses this call the UI source doesn't need to be changed when we start supporting a new version or vendor.

Here is how the call look like:

{
"operation": "getSupportedClusterTypes",
"request_created": "2017-08-15T08:37:30.150Z",
"request_id": 3
}

And here is the reply. Please note that this call is under construction, we might add new fields if we find some other important information.

{
"cluster_type_names": [ "galera", "replication", "groupreplication", "myqlcluster", "postgresql", "mongodb" ],
"cluster_type_properties":
{
"galera":
{
"intro_url": "http://galeracluster.com/products/",
"long_name": "Galera Cluster for MySQL",
"short_name": "Galera",
"type_name": "galera",
"vendors": [
{
"name": "mariadb",
"versions": [ "5.5", "10.1", "10.2" ]
},
{
"name": "codership",
"versions": [ "5.5", "5.6" ]
},
{
"name": "percona",
"versions": [ "5.5", "5.6", "5.7" ]
} ]
},
"groupreplication":
{
"intro_url": "http://mysqlhighavailability.com/gr/doc/index.html",
"long_name": "MySQL Group Replication",
"short_name": "Group Replication",
"type_name": "groupreplication",
"vendors": [
{
"name": "oracle",
"versions": [ "5.7" ]
} ]
},
"mongodb":
{
"intro_url": "https://www.mongodb.com/what-is-mongodb",
"long_name": "MongoDB Cluster",
"short_name": "MongoDB",
"type_name": "mongodb",
"vendors": [
{
"name": "10gen",
"versions": [ "3.2", "3.4" ]
},
{
"name": "percona",
"versions": [ "3.2" ]
} ]
},
"myqlcluster":
{
"long_name": "MySQL NDB Cluster",
"nick_name": "ndb",
"short_name": "NDB Cluster",
"type_name": "myqlcluster",
"vendors": [
{
"name": "oracle",
"versions": [ "7.4" ]
} ]
},
"postgresql":
{
"intro_url": "https://www.postgresql.org/about/",
"long_name": "PostgreSQL Cluster",
"short_name": "PostgreSQL",
"type_name": "postgresql",
"vendors": [
{
"name": "postgresql",
"versions": [ "9.4", "9.5", "9.6", "10" ]
} ]
},
"replication":
{
"long_name": "MySQL Replication",
"nick_name": "mysqlreplication",
"short_name": "Replication",
"type_name": "replication",
"vendors": [
{
"name": "mariadb",
"versions": [ "5.5", "10.1", "10.2" ]
},
{
"name": "codership",
"versions": [ "5.5", "5.6" ]
},
{
"name": "percona",
"versions": [ "5.5", "5.6", "5.7" ]
} ]
}
},
"request_created": "2017-08-15T08:37:30.150Z",
"request_id": 3,
"request_processed": "2017-08-15T08:37:30.200Z",
"request_status": "Ok",
"request_user_id": 3,
"total": 6
}

getSshCredentials

Using the getSshCredentials the caller can get the SSH credentials used when the controller authenticates on the nodes of the cluster. This cluster is made for the UI that creates a new cluster: here the user can check and re-use the SSH credentials used on some existing cluster.

{
"cluster_id": 1,
"operation": "getSshCredentials",
"request_created": "2017-08-14T08:25:20.298Z",
"request_id": 3
}
  • cluster_id: The numerical ID of the cluster to read. The cluster name also can be used to identify the cluster.
  • cluster_name: The name of the cluster to read. The cluster ID can also be used to identify the cluster.

The privilege check provides access if the authenticated user is either the system user or the owner of the existing cluster. No ACLs are implemented and other than the owner can't access the sensitive information of the existing clusters. FIXME: This needs a fix.

A successful reply will look like this:

{
"cluster_id": 1,
"request_created": "2017-08-14T08:25:20.298Z",
"request_id": 3,
"request_processed": "2017-08-14T08:25:20.344Z",
"request_status": "Ok",
"request_user_id": 3,
"ssh_credentials":
{
"class_name": "CmonSshCredentials",
"password": "",
"port": 22,
"public_key_file": "./configs/testing_id_rsa",
"timeout": 30,
"tty_for_sudo": true,
"user_name": "pipas"
}
}
  • cluster_id: The numerical ID of the cluster is always sent back even if the cluster was identified by its name in the request.
  • ssh_credentials: The credentials used to authenticate on the nodes of the cluster.

checkHosts

The checkHosts request performs a set of tests on the given hosts and returns various hardware information about the hosts themselves. The call can specify what kind of tests it needs at the given stage, e.g. if the user already provided an ssh password the access to the host through SSH can be checked.

Here is how these checks can be performed using the s9s command line tool:

s9s cluster --check-hosts --nodes="192.168.1.113" --cluster-type=galera --provider-version=5.7 --vendor=codership

Here is an example of the request:

{
"check_if_already_registered": true,
"nodes": [
{
"class_name": "CmonHost",
"hostname": "192.168.1.212",
"port": 8089
} ],
"operation": "checkHosts",
"request_created": "2017-08-11T12:32:58.547Z",
"request_id": 3
}
  • check_if_already_registered: If true the controller will check if the hosts are already registered for other clusters.

    If the port is set the code will check if the same host with the same name is already registered in some other cluster. The same port on the same host can't be used twice and the test will check this.

    If the class_name is set the backend will perform an other check. We do not support running two instances of the same daemon on one single host, so if for example a mysqld daemon is running on a host we can't put an other mysqld daemon to the same host even if we use a different port number. So if the class name is set in the request the backend checks if the host is already registered with the same purpose/class. Currently the following class names are supported:

    • CmonHost: A generic host. Use this if there is no information about what role the user plans for the host and no check of host type will be performed.
    • CmonMySqlHost: For hosts that will hold a MySQL daemon.
    • CmonPostgreSqlHost: For hosts that will hold a PostgreSql daemon.
    • CmonProxySqlHost: For hosts that will be holding a ProxySql installation.
    • CmonHaProxyHost: For HAProxy hosts.
  • check_ssh_sudo: This check will see that the host is actually accessible through ssh as superuser. For this test either the SSH credentials or an existing cluster must be specified.
  • check_job: When this property is true the caller can send a "job" field with the "create_cluster" job for a preliminary check. The job will not be of course executed, but some checks will be performed to see if the job with the passed properties is valid.
  • cluster_id: The cluster can be specified through the numerical ID of the cluster. If the cluster is specified the SSH credentials can be accessed from the cluster configuration, so the user don't need to enter those. If the cluster is not specified the user has to enter the information needed for the SSH access to perform ssh test.
  • cluster_name: Same as cluster_id, but here the cluster can be referenced by the cluster name.
  • ssh_credentials: Fields providing SSH access to the nodes. The SSH credentials can come from the existing cluster and from this field, but this field has precedence.

Here is an other request that is slightly different. Note that this request will not check if the host is already registered simple because the "check_if_already_registered" field is not set to true. This reques does not indentify an existing cluster either, it contains the "ssh_credentials" field so that the controller can gain access to the nodes. Should the cluster be also referenced the "ssh_credentials" have priority over the credentials found in an existing cluster.

{
"check_ssh_sudo": true,
"nodes": [
{
"class_name": "CmonHost",
"hostname": "192.168.1.113",
"port": 8089
} ],
"operation": "checkHosts",
"request_created": "2017-08-14T10:48:50.978Z",
"request_id": 4,
"ssh_credentials":
{
"class_name": "CmonSshCredentials",
"password": "",
"public_key_file": "./configs/testing_id_rsa",
"user_name": "pipas"
}
}

And here is a call that will also check the passed "create_cluster" job:

{
"check_if_already_registered": true,
"check_job": true,
"check_ssh_sudo": true,
"job":
{
"class_name": "CmonJobInstance",
"job_spec":
{
"command": "create_cluster",
"job_data":
{
"cluster_type": "group_replication",
"mysql_version": "",
"nodes": [
{
"class_name": "CmonHost",
"hostname": "192.168.1.113"
} ],
"vendor": ""
}
}
},
"nodes": [
{
"class_name": "CmonHost",
"hostname": "192.168.1.113"
} ],
"operation": "checkHosts",
"request_created": "2017-10-10T07:55:59.653Z",
"request_id": 3
}

Here is how a simple reply telling that the tests are all passed looks like:

{
"checked_hosts": [
{
"host":
{
"class_name": "CmonHost",
"hostname": "192.168.1.112",
"port": 8089
},
"status":
{
"error_code": "HostIsOk"
}
} ],
"request_created": "2017-08-11T13:49:10.052Z",
"request_id": 3,
"request_processed": "2017-08-11T13:49:10.097Z",
"request_status": "Ok",
"request_user_id": 3,
"total": 1,
"total_failed": 0
}
  • checked_hosts: The detailed list with the hosts checked. Every host has the status showing what was the result of the check on that particular host.
  • total: The toal number of hosts checked.
  • total_failed: The number of hosts that failed the checks. This can be used as a quick checks, but the details of the failure is in the reply too.

The "error_code" field of the "status" can be one of the following strings:

  • HostFoundInOtherCluster: The host is already part of some other cluster.
  • SshConnectionFailed: The SSH connection failed.
  • SshAuthenticationFailed: The SSH connected, but the authentication failed.
  • SudoFailed: Failed to gain root access.
  • InvalidRequest: The request is invalid, check could not be performed.
  • JobWouldFail: The job would fail if it is sent seriously.
  • HostIsOk: The host passed all tests.

When the "check_ssh_sudo" test is performed the reply shows some information about the hosts checked. Please note that these tests are under development, later more information might be added.

{
"checked_hosts": [
{
"hardware":
{
"cpu":
{
"class_name": "CmonCpuSet",
"cpus": [
{
"cores": 4,
"cpu_max_ghz": 2.268,
"id": 0,
"model": "Intel(R) Xeon(R) CPU L5520 @ 2.27GHz",
"siblings": 8,
"vendor": "GenuineIntel"
},
{
"cores": 4,
"cpu_max_ghz": 2.268,
"id": 1,
"model": "Intel(R) Xeon(R) CPU L5520 @ 2.27GHz",
"siblings": 8,
"vendor": "GenuineIntel"
} ],
"total_cores": 8,
"total_cpus": 2,
"total_siblings": 16
},
"memory":
{
"class_name": "CmonMemoryInfo",
"memory_available_mb": 16085,
"memory_free_mb": 16085,
"memory_total_mb": 16384,
"swap_free_mb": 0,
"swap_total_mb": 0
},
"mounted_partitions": [
{
"device": "/dev/mapper/core1--vg-root",
"filesystem": "ext4",
"free_gb": 142.631,
"free_mb": 146053,
"mountpoint": "/",
"total_gb": 162.286,
"total_mb": 166180
} ],
"network_interfaces":
{
"eth0": "192.168.1.113"
}
},
"host":
{
"class_name": "CmonHost",
"hostname": "192.168.1.113",
"port": 8089
},
"software":
{
"firewall":
{
"firewalld_installed": false,
"ip_tables_installed": false,
"iptables_installed": false,
"ufw_installed": false
},
"os_version":
{
"class_name": "CmonOsVersion",
"codename": "xenial",
"name": "ubuntu",
"release": "16.04",
"type": "debian"
}
"security":
{
"apparmor_installed": false,
"selinux_installed": false,
"sudo_installed": true
}
},
"status":
{
"error_code": "HostIsOk"
}
} ],
"request_created": "2017-08-16T10:21:19.122Z",
"request_id": 4,
"request_processed": "2017-08-16T10:21:20.571Z",
"request_status": "Ok",
"request_user_id": 3,
"total": 1,
"total_failed": 0
}
  • hardware/cpu: Some information about the CPU(s) found in the host. This can be shown to the user te help recognize the hardware. A total can also be calculated for the cluster so that the user can see whet resources the cluster will have.
  • hardware/memory: Information about the memory configuration.
  • hardware/mounted_partitions: Very important piece of information that shows the available storage capacity.
  • hardware/network_interfaces: This actually could show more, like we could show the type of connection.
  • software/firewall: Shows what firewall software is installed. FIXME: we could show if we actually have iptables rules or even the rules themselves. The user has to decide if the firewall should be disabled or not and we are not helping much with this decision.
  • software/os_version: The distribution and its version. FIXME: This '/' looks weird.

Well, it is clear, we need to send more info, but the basics are there. Takes a lot of work add new things here.

Hosts and Containers

The "/v2/host" path is for managing servers, hosts and virtual machines or containers.

startServers

This call is for starting up (or booting) servers.

{
"operation": "startServers",
"request_created": "2017-10-16T07:48:21.779Z",
"request_id": 3,
"servers": [
{
"class_name": "CmonContainerServer",
"hostname": "host04"
} ]
}
{
"messages": [ "Started server 'host04'." ],
"request_created": "2017-10-16T07:48:21.779Z",
"request_id": 3,
"request_processed": "2017-10-16T07:48:21.943Z",
"request_status": "Ok",
"request_user_id": 3
}

shutDownServers

This call is for shut down up (power off) servers.

registerServers

This call can be used to register a container server in the Cmon Controller. This is the fast way, but there is a job called "create_server" that does a very similar registration. The job will actually install software, this call just registers an existing server.

Here is how a request will be sent:

{
"operation": "registerServers",
"request_created": "2017-08-31T07:53:41.158Z",
"request_id": 3,
"servers": [
{
"class_name": "CmonContainerServer",
"cdt_path": "myservers",
"hostname": "storage01"
} ]
}

Here is a reply that shows the server was registered, but the server is actually turned off:

{
"reply_received": "2017-10-16T11:33:24.501Z",
"request_created": "2017-10-16T11:33:24.496Z",
"request_id": 3,
"request_processed": "2017-10-16T11:33:27.606Z",
"request_status": "Ok",
"request_user_id": 3,
"servers": [
{
"cdt_path": "/",
"class_name": "CmonContainerServer",
"clusterid": 0,
"connected": false,
"hostId": 6,
"hostname": "storage01",
"hoststatus": "CmonHostOffLine",
"ip": "192.168.1.17",
"maintenance_mode_active": false,
"message": "SSH connection failed.",
"owner_group_id": 2,
"owner_group_name": "users",
"owner_user_id": 3,
"owner_user_name": "pipas",
"protocol": "lxc",
"timestamp": 1508153607,
"unique_id": 6
} ]
}

unregisterHost

This call will drop the given host from our database entirely. The computer the host represents will not be touched in any way, software will not be uninstalled, service will not be stopped, nada!

{
"cluster_id": 1,
"dry_run": true,
"host":
{
"class_name": "CmonHost",
"hostname": "192.168.1.127",
"port": 9555
},
"operation": "unregisterHost",
"request_created": "2017-09-08T09:42:49.137Z",
"request_id": 3
}
  • cluster_id: The numerical ID of the cluster that will execute the job. The cluster can also be referenced using the cluster name. For RPC v2 identifying the cluster is not mandatory, but it can be usefull if the same host is part of multiple clusters.
  • cluster_name: The name of the cluster that will execute the job. The cluster can also be referenced using the cluster ID. For RPC v2 identifying the cluster is not mandatory, but it can be usefull if the same host is part of multiple clusters.
  • dry_run: If this field is provided (with any value at all) the host will not be really unregistered. All the checks will be done, a success will be reported back, only the error message will show that this was just a drill.
  • host: The host that shall be unregistered.
  • class_name: The class name of the host will not be strictly checked (because the client might not know this), just send "CmonHost" that'll do.
  • hostname: This is mandatory to identify the host.
  • port: The port of the host. RPC v2 allows the usage of hosts without a port, but for most cases the port will be required.

So there are multiple fields to identify the host, there is teh hostname, the port, the cluster ID, maybe the cluster name. The backend will use whatever it is provided and find the host. If multiple hosts are found with the given data the request will be rejected.

Here is what we get for a dry run:

{
"error_string": "Dry run was requested, not unregistering host.",
"request_created": "2017-09-08T09:42:49.137Z",
"request_id": 3,
"request_processed": "2017-09-08T09:42:49.183Z",
"request_status": "Ok",
"request_user_id": 3
}

unregisterServers

This call is for dropping a container server from the scope of the controller. The server will not seize to exist, but the controller will forget everything about it.

{
"operation": "unregisterServers",
"request_created": "2017-09-01T08:42:22.756Z",
"request_id": 3,
"servers": [
{
"class_name": "CmonContainerServer",
"hostname": "core1"
} ]
}
  • servers: A list of CmonContainerServer class objects to unregister. In the objects only the class name and the host name has to be provided.
{
"messages": [ "Unregistered server 'core1'." ],
"request_created": "2017-09-01T08:42:22.756Z",
"request_id": 3,
"request_processed": "2017-09-01T08:42:22.810Z",
"request_status": "Ok",
"request_user_id": 3
}

getContainers

Returns the list of containers known by the controller. The RPC v2 call also checks the rights of the authenticated user, if the user has no right to read the container object it will not appear in the list.

{
"operation": "getContainers",
"request_created": "2017-09-07T05:09:16.895Z",
"request_id": 3
}
{
"containers": [
{
"alias": "debian",
"class_name": "CmonContainer",
"hostname": "debian",
"owner_group_id": 4,
"owner_group_name": "testgroup",
"owner_user_id": 3,
"owner_user_name": "pipas",
"parent_server": "storage01",
"status": "STOPPED",
"type": "lxc"
},
. . .
{
"alias": "vnc_server",
"class_name": "CmonContainer",
"hostname": "192.168.1.210",
"ip": "192.168.1.210",
"ipv4_addresses": [ "192.168.1.210" ],
"owner_group_id": 4,
"owner_group_name": "testgroup",
"owner_user_id": 3,
"owner_user_name": "pipas",
"parent_server": "storage01",
"status": "RUNNING",
"template": "ubuntu",
"type": "lxc"
} ],
"request_created": "2017-09-07T05:09:16.895Z",
"request_id": 3,
"request_processed": "2017-09-07T05:09:16.947Z",
"request_status": "Ok",
"request_user_id": 3,
"total": 5
}
  • containers: A list of #CmonContainer objects. The following command can be used to print the properties of this class:
    s9s metatype --list-properties --type=CmonContainer --long
  • total: The total number of containers known by the controller.

getServers

Returns the container servers and their properties. Here is a request:

{
"operation": "getServers",
"request_created": "2017-10-03T08:19:58.769Z",
"request_id": 3
}

And here is the reply. It is rather complex, but we have a lot of data.

{
"request_created": "2017-10-03T08:19:58.769Z",
"request_id": 3,
"request_processed": "2017-10-03T08:19:58.824Z",
"request_status": "Ok",
"request_user_id": 3,
"servers": [
{
"cdt_path": "/",
"class_name": "CmonContainerServer",
"clusterid": 0,
"connected": false,
"containers": [
{
"acl": "user::rwx,user:nobody:r--,group::rw-,other::---",
"alias": "bestw_controller",
"cdt_path": "/core1/containers",
"class_name": "CmonContainer",
"container_id": 1,
"hostname": "192.168.1.182",
"ip": "192.168.1.182",
"ipv4_addresses": [ "192.168.1.182" ],
"owner_group_id": 2,
"owner_group_name": "users",
"owner_user_id": 3,
"owner_user_name": "pipas",
"parent_server": "core1",
"status": "RUNNING",
"type": "lxc",
"version": 25
},
. . .
{
"acl": "user::rwx,group::rw-,other::---",
"alias": "ubuntu",
"cdt_path": "/core1/containers",
"class_name": "CmonContainer",
"container_id": 5,
"hostname": "ubuntu",
"owner_group_id": 2,
"owner_group_name": "users",
"owner_user_id": 3,
"owner_user_name": "pipas",
"parent_server": "core1",
"status": "STOPPED",
"type": "lxc",
"version": 25
} ],
"disk_devices": [
{
"class_name": "CmonDiskDevice",
"device": "/dev/mapper/core1--vg-root",
"filesystem": "ext4",
"free_mb": 141617,
"mountpoint": "/",
"total_mb": 166180
},
. . .
{
"device": "/dev/sdc",
"is_hardware_storage": false,
"model": "LSILOGIC Logical Volume",
"total_mb": 170230,
"volumes": [
{
"description": "Linux filesystem partition",
"device": "/dev/sdc1",
"filesystem": "ext2",
"free_mb": 295,
"mount_point": "/boot",
"total_mb": 487
},
{
"description": "Extended partition",
"device": "/dev/sdc2",
"filesystem": "",
"mount_point": "",
"total_mb": 169741,
"volumes": [
{
"description": "Linux LVM Physical Volume partition",
"device": "/dev/sdc5",
"filesystem": "",
"mount_point": "",
"total_mb": 169741
} ]
} ]
},
. . .
{
"device": "/dev/sda",
"is_hardware_storage": false,
"model": "SCSI Disk",
"total_mb": 15272.1,
"volumes": [
{
"description": "EXT4 volume",
"device": "/dev/sda1",
"filesystem": "ext4",
"mount_point": "",
"total_mb": 15271.1
} ]
} ],
"distribution":
{
"codename": "xenial",
"name": "ubuntu",
"release": "16.04",
"type": "debian"
},
"hostId": 1,
"hostname": "core1",
"hoststatus": "CmonHostOffLine",
"ip": "192.168.1.4",
"last_container_collect": 1506791691,
"last_hw_collect": 1507017024,
"lastseen": 1506791691,
"maintenance_mode_active": false,
"memory":
{
"banks": [
{
"bank": "0",
"name": "DIMM 800 MHz (1.2 ns)",
"size": 4294967296
},
. . .
{
"bank": "7",
"name": "DIMM 800 MHz (1.2 ns)",
"size": 4294967296
} ],
"class_name": "CmonMemoryInfo",
"memory_available_mb": 54091,
"memory_free_mb": 41919,
"memory_total_mb": 64421,
"swap_free_mb": 0,
"swap_total_mb": 0
},
"message": "SSH connection failed.",
"model": "SUN FIRE X4170 SERVER (4583256-1)",
"network_interfaces": [
{
"gbits": 1,
"link": true,
"mac": "00:21:28:76:06:2a",
"model": "82575EB Gigabit Network Connection",
"name": "enp1s0f0"
},
. . .
{
"gbits": 0,
"ip": "192.168.122.1",
"mac": "",
"model": "",
"name": "virbr0"
} ],
"owner_group_id": 2,
"owner_group_name": "users",
"owner_user_id": 3,
"owner_user_name": "pipas",
"processors": [
{
"cores": 4,
"cpu_max_ghz": 1.6,
"id": 5,
"model": "Intel(R) Xeon(R) CPU L5520 @ 2.27GHz",
"siblings": 8,
"vendor": "Intel Corp."
},
{
"cores": 4,
"cpu_max_ghz": 1.6,
"id": 9,
"model": "Intel(R) Xeon(R) CPU L5520 @ 2.27GHz",
"siblings": 8,
"vendor": "Intel Corp."
} ],
"timestamp": 1507017024,
"unique_id": 1,
"version": "2.17"
},
. . .
} ],
"total": 8
}

Job Information and Manipulation

Creating and manipulating jobs, reading information about jobs is available on the /v2/jobs path.

createJobInstance

Arguably the most important call of the whole RPC 2.0 is the "createJobInstance" call where new jobs can be started. Jobs are very powerfull and complex and so this RPC call is very important and very often used.

The request contains a job description that will be created as a new job and then the new job will be described in detail returned in the reply. This way the caller immediately know what properties the controller added to the new job.

{
"cluster_id": 1,
"job":
{
"class_name": "CmonJobInstance",
"job_spec":
{
"command": "rolling_restart"
},
"title": "Rolling Restart",
},
"operation": "createJobInstance",
"request_created": "2017-06-22T12:46:05.054Z",
"request_id": 3
}
  • cluster_id: The numerical ID of the cluster that will execute the job. The cluster can also be referenced using the cluster name.
  • cluster_name: The name of the cluster that will execute the job. The cluster can also be referenced using the cluster ID.
  • job: The job that will be created. Here the caller can set various properties while other properties will be set by the controller automatically. The most important properties the caller can send are the following:

    • class_name: This should always be "CmonJobInstance".
    • job_spec: Speicifies what the job will do.
    • recurrence: A crontab string that describes when the job should be repeated. When this is provided a recurring (repeated) job will be created.
    • scheduled: If this date and time is specified the job will be executed at a fixed time later. Recurring jobs are scheduled by the recurrence, so a job can not contain both the recurrence and the scheduled properties simultaneously.
    • title: The human readable title of the job. If this property is not specified the controller will choose a title that is appropriate.

    To get a comprehensive list of the properties the controller supports one can run the following command (or check the The Job Related Structures page):

    s9s metatype --list-properties --type=CmonJobInstance --long

Here is an example showing a reply to the "createJobInstance" request:

{
"job":
{
"can_be_aborted": false,
"can_be_deleted": true,
"class_name": "CmonJobInstance",
"cluster_id": 1,
"created": "2017-06-22T12:46:05.000Z",
"exit_code": 0,
"group_id": 2,
"group_name": "users",
"job_id": 2,
"job_spec":
{
"command": "rolling_restart"
},
"status": "DEFINED",
"status_text": "Waiting",
"title": "Rolling Restart",
"user_id": 3,
"user_name": "pipas"
},
"request_created": "2017-06-22T12:46:05.054Z",
"request_id": 3,
"request_processed": "2017-06-22T12:46:05.105Z",
"request_status": "Ok",
"request_user_id": 3
}

Here is an example that registers a recurring job:

{
"cluster_id": 1,
"job":
{
"class_name": "CmonJobInstance",
"job_spec":
{
"command": "success",
"job_data":
{
}
},
"recurrence": "2 * * * *",
"tags": [ "testRecurringJob" ],
"title": "Simulated Success"
},
"operation": "createJobInstance",
"request_created": "2019-11-06T06:16:23.859Z",
"request_id": 3
}

deleteJobInstance

{
"job_id": 4,
"operation": "deleteJobInstance",
"request_created": "2017-10-20T12:55:08.277Z",
"request_id": 3
}
{
"request_created": "2017-10-20T12:55:08.277Z",
"request_id": 3,
"request_processed": "2017-10-20T12:55:08.335Z",
"request_status": "Ok",
"request_user_id": 3
}

killJobInstance

This call was made for killing/aborting jobs running on the controller. To be more precise killing a Cmon job works like killing a UNIX process (it is not a real UNIX process though): we deliver a signal, the job code recognizes the job signal and aborts itself. In short we could say this is not preemptive, jobs that don't want to be aborted will not be aborted.

Here is a request to abort a job:

{
"job_id": 1,
"operation": "killJobInstance",
"request_created": "2019-05-22T06:41:18.944Z",
"request_id": 3,
"signal": 15
}
  • job_id: The numerical ID of the job that should receive the signal.
  • signal: The signal itself as a single integer number. In the current implementation there is very little difference between the various signals, it is a good practice to send 15 now.

Here is the reply, it is just a notification about the signal being delivered. The job will be aborted later (if the job supports aborting at all).

{
"request_created": "2019-05-22T06:41:18.944Z",
"request_id": 3,
"request_processed": "2019-05-22T06:41:18.953Z",
"request_status": "Ok",
"request_user_id": 4
}

cloneJobInstance

This call will clone (copy) an existing job instance in order to execute it again. All the properties are going to be inherited from the existing job and the newly created copy will be executed the same way new jobs are always executed. Here is how the request looks like:

{
"cluster_id": 1,
"job_id": 2,
"operation": "cloneJobInstance",
"request_created": "2018-09-19T12:01:57.142Z",
"request_id": 3
}

The following fields areimportant:

  • job_id: The ID of the job that will be cloned. The original job.
  • cluster-id: If this field is provided the job will be executed by this cluster. If this field is not provided the job will be executed by the same cluster that executed the original job.

getJobInstance

This call is very similar to the "getJobInstances" call, but this one returns information about one job.

{
"job_id": 1,
"operation": "getJobInstance",
"request_created": "2017-06-23T08:29:47.266Z",
"request_id": 4
}
  • job_id: The numerical ID of the job that should be returned.
{
"job":
{
"can_be_aborted": false,
"can_be_deleted": false,
"class_name": "CmonJobInstance",
"cluster_id": 0,
"created": "2017-06-23T08:29:19.000Z",
"exit_code": 0,
"group_id": 2,
"group_name": "users",
"has_progress": true,
"job_id": 1,
"job_spec":
{
"command": "create_cluster",
"job_data":
{
"cluster_name": "ft_postgresql_63712",
"cluster_type": "postgresql_single",
"enable_uninstall": true,
"hostname": "192.168.1.192",
"port": 8089,
"postgre_password": "passwd12",
"postgre_user": "postmaster",
"ssh_user": "pipas",
"type": "postgresql"
}
},
"progress_percent": 20,
"started": "2017-06-23T08:29:19.000Z",
"status": "RUNNING",
"status_text": "Installing software",
"title": "Setup PostgreSQL Server",
"user_id": 3,
"user_name": "pipas"
},
"request_created": "2017-06-23T08:31:23.490Z",
"request_id": 3,
"request_processed": "2017-06-23T08:31:23.537Z",
"request_status": "Ok",
"request_user_id": 3
}
  • job: The job instance itself.

getJobInstances

This call can be used to get the job instances from the controller. The request is rather simple:

{
"operation": "getJobInstances",
"request_created": "2017-06-23T05:25:17.891Z",
"request_id": 3
}

This can hold the following fields:

  • ascending: Normally the records will be sent in descending order of time, the newest first. If this boolean is true the order will be descending.
  • cluster_id: The ID of the cluster from which the jobs will be received. The cluster name can also be used to identify the cluster, but neither of them are mandatory. It is possible to receive a mixed job list with multiple clusters.
  • cluster_ids: A list of integers, the IDs of the clusters from which we will get the jobs. No cluster ID or cluster name is mandatory, this call works without any identification of the clusters.
  • cluster_name: The name of the cluster from which the job list will be received. The cluster ID can also be used to identify the cluster, but neither of them are mandatory.
  • show_defined: Turn on state filtering and add jobs in defined state.
  • show_running: Turn on state filtering and add jobs that are running.
  • show_scheduled: Turn on state filtering and add jobs that are scheduled.
  • show_aborted: Turn on state filtering and add jobs that are aborted.
  • show_finished: Turn on state filtering and add jobs that are finished.
  • show_failed: Turn on state filtering and add jobs that are failed.
  • tags: A list of short strings (tags) to filter the job list. If the list is empty no tags will be considered.
  • limit: Limits the number of records the same way the SQL LIMIT statement does.
  • offset: The offset of the first record returned. This works the same way the SQL OFFSET statement works.

If no cluster_id, cluster_ids or cluster_name is sent the controller will send the list of jobs of all the clusters (considering the limit and offset filters of course). The access rights of the authenticated user should also filter the records sent back, but that is unfortunately not yet implemented.

Here is an example showing a reply sent to the getJobInstances request:

{
"jobs": [
{
"can_be_aborted": false,
"can_be_deleted": true,
"class_name": "CmonJobInstance",
"cluster_id": 1,
"created": "2017-06-23T01:00:00.000Z",
"ended": "2017-06-23T01:00:00.000Z",
"exit_code": 0,
"group_id": 1,
"group_name": "admins",
"ip_address": "127.0.0.1",
"job_id": 2,
"job_spec": "Removing Old Backups",
"started": "2017-06-23T01:00:00.000Z",
"status": "FINISHED",
"status_text": "Command ok",
"title": "Removing Old Backups",
"user_id": 0,
"user_name": "system"
},
{
"can_be_aborted": false,
"can_be_deleted": true,
"class_name": "CmonJobInstance",
"cluster_id": 0,
"created": "2017-06-22T12:56:33.000Z",
"ended": "2017-06-22T12:58:18.000Z",
"exit_code": 0,
"group_id": 2,
"group_name": "users",
"has_progress": true,
"job_id": 1,
"job_spec":
{
"command": "create_cluster",
"job_data":
{
"cluster_name": "ft_postgresql_4357",
"cluster_type": "postgresql_single",
"enable_uninstall": true,
"hostname": "192.168.1.191",
"port": 8089,
"postgre_password": "passwd12",
"postgre_user": "postmaster",
"ssh_user": "pipas",
"type": "postgresql"
}
},
"progress_percent": 100,
"started": "2017-06-22T12:56:33.000Z",
"status": "FINISHED",
"status_text": "Job finished.",
"title": "Setup PostgreSQL Server",
"user_id": 3,
"user_name": "pipas"
} ],
"request_created": "2017-06-23T05:25:17.891Z",
"request_id": 3,
"request_processed": "2017-06-23T05:25:17.939Z",
"request_status": "Ok",
"request_user_id": 3,
"total": 2
}

The most important fields of the reply are as follows.

  • jobs: This is of course the list of job instances found. The elements in the list are CmonJobInstance class objects.

    To get a comprehensive list of the properties the controller supports in the CmonJobInstance class please run the following command (or check the The Job Related Structures page):

    s9s metatype --list-properties --type=CmonJobInstance --long
  • total: This is the total number of records found in the database. The caller can implement a pager using this "total", the "limit" and the "offset" properties.

getJobLog

This RPC call is for reading the job status and job messages from the controller. The reply will have the job and the job message in it, so the caller can show the job together with all the messages.

Here is an example of the RPC call:

{
"ascending": true,
"job_id": 1,
"limit": 1,
"log_level": "DEBUG",
"offset": 100,
"operation": "getJobLog",
"request_created": "2019-07-17T05:23:50.262Z",
"request_id": 3
}
  • ascending: Normally the job messages returned so that the first is the newest. When this property is set to true in the request the first message will be the oldest, the last will be the newest.
  • job_id: The numerical ID of the job to return.
  • limit: Optional argument, setting the limit for the log messages sent back.
  • log_level: Optional argument controlling the severity of the messages that will be sent back.

    Valid values are "DEBUG", "SUCCESS", "WARNING" or "FAILED" (or as an alternative "JOB_DEBUG", "JOB_SUCCESS", "JOB_WARNING" or "JOB_FAILED").

    The "DEBUG" will send back every message, "SUCCESS" will not send debug messages, "WARNING" will send warning and failure messages while "FAILURE" will send only failure messages.

  • offset: Optional argument, setting the offset of the first log message to be sent back.

Here is an example of the reply:

{
"job":
{
"can_be_aborted": false,
"can_be_deleted": true,
"class_name": "CmonJobInstance",
"cluster_id": 0,
"created": "2017-06-23T08:29:19.000Z",
"ended": "2017-06-23T08:32:31.000Z",
"exit_code": 0,
"group_id": 2,
"group_name": "users",
"has_progress": true,
"job_id": 1,
"job_spec":
{
"command": "create_cluster",
"job_data":
{
"cluster_name": "ft_postgresql_63712",
"cluster_type": "postgresql_single",
"enable_uninstall": true,
"hostname": "192.168.1.192",
"port": 8089,
"postgre_password": "passwd12",
"postgre_user": "postmaster",
"ssh_user": "pipas",
"type": "postgresql"
}
},
"progress_percent": 100,
"started": "2017-06-23T08:29:19.000Z",
"status": "FINISHED",
"status_text": "Job finished.",
"title": "Setup PostgreSQL Server",
"user_id": 3,
"user_name": "pipas"
},
"messages": [
{
"class_name": "CmonJobMessage",
"created": "2017-06-23T08:29:19.000Z",
"file_name": "CmonCommandHandlerWorker.cpp",
"job_id": 1,
"line_number": 2820,
"message_id": 1,
"message_status": "JOB_SUCCESS",
"message_text": "Started create cluster job."
},
. . .
{
"class_name": "CmonJobMessage",
"created": "2017-06-23T08:32:31.000Z",
"file_name": "CmonCommandHandlerWorker.cpp",
"job_id": 1,
"line_number": 945,
"message_id": 79,
"message_status": "JOB_WARNING",
"message_text": "Try Settings -> Cluster Registrations and press 'Synchronize Clusters'."
} ],
"reply_received": "2017-06-23T08:38:01.052Z",
"request_created": "2017-06-23T08:38:01.046Z",
"request_id": 3,
"request_processed": "2017-06-23T08:38:01.102Z",
"request_status": "Ok",
"request_user_id": 3
}

Log Related Calls

The /v2/log provides calls to access the log handled by the Cmon controller. This is not for the job messages, this is about the logs that are stored in ASCII files.

getLogEntries

This call can be used to get the log messages from the controller.

{
"ascending": true,
"cluster_name": "ft_postgresql_31001",
"limit": 3,
"operation": "getLogEntries",
"request_created": "2017-07-26T07:22:25.767Z",
"request_id": 200
}
  • ascending: Boolean value controlling the order of the log messages.
  • cluster_name: Identifies the cluster from which the log entries will be returned. The cluster ID can also be to identify the cluster.
  • cluster_id: Identifies the cluster from which the log entries will be returned. The cluster name can also be used.
  • component: Identifies the component that sent the log message. Currently this string can be 'Network', 'CmonDatabase', 'Mail', 'Cluster', 'ClusterConfiguration', 'ClusterRecovery', 'Node', 'Host', 'DbHealth', 'DbPerformance', 'SoftwareInstallation', 'Backup' or 'Unknown'.
  • created_before: If this date&time string is provided it will filter the messages by their creation time.
  • created_after: If this date&time string is provided it fill filter the messages by their creation time.
  • limit: If this provided it limits the number of log entries sent back.
  • offset: The index of the first item. If this value is provided together with the limit they can be used to implement a pager.
  • severity: This string can be one of the "LOG_EMERG", "LOG_ALERT", "LOG_CRIT", "LOG_ERR", "LOG_WARNING", "LOG_NOTICE", "LOG_INFO", "LOG_DEBUG" to filter the messages by their severity.

    FIXME: I think this is not implemented as it should be, it is filtering by comparing the value using '=' instead of '<='.

{
"log_entries": [
{
"class_name": "CmonLogMessage",
"component": "ClusterConfiguration",
"created": "2017-07-26T08:35:40.704Z",
"log_class": "LogMessage",
"log_id": 110,
"log_origins":
{
"sender_binary": "cmon",
"sender_file": "../../src/cmonhostmanager.cpp",
"sender_line": 594,
"sender_pid": 40818,
"tv_nsec": 704637455,
"tv_sec": 1501058140
},
"log_specifics":
{
"cluster_id": 1,
"message_text": "Registering CmonPostgreSqlHost: 192.168.1.167:8089"
},
"severity": "LOG_DEBUG"
},
. . .
{
"class_name": "CmonLogMessage",
"created": "2017-07-26T08:38:11.697Z",
"log_class": "LogMessage",
"log_id": 351,
"log_origins":
{
"sender_binary": "cmon",
"sender_file": "../../src/cmonalarmdb.cpp",
"sender_line": 1467,
"sender_pid": 40818,
"tv_nsec": 697060177,
"tv_sec": 1501058291
},
"log_specifics":
{
"cluster_id": 1,
"message_text": "hostId: 1, nodeId: 0, alarm: Host is not responding"
},
"severity": "LOG_DEBUG"
} ],
"log_entry_counts":
{
"component":
{
"Cluster": 1,
"ClusterConfiguration": 2,
"Node": 3,
"Unknown": 194
},
"hostname":
{
"": 199,
"192.168.1.167": 1
},
"sender_binary":
{
"cmon": 200
},
"severity":
{
"LOG_CRIT": 2,
"LOG_DEBUG": 192,
"LOG_ERR": 1,
"LOG_INFO": 2,
"LOG_WARNING": 3
}
},
"reply_received": "2017-07-26T08:39:53.936Z",
"request_created": "2017-07-26T08:39:53.931Z",
"request_id": 3,
"request_processed": "2017-07-26T08:39:54.019Z",
"request_status": "Ok",
"request_user_id": 3,
"total": 258
}
  • log_entries: A list that holds the log entries returned as result. The log entries returned as CmonLogMessage class objects.
  • log_entry_counts: A simple statistics showing how many messages are in the reply with various properties.
  • total: The total number of records with the given filters. This with the limit and offset request parameters can be used to implement a pager.

getLogEntry

This call is made for retrieving one message by its message ID. Here is how a request looks like:

{
"cluster_id": -1,
"message_id": 32,
"operation": "getLogEntry",
"request_created": "2019-08-13T05:22:18.882Z",
"request_id": 3
}

And here is an example for the reply:

{
"debug_messages": [ "RPC V2 authenticated user is 'pipas'." ],
"log_entry":
{
"class_name": "CmonLogMessage",
"created": "2019-08-13T04:00:38.375Z",
"log_class": "JobStarted",
"log_id": 32,
"log_origins":
{
"sender_binary": "cmon",
"sender_file": "CmonCommandHandlerWorker.cpp",
"sender_line": 332,
"sender_pid": 49080,
"tv_nsec": 375158422,
"tv_sec": 1565668838
},
"log_specifics":
{
"cluster_id": 0,
"message_text": "Simulated Failure",
"reason": "Simulated Failure"
},
"severity": "LOG_INFO"
},
"request_created": "2019-08-13T05:22:18.882Z",
"request_id": 3,
"request_processed": "2019-08-13T05:22:18.891Z",
"request_status": "Ok",
"request_user_id": 4
}

getLogStatistics

This call can be used to get some statistical information about the subsystem that handles log messages. This is mostly for debugging purposes, but it also can be used to get some information that is shown to the end-user.

Here is an example for the request:

{
"operation": "getLogStatistics",
"request_created": "2017-07-26T10:58:35.447Z",
"request_id": 3
}

Here is an example for the reply:

{
"log_statistics":
{
"cluster_log_statistics": [
{
"cluster_id": -1,
"disabled": false,
"entries_received": 3,
"format_string": "%C : (%S) %M",
"last_error_message": "",
"lines_written": 0,
"log_file_name": "",
"max_log_file_size": 5242880,
"messages_per_sec": 0.05,
"syslog_enabled": false,
"write_cycle_counter": 1
},
{
"cluster_id": 0,
"disabled": false,
"entries_received": 93,
"format_string": "%C : (%S) %M",
"last_error_message": "Success.",
"lines_written": 93,
"log_file_name": "./cmon-ft-install.log",
"max_log_file_size": 5242880,
"messages_per_sec": 0.05,
"syslog_enabled": false,
"write_cycle_counter": 3
},
{
"cluster_id": 1,
"disabled": false,
"entries_received": 218,
"format_string": "%C : (%S) %M",
"last_error_message": "Success.",
"lines_written": 218,
"log_file_name": "/tmp/cmon_1.log",
"max_log_file_size": 5242880,
"messages_per_sec": 0.85,
"syslog_enabled": false,
"write_cycle_counter": 11
} ],
"current_time": "2017-07-26T11:01:08.884Z",
"entries_statistics":
{
"entries_received": 319,
"entries_written_to_cmondb": 314
},
"has_cmondb": true,
"last_error_message": "Success.",
"last_flush_time": "2017-07-26T11:00:59.543Z",
"log_debug_enabled": false,
"writer_thread_running": true,
"writer_thread_started": "2017-07-26T10:55:29.410Z"
},
"request_created": "2017-07-26T11:01:08.840Z",
"request_id": 3,
"request_processed": "2017-07-26T11:01:08.884Z",
"request_status": "Ok",
"request_user_id": 3
}

Maintenance

The "v2/maintenance" is for creating, deleting and reading maintenance periods.

addMaintenance

The addMaintenance call is for adding new maintenance periods for clusters and hosts. A maintenance period is either belong to an entire cluster, in which case the cluster must be identified or it belongs to a host, so that the host name must be provided.

{
"deadline": "2017-06-23T10:46:37.000Z",
"hostname": "192.168.1.195",
"initiate": "2017-06-23T10:46:27.000Z",
"operation": "addMaintenance",
"reason": "test_31643_maintenance",
"request_created": "2017-06-23T10:46:27.754Z",
"request_id": 3
}
  • deadline: Marks the end of the maintenance period. At this time the maintenance period will automatically expire.
  • cluster_id: The numerical ID of the cluster if this is a cluster maintenance period.
  • hostname: The name of the host if this is a host maintenance.
  • initiate: The start of the maintenance period.
  • reason: A human readable string which the users will see.
{
"UUID": "366372d3-2633-f126-bf8b-c69418274d69",
"request_created": "2017-06-23T10:46:27.754Z",
"request_id": 3,
"request_processed": "2017-06-23T10:46:27.800Z",
"request_status": "Ok",
"request_user_id": 3
}
  • UUID: A unique string which can be used to identify the maintenance period.

removeMaintenance

This RPC call can be used to delete maintenance periods before their time. Deleting a maintenance period will make them expire immediately.

{
"UUID": "a1f2f274-b151-e286-0eee-199efaa5b444",
"operation": "removeMaintenance",
"request_created": "2017-06-23T13:23:28.770Z",
"request_id": 3
}
  • UUID: Indentifies which maintenance period is going to be deleted.
{
"request_created": "2017-06-23T13:23:28.770Z",
"request_id": 3,
"request_processed": "2017-06-23T13:23:28.816Z",
"request_status": "Ok",
"request_user_id": 3
}

getMaintenance

This call either sends back cluster maintenance periods when it received a cluster ID or host maintenance periods when it received one or more hostnames. When no cluster ID provided and no host names are sent either, both cluster maintenance and host maintenance periods are sent back.

The idea behind this call is that when the UI shows a host, it gets the maintenance periods of that host to show the details, when it shows a cluster it gets the maintenance periods of that cluster to show the details. If however the UI needs to know what nodes are in a cluster, that's an other call, that's not a "getMaintenance" call at all.

{
"operation": "getMaintenance",
"request_created": "2017-06-23T10:46:27.913Z",
"request_id": 3
}

Here is an example of an s9s run. It shows two maintenance records, one for a host and one for a cluster:

$ s9s maintenance --list --long
ST UUID OWNER GROUP START END HOST/CLUSTER * REASON
-h 1da4955 pipas testgroup 2019-11-10 13:05:40 2019-11-10 15:05:40 192.168.0.58 testMaintenanceJob4
Ac 2358876 pipas testgroup 13:05:37 14:05:37 ft_maintenance_36279 testMaintenanceJob1
-c d89319c pipas testgroup 2019-11-09 13:05:39 2019-11-09 15:05:39 ft_maintenance_36279 testMaintenanceJob3
Total: 2

But the two records has three maintenance periods in it, because the same cluster has two maintenance periods. Now these looks like this in a reply:

{
"debug_messages": [ "RPC V2 authenticated user is 'pipas'." ],
"maintenance_records": [
{
"class_name": "CmonMaintenanceInfo",
"hostname": "192.168.0.58",
"is_active": false,
"maintenance_periods": [ {
"UUID": "1da49552-dd68-4604-ab16-a340c312cd9c",
"deadline": "2019-11-10T14:05:40.000Z",
"groupid": 4,
"groupname": "testgroup",
"initiate": "2019-11-10T12:05:40.000Z",
"is_active": false,
"reason": "testMaintenanceJob4",
"userid": 4,
"username": "pipas"
} ]
},
{
"class_name": "CmonMaintenanceInfo",
"cluster": {
"acl": "user::rwx,group::rwx,other::---",
"alarm_statistics": {
"class_name": "CmonAlarmStatistics"
},
"cdt_path": "/",
"class_name": "CmonClusterInfo",
"cluster_auto_recovery": true,
"cluster_id": 1,
"cluster_name": "ft_maintenance_36279",
"cluster_type": "POSTGRESQL_SINGLE",
"configuration_file": "/tmp/cmon_1.cnf",
"effective_privileges": "",
"group_owner": {
"acl": "user::rwx,group::rwx,other::---",
"cdt_path": "/groups",
"class_name": "CmonGroup",
"created": "2019-11-08T12:02:37.983Z",
"group_id": 4,
"group_name": "testgroup",
"owner_group_id": 1,
"owner_group_name": "admins",
"owner_user_id": 1,
"owner_user_name": "system"
},
"job_statistics": {
"class_name": "CmonJobStatistics"
},
"log_file": "/tmp/cmon_1.log",
"maintenance_mode_active": true,
"managed": true,
"node_auto_recovery": true,
"owner": {
"acl": "user::rwx,group::r--,other::r--",
"cdt_path": "/",
"class_name": "CmonUser",
"created": "2019-11-08T12:02:37.995Z",
"disabled": false,
"email_address": "[email protected]",
"first_name": "Laszlo",
"groups": [ {
"acl": "user::rwx,group::rwx,other::---",
"cdt_path": "/groups",
"class_name": "CmonGroup",
"created": "2019-11-08T12:02:37.983Z",
"group_id": 4,
"group_name": "testgroup",
"owner_group_id": 1,
"owner_group_name": "admins",
"owner_user_id": 1,
"owner_user_name": "system"
} ],
"last_failed_login": "",
"last_login": "2019-11-08T12:24:56.456Z",
"last_name": "Pere",
"n_failed_logins": 0,
"origin": "CmonDb",
"owner_group_id": 1,
"owner_group_name": "admins",
"owner_user_id": 4,
"owner_user_name": "pipas",
"suspended": false,
"timezone": {
"abbreviation": "CET",
"class_name": "CmonTimeZone",
"name": "Central European Time",
"offset": -3600,
"use_dst": true
},
"user_id": 4,
"user_name": "pipas"
},
"state": "STARTED",
"status_text": "All nodes are operational.",
"vendor": "postgres",
"version": "9.6"
},
"cluster_id": 1,
"is_active": true,
"maintenance_periods": [
{
"UUID": "2358876c-8eec-42c8-b149-69e722044b3f",
"deadline": "2019-11-08T13:05:37.178Z",
"groupid": 4,
"groupname": "testgroup",
"initiate": "2019-11-08T12:05:37.178Z",
"is_active": true,
"reason": "testMaintenanceJob1",
"userid": 4,
"username": "pipas"
},
{
"UUID": "d89319c9-3e7c-462e-9f54-4052682d2daa",
"deadline": "2019-11-09T14:05:39.000Z",
"groupid": 4,
"groupname": "testgroup",
"initiate": "2019-11-09T12:05:39.000Z",
"is_active": false,
"reason": "testMaintenanceJob3",
"userid": 4,
"username": "pipas"
}
]
}
],
"reply_received": "2019-11-08T12:24:56.475Z",
"request_created": "2019-11-08T12:24:56.464Z",
"request_id": 3,
"request_processed": "2019-11-08T12:24:56.475Z",
"request_status": "Ok",
"request_user_id": 4,
"total": 2
}

getCurrentMaintenance

This RPC call was made so that the client program has a quick and easy way to get information about the active maintanence for a cluster or a host.

If this call finds a host maintenance that is active it will return that, if it finds a cluster maintenance that will be returned.

Here is a call to search for a cluster wide maintenance (no host maintenance is returned ever).

{
"cluster_id": 1,
"operation": "getCurrentMaintenance",
"request_created": "2019-11-11T15:07:37.539Z",
"request_id": 3
}

Here is a call that returns either the cluster maintenance or the host maintenance or no maintenance if neither of those found:

{
"cluster_id": 1,
"hostname": "10.10.1.8",
"operation": "getCurrentMaintenance",
"request_created": "2019-11-12T08:55:58.507Z",
"request_id": 3
}

And here is how a reply should look like when a maintenance period is found:

{
"debug_messages": [ "RPC V2 authenticated user is 'pipas'." ],
"found_maintenance": true,
"maintenance_period": {
"UUID": "9dec403d-0940-4603-b93f-3c58fa773ce1",
"class_name": "CmonMaintenanceDate",
"deadline": "2019-11-11T16:07:12.969Z",
"groupid": 4,
"groupname": "testgroup",
"initiate": "2019-11-11T15:07:12.969Z",
"reason": "testMaintenanceJob1",
"userid": 4,
"username": "pipas"
},
"reply_received": "2019-11-11T15:07:37.546Z",
"request_created": "2019-11-11T15:07:37.539Z",
"request_id": 3,
"request_processed": "2019-11-11T15:07:37.546Z",
"request_status": "Ok",
"request_user_id": 4,
"ui_string": "Cluster 1 is under maintenance: testMaintenanceJob1"
}

getNextMaintenance

This RPC call was made so that the client program has a quick and easy way to get information about the next coming maintenance periods.

If this call finds a host maintenance that is active it will return that, if it finds a cluster maintenance that will be returned.

Here is a call to search for a cluster wide maintenance (no host maintenance is returned ever).

{
"cluster_id": 1,
"operation": "getNextMaintenance",
"request_created": "2019-11-12T08:07:29.966Z",
"request_id": 3
}

Here is a call that returns either the cluster maintenance or the host maintenance or no maintenance if neither of those found:

{
"cluster_id": 1,
"hostname": "10.10.1.8",
"operation": "getNextMaintenance",
"request_created": "2019-11-12T08:55:15.628Z",
"request_id": 3
}

And here is how a reply should look like when a maintenance period is found:

{
"debug_messages": [ "RPC V2 authenticated user is 'pipas'." ],
"found_maintenance": true,
"maintenance_period": {
"UUID": "8a812d58-18a2-4d55-9702-2e5e05de1a41",
"class_name": "CmonMaintenanceDate",
"deadline": "2019-11-13T09:07:15.000Z",
"groupid": 4,
"groupname": "testgroup",
"initiate": "2019-11-13T08:07:15.000Z",
"reason": "testMaintenanceJob1",
"userid": 4,
"username": "pipas"
},
"reply_received": "2019-11-12T08:07:29.973Z",
"request_created": "2019-11-12T08:07:29.966Z",
"request_id": 3,
"request_processed": "2019-11-12T08:07:29.973Z",
"request_status": "Ok",
"request_user_id": 4,
"ui_string": "Cluster 1 maintenance starts Nov 13 08:07:15: testMaintenanceJob1"
}

Reports

The "/v2/reports" provides access to reports generated by the Cmon controller.

generateReport

{
"cluster_id": 1,
"operation": "generateReport",
"report":
{
"class_name": "CmonReport",
"recipients": "[email protected]",
"report_type": "testreport",
"text_format": "AnsiTerminal"
},
"request_created": "2017-08-04T11:06:32.148Z",
"request_id": 3
}
{
"report":
{
"class_name": "CmonReport",
"content": " Simple Test Report\n\n This report was created to test the report subsystem. The document was\n created in the CmonReporter::buildTestReport() method.\n\n",
"created": "2017-08-04T11:06:32.000Z",
"document":
{
"className": "CmonDocument",
"elements": [
{
"className": "CmonTitle",
"text": "Simple Test Report",
"titleLevel": 1
},
{
"className": "CmonParagraph",
"text": "This report was created to test the report subsystem. The document was created in the CmonReporter::buildTestReport() method."
} ]
},
"owner": "pipas",
"path": "/home/pipas/s9s_tmp/1/postgresql_single/cmon-reports/testreport_2017-08-04_130632.html",
"recipients": "[email protected]",
"report_id": 1,
"report_type": "testreport",
"text_format": "AnsiTerminal",
"title": "Simple Test Report"
},
"request_created": "2017-08-04T11:06:32.148Z",
"request_id": 3,
"request_processed": "2017-08-04T11:06:32.200Z",
"request_status": "Ok",
"request_user_id": 3
}
  • report: The report that has been created as a CmonReport class object.

    To get a comprehensive list of the properties the controller supports in the CmonReport class one can run the following command:

    s9s metatype --list-properties --type=CmonReport --long

getReport

The request format for this call has recently been changed. Instead of passing just a single ID we need to pass a CmonReport class object. Here is how:

{
"operation": "getReport",
"report":
{
"class_name": "CmonReport",
"report_id": 1,
"text_format": "AnsiTerminal"
},
"request_created": "2019-10-04T07:39:40.138Z",
"request_id": 3
}

getReportTemplates

This call is to return the report templates describing what kind of reports are available for the user when generating reports. Report templates are just simple CmonReport objects with a few properties set to reflect the properties that the report will have when a report with the same report type is used.

So template name is just an other way of saying report_type.

Here is an example showing how a request should look like:

{
"operation": "getReportTemplates",
"request_created": "2019-07-31T06:45:30.083Z",
"request_id": 3
}

And here is an example showing how the reply will look like:

{
"reports": [
{
"class_name": "CmonReport",
"report_type": "default",
"title": "System Report"
},
. . .
{
"class_name": "CmonReport",
"report_type": "capacity",
"title": "Database Growth Report"
} ],
"request_created": "2019-07-31T06:45:30.083Z",
"request_id": 3,
"request_processed": "2019-07-31T06:45:30.089Z",
"request_status": "Ok",
"request_user_id": 4
}

deleteReport

A call to delete an existing report from the controller.

{
"cluster_id": 1,
"operation": "deleteReport",
"report":
{
"class_name": "CmonReport",
"report_id": 1
},
"request_created": "2017-08-04T07:11:38.745Z",
"request_id": 4
}
{
"request_created": "2017-08-04T07:11:38.745Z",
"request_id": 4,
"request_processed": "2017-08-04T07:11:38.793Z",
"request_status": "Ok",
"request_user_id": 3
}

getReports

A call to get existing reports from the controller.

{
"cluster_id": 1,
"operation": "getReports",
"request_created": "2017-08-02T12:34:59.892Z",
"request_id": 4
}
{
"reports": [
{
"class_name": "CmonReportInfo",
"cluster_id": 1,
"created": "2017-08-02T13:01:44.000Z",
"name": "availability_2017-08-02_143459.html",
"owner": "pipas",
"path": "/home/pipas/s9s_tmp/1/postgresql_single/cmon-reports/availability_2017-08-02_143459.html",
"recipients": "",
"report_id": 1,
"report_type": "availability"
} ],
"request_created": "2017-08-02T12:34:59.892Z",
"request_id": 4,
"request_processed": "2017-08-02T12:34:59.936Z",
"request_status": "Ok",
"request_user_id": 3,
"total": 1
}

Repositories

The /v2/repositories path provides calls to create and manage local software repositories and get information about supported software.

getRepositories

Returns available local repositories.

{
"cluster_id": 1,
"operation": "getRepositories",
"request_created": "2017-07-31T07:56:02.214Z",
"request_id": 3
}
  • cluster-type: filter by cluster-type (galera, mongodb, postgresql, ...)
  • vendor: filter by vendor (percona, mariadb, ...)
  • db-version: filter by db (major.minor) version (5.6, 10.1, ...)
{
"repositories": [
{
"class_name": "CmonRepository",
"cluster_type": "galera",
"db_version": "5.6",
"modified": "2017-08-01T08:25:29.000Z",
"name": "percona-5.6-apt-precise",
"os":
{
"distribution/codename": "unknown",
"distribution/name": "unknown",
"distribution/release": "unknown",
"distribution/type": "debian"
},
"path": "/var/tmp/unammed-cmon-repo",
"used_by_cluster": "",
"vendor": "percona"
} ],
"request_created": "2017-08-01T08:25:35.414Z",
"request_id": 4,
"request_processed": "2017-08-01T08:25:35.462Z",
"request_status": "Ok",
"request_user_id": 3,
"total": 1
}

deleteRepository

Removes a repository from controller (it will also delete the repository directory).

getRepositorySetup

Prints out the (semi-)generated repository filename and contents to use the repository manually. You have to substitute the CMON-HOSTNAME string with the controllers IP address before u use it.

getSupportedSetups

This call will return what possible setups can be used to create/install a new node.

The request does not need a cluster ID or cluster name, the only field that is needed is the "operation" field:

{
"operation": "getSupportedSetups",
"request_created": "2017-07-31T09:57:07.145Z",
"request_id": 3
}

Here is how the reply will look like:

{
"request_created": "2017-07-31T09:57:07.145Z",
"request_id": 3,
"request_processed": "2017-07-31T09:57:07.188Z",
"request_status": "Ok",
"request_user_id": 3,
"supported_setups": [
{
"cluster_type": "mongodb",
"db_version": "3.2",
"os_version": "5",
"vendor": "10gen"
},
. . .
{
"cluster_type": "replication",
"db_version": "5.7",
"os_version": "xenial",
"vendor": "oracle"
} ],
"total": 206
}

getSpreadsheetNames

{
"cluster_id": 1,
"operation": "getSpreadsheetNames",
"request_created": "2018-10-01T10:25:27.120Z",
"request_id": 3
}

getSpreadsheet

{
"cluster_id": 1,
"operation": "getSpreadsheet",
"request_created": "2018-10-02T07:12:36.353Z",
"request_id": 3,
"spreadsheet_name": "Capacity Planning"
}

Statistical Data Calls

The /v2/stat provides calls to access various statistical information about the cluster.

getInfo

This call is for getting the data that the sheet manager collected from the system. Here is an example of the call:

{
"operation": "getInfo",
"request_created": "2017-07-27T12:58:00.283Z",
"request_id": 3
}
{
"collected_info": [
{
"cluster_id": 1,
"info":
{
"cluster.status": 2,
"cluster.statustext": "Cluster started.",
"cmon.domainname": "",
"cmon.hostname": "t7500",
"cmon.running": true,
"cmon.starttime": 1501144642,
"cmon.uptime": 15636,
"conf.backup_retention": 31,
"conf.clusterid": 1,
"conf.clustername": "ft_postgresql_48154",
"conf.clustertype": 5,
"conf.configfile": "/tmp/cmon_1.cnf",
"conf.hostname": "192.168.1.127",
"conf.os": "debian",
"conf.statustext": "Configuration loaded.",
"host.1.connected": true,
"host.1.cpu_io_wait_percent": 0.353357,
"host.1.cpu_steal_percent": 0,
"host.1.cpu_usage_percent": 15.5331,
"host.1.cpucores": 16,
"host.1.cpuinfo": [
{
"class_name": "CmonCpuInfo",
"cpucores": 4,
"cpumaxmhz": 2.268e+06,
"cpumhz": 1600,
"cpumodel": "Intel(R) Xeon(R) CPU L5520 @ 2.27GHz",
"cputemp": 55.5,
"hostid": 1,
"physical_cpu_id": 0,
"siblings": 8,
"vendor": "GenuineIntel"
},
{
"class_name": "CmonCpuInfo",
"cpucores": 4,
"cpumaxmhz": 2.268e+06,
"cpumhz": 1733,
"cpumodel": "Intel(R) Xeon(R) CPU L5520 @ 2.27GHz",
"cputemp": 55.5,
"hostid": 1,
"physical_cpu_id": 1,
"siblings": 8,
"vendor": "GenuineIntel"
} ],
"host.1.cpumaxmhz": 2268,
"host.1.cpumhz": 1733,
"host.1.cpumodel": "Intel(R) Xeon(R) CPU L5520 @ 2.27GHz",
"host.1.cputemp": 55.5,
"host.1.devices": [ "/dev/mapper/core1--vg-root" ],
"host.1.free_disk_bytes": 155814936576,
"host.1.hostname": "192.168.1.174",
"host.1.interfaces": [ "eth0" ],
"host.1.ip": "192.168.1.174",
"host.1.membuffer": 0,
"host.1.memcached": 1,
"host.1.memfree": 16031900,
"host.1.memtotal": 16777216,
"host.1.network_interfaces": [
{
"interface_name": "eth0",
"rx_bytes_per_sec": 11775.8,
"tx_bytes_per_sec": 17652.3
} ],
"host.1.pingdelay": -1,
"host.1.pingstatustext": "Creating ICMP socket (to ping '192.168.1.174') failed: Operation not permitted.",
"host.1.port": 8089,
"host.1.rx_bytes_per_second": 11775.8,
"host.1.swapfree": 0,
"host.1.swaptotal": 0,
"host.1.total_disk_bytes": 208033853440,
"host.1.tx_bytes_per_second": 17652.3,
"host.1.uptime": 15779,
"host.1.wallclock": 1501160299,
"host.1.wallclocksampled": 1501160235,
"host.2.class_name": "controller",
"host.2.connected": true,
"host.2.cpu_io_wait_percent": 2.89087,
"host.2.cpu_steal_percent": 0,
"host.2.cpu_usage_percent": 204.378,
"host.2.cpucores": 24,
"host.2.cpuinfo": [
{
"class_name": "CmonCpuInfo",
"cpucores": 6,
"cpumaxmhz": 2.661e+06,
"cpumhz": 1596,
"cpumodel": "Intel(R) Xeon(R) CPU X5650 @ 2.67GHz",
"cputemp": 0,
"hostid": 2,
"physical_cpu_id": 0,
"siblings": 12,
"vendor": "GenuineIntel"
},
{
"class_name": "CmonCpuInfo",
"cpucores": 6,
"cpumaxmhz": 2.661e+06,
"cpumhz": 1596,
"cpumodel": "Intel(R) Xeon(R) CPU X5650 @ 2.67GHz",
"cputemp": 0,
"hostid": 2,
"physical_cpu_id": 1,
"siblings": 12,
"vendor": "GenuineIntel"
} ],
"host.2.cpumaxmhz": 2661,
"host.2.cpumhz": 1596,
"host.2.cpumodel": "Intel(R) Xeon(R) CPU X5650 @ 2.67GHz",
"host.2.cputemp": 0,
"host.2.devices": [ "/dev/sda1" ],
"host.2.free_disk_bytes": 1409452683264,
"host.2.hostname": "192.168.1.127",
"host.2.interfaces": [ "eth0" ],
"host.2.ip": "192.168.1.127",
"host.2.membuffer": 318624,
"host.2.memcached": 19415480,
"host.2.memfree": 9243460,
"host.2.memtotal": 49453276,
"host.2.network_interfaces": [
{
"interface_name": "eth0",
"rx_bytes_per_sec": 29150.4,
"tx_bytes_per_sec": 18520.8
} ],
"host.2.pingdelay": -1,
"host.2.pingstatustext": "Creating ICMP socket (to ping '192.168.1.127') failed: Operation not permitted.",
"host.2.port": 9555,
"host.2.rx_bytes_per_second": 29150.4,
"host.2.swapfree": 0,
"host.2.swaptotal": 0,
"host.2.total_disk_bytes": 2125259440128,
"host.2.tx_bytes_per_second": 18520.8,
"host.2.uptime": 2.59852e+06,
"host.2.version": "1.4.3",
"host.2.wallclock": 1501160235,
"host.2.wallclocksampled": 1501160235,
"license.expires": -1,
"license.status": false,
"license.statustext": "No license found.",
"mail.statustext": "Created.",
"netStat.1.eth0.rxBytes": 287358005,
"netStat.1.eth0.txBytes": 205682361,
"netStat.2.eth0.rxBytes": 531084878595,
"netStat.2.eth0.txBytes": 503628558465
}
} ],
"request_created": "2017-07-27T12:58:00.283Z",
"request_id": 3,
"request_processed": "2017-07-27T12:58:00.328Z",
"request_status": "Ok",
"request_user_id": 3
}

statByName

{
"cluster_id": 1,
"end_datetime": "15:21",
"name": "cpustat",
"operation": "statByName",
"request_created": "2017-07-27T13:30:41.866Z",
"request_id": 3,
"start_datetime": "15:20",
"with_hosts": true
}
{
"data": [
{
"busy": 0.00555556,
"cpuid": 0,
"cpumhz": 1600,
"cpumodelname": "Intel(R) Xeon(R) CPU L5520 @ 2.27GHz",
"cpuphysicalid": 0,
"cputemp": 55.5,
"created": 1501161605,
"hostid": 1,
"idle": 0.994444,
"interval": 5473,
"iowait": 0,
"loadavg1": 0.14,
"loadavg15": 0.09,
"loadavg5": 0.11,
"sampleends": 1501161608,
"samplekey": "CmonCpuStats-1-0",
"steal": 0,
"sys": 0.0037037,
"uptime": 17107,
"user": 0.00185185
},
. . .
{
"busy": 0.0980228,
"cpuid": 23,
"cpumhz": 1596,
"cpumodelname": "Intel(R) Xeon(R) CPU X5650 @ 2.67GHz",
"cpuphysicalid": 0,
"cputemp": 0,
"created": 1501161658,
"hostid": 2,
"idle": 0.901977,
"interval": 5379,
"iowait": 0,
"loadavg1": 2.35,
"loadavg15": 2.23,
"loadavg5": 2.33,
"sampleends": 1501161661,
"samplekey": "CmonCpuStats-2-23",
"steal": 0,
"sys": 0.0247148,
"uptime": 2.5999e+06,
"user": 0.073308
} ],
"hosts": [
{
"class_name": "CmonPostgreSqlHost",
"clusterid": 1,
"configfile": [ "/etc/postgresql/9.6/main/postgresql.conf" ],
"connected": true,
"data_directory": "/var/lib/postgresql/9.6/main",
"datadir": "/var/lib/postgresql/9.6/main",
"description": "",
"distribution":
{
"codename": "xenial",
"name": "ubuntu",
"release": "16.04",
"type": "debian"
},
"hostId": 1,
"hostname": "192.168.1.174",
"hoststatus": "CmonHostOnline",
"hot_standby": true,
"ip": "192.168.1.174",
"lastseen": 1501162240,
"logfile": "/var/log/postgresql/postgresql-9.6-main.log[0m",
"maintenance_mode_active": false,
"message": "Up and running",
"nodetype": "postgres",
"pid": 5247,
"pidfile": "/var/run/postgresql/9.6-main.pid",
"pingstatus": 0,
"pingtime": 0,
"port": 8089,
"readonly": false,
"received_location": "0/1523B10",
"replay_location": "0/1523B10",
"role": "master",
"sshfailcount": 0,
"ssl_certs":
{
"server":
{
"ca": "",
"id": 0,
"key": "/etc/ssl/private/ssl-cert-snakeoil.key",
"path": "/etc/ssl/certs/ssl-cert-snakeoil.pem"
}
},
"timestamp": 1501162240,
"unique_id": 1,
"uptime": 17592,
"version": "9.6.3",
"wallclock": 1501162260,
"wallclocktimestamp": 1501162197
},
{
"class_name": "CmonHost",
"clusterid": 1,
"configfile": "/tmp/cmon_1.cnf",
"connected": true,
"distribution":
{
"codename": "trusty",
"name": "ubuntu",
"release": "14.04",
"type": "debian"
},
"hostId": 2,
"hostname": "192.168.1.127",
"hoststatus": "CmonHostOnline",
"ip": "192.168.1.127",
"lastseen": 1501162240,
"logfile": "/tmp/cmon_1.log",
"maintenance_mode_active": false,
"message": "Up and running",
"nodetype": "controller",
"pid": 48111,
"pingstatus": 0,
"pingtime": 0,
"port": 9555,
"role": "controller",
"timestamp": 1501162240,
"unique_id": 2,
"uptime": 17759,
"version": "1.4.3",
"wallclock": 1501162197,
"wallclocktimestamp": 1501162197
} ],
"reply_received": "2017-07-27T13:30:41.871Z",
"request_created": "2017-07-27T13:30:41.866Z",
"request_id": 3,
"request_processed": "2017-07-27T13:30:41.929Z",
"request_status": "Ok",
"request_user_id": 3,
"total": 384
}

getCpuPhysicalInfo

{
"cluster_id": 1,
"operation": "getCpuPhysicalInfo",
"request_created": "2017-07-27T13:43:47.802Z",
"request_id": 3
}
{
"data": [
{
"class_name": "CmonCpuInfo",
"cpucores": 4,
"cpumaxmhz": 2.268e+06,
"cpumhz": 1600,
"cpumodel": "Intel(R) Xeon(R) CPU L5520 @ 2.27GHz",
"cputemp": 55.5,
"hostid": 1,
"physical_cpu_id": 0,
"siblings": 8,
"vendor": "GenuineIntel"
},
{
"class_name": "CmonCpuInfo",
"cpucores": 4,
"cpumaxmhz": 2.268e+06,
"cpumhz": 1600,
"cpumodel": "Intel(R) Xeon(R) CPU L5520 @ 2.27GHz",
"cputemp": 55.5,
"hostid": 1,
"physical_cpu_id": 1,
"siblings": 8,
"vendor": "GenuineIntel"
} ],
"request_created": "2017-07-27T13:43:47.802Z",
"request_id": 3,
"request_processed": "2017-07-27T13:43:47.848Z",
"request_status": "Ok",
"request_user_id": 3,
"total": 2
}

Cmon Directory Tree

The "/v2/tree" path is for managing objects in the Cmon Directory Tree.

getTree

This call can be used to get the CDT (Cmon Directory Tree).

{
"operation": "getTree",
"path": "/galera_001",
"request_created": "2017-10-03T07:12:13.682Z",
"request_id": 3
}
  • path: The path of the sub-tree to get. This is not mandatory, if it is not provided the entire tree will be returned (or at least the part that the user allowed to see).
{
"cdt":
{
"item_acl": "user::rwx,group::rw-,other::---",
"item_name": "galera_001",
"item_path": "/",
"item_type": "Cluster",
"object_id": 1,
"owner_group_id": 2,
"owner_group_name": "users",
"owner_user_id": 3,
"owner_user_name": "pipas",
"sub_items": [
{
"item_acl": "user::rwx,group::rw-,other::---",
"item_name": "192.168.1.212:3306",
"item_path": "/galera_001",
"item_type": "Node",
"object_id": 2,
"owner_group_id": 1,
"owner_group_name": "admins",
"owner_user_id": 1,
"owner_user_name": "system"
},
{
"item_acl": "user::rwx,group::rw-,other::---",
"item_name": "192.168.1.127:9555",
"item_path": "/galera_001",
"item_spec": "controller",
"item_type": "Node",
"object_id": 3,
"owner_group_id": 1,
"owner_group_name": "admins",
"owner_user_id": 1,
"owner_user_name": "system"
} ]
},
"request_created": "2017-10-03T07:12:13.682Z",
"request_id": 3,
"request_processed": "2017-10-03T07:12:13.750Z",
"request_status": "Ok",
"request_user_id": 3
}
  • cdt: The Cmon Directory Tree organized into a tree of objects. This is either the root node of the tree or the node representing the path that was requested.

This is experimental code, nobody uses it, except the s9s CLI.

move

This call can be used to move an object into a new location in the tree.

{
"operation": "move",
"request_created": "2017-10-03T07:51:53.285Z",
"request_id": 3,
"source_path": "/storage01",
"target_path": "/servers"
}
  • source_path: The full path of the object to move.
  • target_path: The new location where the object will be moved.

The reply of this request is quite simple, it just states the request was processed:

{
"request_created": "2017-10-03T07:51:53.285Z",
"request_id": 3,
"request_processed": "2017-10-03T07:51:53.400Z",
"request_status": "Ok",
"request_user_id": 3
}

This is experimental code, nobody uses it, except the s9s CLI.

rename

Changes the name of a CDT entry. Here is how a request should look like.

{
"operation": "rename",
"request_created": "2019-01-08T09:18:04.315Z",
"request_id": 62,
"source_path": "/groups/mygroup",
"target_name": "newgroup"
}

So for example if one wants to change the name of a user group one needs to get the group. Then the "cdt_path" and the "group_name" property of the group has to be accessed to build the path of the group object and the "rename" request has to be sent.

Here is a reply given for the "rename" request if the operation was successful:

{
"request_created": "2019-01-08T09:18:04.315Z",
"request_id": 62,
"request_processed": "2019-01-08T09:18:04.343Z",
"request_status": "Ok",
"request_user_id": 1
}

getAcl

This call can be used to get the ACL of an object from the Cmon Directory Tree.

{
"operation": "getAcl",
"path": "/core1/containers/bestw_controller",
"request_created": "2017-10-03T06:39:59.289Z",
"request_id": 3
}
  • path: The full path of the object.
{
"acl": "user::rwx,group::rw-,other::---",
"object_name": "bestw_controller",
"object_path": "/core1/containers",
"owner_group_name": "users",
"owner_user_name": "pipas",
"request_created": "2017-10-03T06:39:59.289Z",
"request_id": 3,
"request_processed": "2017-10-03T06:40:02.358Z",
"request_status": "Ok",
"request_user_id": 3
}
  • acl: The ACL for the requested object in string format.
  • object_name: The name of the object.
  • object_path: The path in the CDT where the object can be found.
  • owner_group_name: The group name of the group owner for the object.
  • owner_user_name: The user name of the owner of the group.

This is experimental code, nobody uses it, except the s9s CLI.

cat

This call reads the content of a CDT file.

{
"operation": "cat",
"path": "/.issue",
"request_created": "2017-12-18T13:22:45.664Z",
"request_id": 3
}
{
"file_content": "Severalnines Clustercontrol Server.\n",
"request_created": "2017-12-18T13:22:45.664Z",
"request_id": 3,
"request_processed": "2017-12-18T13:22:45.681Z",
"request_status": "Ok",
"request_user_id": 3
}

getObject

This call can be used to get the object using its full path as a reference.

This is how the request should be sent.

{
"operation": "getObject",
"path": "/.runtime/cluster_manager",
"request_created": "2018-11-29T09:09:22.836Z",
"request_id": 2
}

Here is how a reply looks like.

{
"object":
{
"acl": "user::r--,group::---,other::---",
"cdt_path": "/.runtime",
"class_name": "CmonCdtEntry",
"content": " cluster_manager_instance : 0x7fad58001380\n\n",
"dirty": false,
"major_device_number": 10,
"minor_device_number": 1,
"name": "cluster_manager",
"owner_group_id": 1,
"owner_group_name": "admins",
"owner_user_id": 1,
"owner_user_name": "system",
"type": "CmonCdtFile"
},
"reply_received": "2018-11-29T09:09:22.841Z",
"request_created": "2018-11-29T09:09:22.836Z",
"request_id": 2,
"request_processed": "2018-11-29T09:09:22.864Z",
"request_status": "Ok",
"request_user_id": 1
}

addAcl

This call can be used to add an ACL entry to an object. Please note that if the given entry is already added to the ACL it will be overwritten. This is the intuitive thing to do.

{
"acl": "user:nobody:r--",
"operation": "addAcl",
"path": "/core1/containers/bestw_controller",
"request_created": "2017-10-03T06:43:10.458Z",
"request_id": 3
}
  • acl: The ACL to add. This should be the text format of one ACL entry.
  • path: The full path of the object to change.
{
"acl": "user::rwx,user:nobody:r--,group::rw-,other::---",
"object_name": "bestw_controller",
"object_path": "/core1/containers",
"owner_group_name": "users",
"owner_user_name": "pipas",
"request_created": "2017-10-03T06:46:42.233Z",
"request_id": 3,
"request_processed": "2017-10-03T06:46:42.322Z",
"request_status": "Ok",
"request_user_id": 3
}
  • acl: The ACL of the object after the change.
  • object_name: The name of the object.
  • object_path: The path in the CDT where the object can be found.
  • owner_group_name: The group name of the group owner for the object.
  • owner_user_name: The user name of the owner of the group.

removeAcl

This call can be used to remove an ACL entry from the ACL of an object. Currently only one single ACL entry can be removed at a a time.

Please note that certain ACL entries are mandatory and can not be removed only changed. If the call tries to remove such a mandatory entry an error will be reported.

{
"acl": "user:pipas:rwx",
"operation": "removeAcl",
"path": "/",
"request_created": "2017-10-04T15:07:50.427Z",
"request_id": 3
}

Here is a reply notifying the caller about the successful removal of the entry.

{
"acl": "user::rwx,group::rwx,other::rwx",
"object_name": "",
"object_path": "/",
"owner_group_name": "admins",
"owner_user_name": "system",
"request_created": "2017-10-04T15:07:50.427Z",
"request_id": 3,
"request_processed": "2017-10-04T15:07:50.504Z",
"request_status": "Ok",
"request_user_id": 3
}

checkAccess

This RPC call can be used to check if the authenticated has access to the given object. Here is the request:

{
"operation": "checkAccess",
"path": "/pipas",
"privileges": "rw-",
"request_created": "2017-12-06T12:08:53.704Z",
"request_id": 3
}

The reply will simply be an "Ok" reply if the user have the access or an "AccessDenied" if the the user is not provided with the given access right(s). Other errors also might be possible, for example the request might be mallformad of the object might be missing. Here is the reply showing the user has the given rights:

{
"request_created": "2017-12-06T12:08:53.704Z",
"request_id": 3,
"request_processed": "2017-12-06T12:08:53.717Z",
"request_status": "Ok",
"request_user_id": 3
}

chown

Changes the ownership of object(s).

{
"operation": "chown",
"owner_group_name": "users",
"owner_user_name": "pipas",
"path": "/core2/containers",
"recursive": true,
"request_created": "2017-10-05T07:51:05.133Z",
"request_id": 3
}
  • owner_group_name: The group name of the new group owner. One of the owner_user_name and owner_group_name is mandatory.
  • owner_user_name: The username of the new owner. One of the owner_user_name and owner_group_name is mandatory.
  • path: The full path of the object to change.
  • recursive: If this propertyis true all the subitems of the tree will be changed too. If any of the change fail with access denied no change will be done on any of the tree items.

The reply to a successful chown request looks like this:

{
"acl": "user::rwx,group::rwx,other::rwx",
"object_name": "containers",
"object_path": "/core2/",
"owner_group_name": "users",
"owner_user_name": "pipas",
"request_created": "2017-10-05T07:51:05.133Z",
"request_id": 3,
"request_processed": "2017-10-05T07:51:05.208Z",
"request_status": "Ok",
"request_user_id": 3
}

mkdir

This call is for creating folders in the Cmon Directory Tree.

{
"operation": "mkdir",
"path": "/foldername",
"request_created": "2017-10-09T07:42:17.091Z",
"request_id": 3
}
  • path: The full path of the folder to create.
{
"request_created": "2017-10-09T07:42:17.091Z",
"request_id": 3,
"request_processed": "2017-10-09T07:42:17.143Z",
"request_status": "Ok",
"request_user_id": 3
}

rmdir

This call is for removing empty folders. This call will be returning with an error if the object at the given path is not an empty folder. To remove objects other than a folder please consider using the "delete" call. The "delete" call is also able to remove empty folders.

{
"operation": "rmdir",
"path": "/foldername",
"request_created": "2017-10-09T07:13:54.410Z",
"request_id": 3
}
  • path: The full path of the folder to remove.
{
"request_created": "2017-10-09T07:13:54.410Z",
"request_id": 3,
"request_processed": "2017-10-09T07:13:54.470Z",
"request_status": "Ok",
"request_user_id": 3
}

mkfile

This call can be used to create a file type CDT entry. Here is a call:

{
"operation": "mkfile",
"path": "/tmp/filename.txt",
"request_created": "2019-01-04T09:05:27.349Z",
"request_id": 17
}

Here is a reply showing a successful file operation:

{
"request_created": "2019-01-04T09:05:27.349Z",
"request_id": 17,
"request_processed": "2019-01-04T09:05:27.364Z",
"request_status": "Ok",
"request_user_id": 1
}

setContent

This call can be used to change the content string of a CDT entry. Currently only the file type CDT entries can have contents handled in this call. Here is an example call:

{
"content": "This is something.\n\n",
"operation": "setcontent",
"path": "/tmp/ak.txt",
"request_created": "2019-01-04T09:03:34.657Z",
"request_id": 13
}

Here is a reply reporting a successful operation:

{
"request_created": "2019-01-04T09:03:34.657Z",
"request_id": 13,
"request_processed": "2019-01-04T09:03:34.670Z",
"request_status": "Ok",
"request_user_id": 1
}

delete

This call is for deleting object from the tree. Once an object is deleted, it is deleted for good. If for example one performs a delete operation on a container it is deleted from the server. It is gone, it is nonexisting, it is a late container.

So be careful. :)

{
"operation": "delete",
"path": "/host04/containers/temporary",
"request_created": "2017-10-16T13:58:53.186Z",
"request_id": 3
}
  • path: The full path of the object to delete.
{
"request_created": "2017-10-16T13:58:53.186Z",
"request_id": 3,
"request_processed": "2017-10-16T13:58:56.179Z",
"request_status": "Ok",
"request_user_id": 3
}

User Database manipulation

The "/v2/users" path is to manipulate Cmon Users, users that are maintained by the Cmon controller. These users are used while setting up encrypted communication for the RPC v2 interface, so without this interface it is not possible to use the RPC v2 itself.

createUser

The createUser RPC call can be used to create a new Cmon User. In the call both the user's password and public kay can be specified so that the user can log in after the account is created.

{
"create_group": true,
"new_password": "p",
"operation": "createUser",
"user":
{
"class_name": "CmonUser",
"groups": [
{
"class_name": "CmonGroup",
"group_name": "testgroup"
} ],
"public_keys": [
{
"key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAxupGXCUCtoytVYBD1NKeAB+oWauxJzGYvunMGnVIqhjF/o8gokNf\nV0mcad022SJ/Mng/PICKKrUtdQoQ+blCTRpOJw44nA/NIfp8mWkeCNB9BjaXtMQz\nxRHMo3Cw6mpPbxgzpTLvpiVyn7i3RK+k97oSk3VDlWOGjmchaBrGqVD77BuzbUQW\nZRkkgHF70b7/8pOESWgo7YGlTE/qbQ1VddGaIatnpm/9cyAj3TJRR3OaUm56d8sm\n48b0IGQkESxjtdHuLcDSo5/Fp0DhAFkabWMRU8d3pBEIxpcOWOGjps50Au4y/46S\np9x0vnW6lLEAVHfWoRqqN2S21yJZAHYDFwIDAQAB\n-----END RSA PUBLIC KEY-----\n",
"name": "No Name"
} ],
"user_name": "pipas"
}
}

Here are the most important elements of the request:

  • create_group: If the groups of the user does not exist the controller will try to create the group on the fly. If the group could be created the user will be added to the group, if not the request will be failed.
  • new_password: The plain text password that the user can actually use to log in. This is not mandatory, if the new password is not provided no password will be set and the user won't be able to log in with a password until a valid password is set.

    Please note that the new_password is not a property of the CmonUser Class. The plain password will not be stored, only the encoded version of the password is a property of the CmonUser object, but even that is a hidden property, not seen from outside of the controller code.

  • groups: The user groups the user belongs to. This is not a mandatory argument, if the "create_group" is set to true, but no group is provided the controller will try to create a group that has the same name as the user and add the new user to this new group.

    It is important to note that every user must belong to at least one group the so called "primary group". If the user belongs to multiple groups the first group is the primary group.

    Theoretically this call can accept multiple groups, but this feature is not yet implemented.

  • public_keys: Zero or more keys the user can use to log in. This is not a mandatory argument, if no key is provided the user will not able to log in with a key, but might still be able to use the system with a password.

    The "public_keys" is actually a property of the CmonUser Class, but it is a hidden property, not readable only using special calls like the getKeys call.

  • key: The actual public key string.
  • name: The name of the key. The name is just a human readable string the user provided as a reminder.

The reply to a createUser request looks like this:

{
"request_created": "2017-07-04T12:49:50.896Z",
"request_id": 2,
"request_processed": "2017-07-04T12:49:50.954Z",
"request_status": "Ok",
"request_user_id": 1,
"user":
{
"class_name": "CmonUser",
"email_address": "[email protected]",
"groups": [
{
"class_name": "CmonGroup",
"group_id": 6,
"group_name": "testgroup"
} ],
"user_id": 11,
"user_name": "rpc_user"
}
}
  • request_user_id: This is just the user ID of the user who sent the request, the same as in any other replies.
  • user: All the details of the user that has been created.
  • user_id: The user ID of the user that just has been created. The only inmutable property of the user that can not be ever changed.

Please note that there is no password or public key in the reply. We handle those as a secret.

You may be interested how we store the user in the backend. Here is an example showing a stored user with the internal properties that are not visible from the outside (but might be set like the "password_encrypted" for example:

{
"class_name": "CmonUser",
"email_address": "[email protected]",
"first_name": "Laszlo",
"groups": [
{
"class_name": "CmonGroup",
"group_id": 4,
"group_name": "testgroup"
} ],
"last_login": "2017-09-06T08:45:55.588Z",
"last_name": "Pere",
"password_encrypted": "3babe525f514e13a421b1e2870824d8d3e08531c5db461b088aa8c4298a1eca8",
"password_format": "sha256",
"password_salt": "7cc254f8-1be8-e78d-765a-2e63339fc99a",
"public_keys": [
{
"key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAxupGXCUCtoytVYBD1NKeAB+oWauxJzGYvunMGnVIqhjF/o8gokNf\nV0mcad022SJ/Mng/PICKKrUtdQoQ+blCTRpOJw44nA/NIfp8mWkeCNB9BjaXtMQz\nxRHMo3Cw6mpPbxgzpTLvpiVyn7i3RK+k97oSk3VDlWOGjmchaBrGqVD77BuzbUQW\nZRkkgHF70b7/8pOESWgo7YGlTE/qbQ1VddGaIatnpm/9cyAj3TJRR3OaUm56d8sm\n48b0IGQkESxjtdHuLcDSo5/Fp0DhAFkabWMRU8d3pBEIxpcOWOGjps50Au4y/46S\np9x0vnW6lLEAVHfWoRqqN2S21yJZAHYDFwIDAQAB\n-----END RSA PUBLIC KEY-----\n",
"name": "No Name"
} ],
"user_id": 3,
"user_name": "pipas"
}

whoAmI

This call returns information about the authenticated user. The client application can send this call periodically to update the information about the user and the privileges the user has.

The request shall look like this:

{
"operation": "whoAmI",
"request_created": "2018-12-07T08:51:14.324Z",
"request_id": 3,
"with_extended_privileges": true
}

Here the elements of the request are:

  • with_extended_privileges: If the request has this field set to true the reply will contain some extra information about the user's rigts.

The reply is as follows:

{
"request_created": "2018-12-07T08:51:14.324Z",
"request_id": 3,
"request_processed": "2018-12-07T08:51:14.332Z",
"request_status": "Ok",
"request_user_id": 4,
"user":
{
"acl": "user::rwx,group::r--,other::r--",
"cdt_path": "/",
"class_name": "CmonUser",
"created": "2018-12-07T07:22:49.165Z",
"disabled": false,
"email_address": "[email protected]",
"first_name": "Laszlo",
"groups": [
{
"acl": "user::rwx,group::rw-,other::---",
"cdt_path": "/groups",
"class_name": "CmonGroup",
"created": "2018-12-07T07:22:49.153Z",
"group_id": 4,
"group_name": "testgroup",
"owner_group_id": 1,
"owner_group_name": "admins",
"owner_user_id": 1,
"owner_user_name": "system"
} ],
"last_failed_login": "",
"last_login": "2018-12-07T08:51:14.317Z",
"last_name": "Pere",
"n_failed_logins": 0,
"owner_group_id": 1,
"owner_group_name": "admins",
"owner_user_id": 4,
"owner_user_name": "pipas",
"suspended": false,
"timezone":
{
"class_name": "CmonTimeZone",
"name": "CET",
"offset": -3600,
"use_dst": false
},
"user_id": 4,
"user_name": "pipas"
},
"user_extended_privileges":
{
"can_create_clusters": true,
"can_execute_jobs": true,
"class_name": "CmonExtendedPrivileges"
}
}

The reply shows the user object and some extended privileges of that user. These privileges are not the property of the user but controller by the access rights of other CDT entries on the controller. So these extended privileges is a convenience check the return information about the effective privileges of several CDT entries.

The list of the extended privileges are under construction, the caller may expect more than shown in this example.

getUsers

A call to read the user database. In its basic form the RPC call looks like this:

{
"operation": "getUsers",
"request_created": "2017-06-26T06:42:12.902Z",
"request_id": 3
}

The reply contains the users managed by the controller:

{
"request_created": "2017-06-26T06:42:12.902Z",
"request_id": 3,
"request_processed": "2017-06-26T06:42:12.951Z",
"request_status": "Ok",
"request_user_id": 12,
"total": 12,
"users": [
{
"class_name": "CmonUser",
"email_address": "[email protected]",
"first_name": "System",
"groups": [
{
"class_name": "CmonGroup",
"group_id": 1,
"group_name": "admins"
} ],
"last_name": "User",
"user_id": 1,
"user_name": "system"
},
{
"class_name": "CmonUser",
"email_address": "",
"groups": [
{
"class_name": "CmonGroup",
"group_id": 3,
"group_name": "nobody"
} ],
"user_id": 2,
"user_name": "nobody"
},
{
"class_name": "CmonUser",
"email_address": "",
"groups": [
{
"class_name": "CmonGroup",
"group_id": 2,
"group_name": "users"
} ],
"user_id": 3,
"user_name": "pipas"
},
. . .
} ]
}
  • total: The total number of users managed by the controller.
  • users: A list of CmonUser class objects. To get more information about the user objects please check the CmonUser Class and CmonGroup Class sections.

setUser

The setUser call can be used to modify certain properties of existing Cmon users. Here us an example showing how to modify the email address of a user:

{
"operation": "setUser",
"request_created": "2017-06-26T07:25:16.198Z",
"request_id": 3,
"user":
{
"class_name": "CmonUser",
"email_address": "[email protected]",
"user_name": "system"
}
}
  • user: Identifies of the user and the properties to change. The "class_name" of this map should be "CmonUser" and the "user_name" should identify an existing user. Any other fields are properties the caller wants to change. Please check out the CmonUser Class section to see what properties can be changed.

The reply to this request will contain the request status and—is an error happened—the error string. If the request was successfuly processed the controller will send back the user object with all of its properties after the change:

{
"request_created": "2017-06-26T07:37:51.522Z",
"request_id": 3,
"request_processed": "2017-06-26T07:37:51.575Z",
"request_status": "Ok",
"request_user_id": 3,
"user":
{
"class_name": "CmonUser",
"email_address": "[email protected]",
"first_name": "System",
"groups": [
{
"class_name": "CmonGroup",
"group_id": 1,
"group_name": "admins"
} ],
"last_name": "User",
"user_id": 1,
"user_name": "system"
}
}
  • user: Shows the properties of the user after the change.

disable

This call will set the "disabled" flag for the user and so prevent the user from logging in.

The request goes like this:

{
"operation": "disable",
"request_created": "2017-10-30T08:25:15.685Z",
"request_id": 3,
"user":
{
"class_name": "CmonUser",
"user_name": "nobody"
}
}

The reply contains the user object the "disabled" flag set to true.

{
"request_created": "2017-10-30T08:25:15.685Z",
"request_id": 3,
"request_processed": "2017-10-30T08:25:16.535Z",
"request_status": "Ok",
"request_user_id": 4,
"user":
{
"cdt_path": "/",
"class_name": "CmonUser",
"created": "2017-10-27T12:34:47.333Z",
"disabled": true,
"first_name": "Default",
"groups": [
{
"cdt_path": "/groups",
"class_name": "CmonGroup",
"group_id": 3,
"group_name": "nobody"
} ],
"last_failed_login": "",
"last_name": "User",
"n_failed_logins": 0,
"owner_group_id": 1,
"owner_group_name": "admins",
"owner_user_id": 2,
"owner_user_name": "nobody",
"suspended": false,
"user_id": 2,
"user_name": "nobody"
}
}

deleteUser

This call can be used to delete an existing Cmon user.

{
"operation": "deleteUser",
"request_created": "2018-11-26T14:19:29.891Z",
"request_id": 3,
"user":
{
"class_name": "CmonUser",
"user_name": "laszlo"
}
}

enable

This call will clear the "disabled" flag for the user so the user will be able to log in. The "suspended" flag will also be cleared, the failed login counter set to 0 and the date&time of the last failed login gets deleted, so users who are suspended for failed login attempts will also be able to log in.

The request looks like this:

{
"operation": "enable",
"request_created": "2017-10-30T08:28:29.852Z",
"request_id": 3,
"user":
{
"class_name": "CmonUser",
"user_name": "nobody"
}
}

And the reply:

{
"request_created": "2017-10-30T08:28:29.852Z",
"request_id": 3,
"request_processed": "2017-10-30T08:28:29.912Z",
"request_status": "Ok",
"request_user_id": 4,
"user":
{
"cdt_path": "/",
"class_name": "CmonUser",
"created": "2017-10-27T12:34:47.333Z",
"disabled": false,
"first_name": "Default",
"groups": [
{
"cdt_path": "/groups",
"class_name": "CmonGroup",
"group_id": 3,
"group_name": "nobody"
} ],
"last_failed_login": "",
"last_name": "User",
"n_failed_logins": 0,
"owner_group_id": 1,
"owner_group_name": "admins",
"owner_user_id": 2,
"owner_user_name": "nobody",
"suspended": false,
"user_id": 2,
"user_name": "nobody"
}
}

changePassword

This call is of course for changing the password of an existing user.

{
"new_password": "newpass",
"old_password": "secret",
"operation": "changePassword",
"request_created": "2017-06-29T07:56:17.710Z",
"request_id": 2,
"user":
{
"class_name": "CmonUser",
"user_name": "system"
}
}
  • new_password: The new password in plain string format.
  • old_password: The current password in plain format. This is currently not required.
  • user: Identifies the user that should have the new password.
{
"request_created": "2017-06-29T07:56:17.710Z",
"request_id": 2,
"request_processed": "2017-06-29T07:56:17.761Z",
"request_status": "Ok",
"request_user_id": 1
}

getKeys

A call to return the public keys of a user. Here is an example showing a getKeys request:

{
"operation": "getKeys",
"request_created": "2017-07-05T09:23:04.988Z",
"request_id": 2,
"user":
{
"class_name": "CmonUser",
"user_name": "pipas"
}
}
  • user: This identifies the user whose public keys will be returned. Only the class name and the user name needed here.

    Please note that the controller handles the public keys securely, normal users can not read the public keys of other users. Users can always read their own keys of course.

Here is an example of the reply to the getKeys request:

{
"public_keys": [
{
"key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAxupGXCUCtoytVYBD1NKeAB+oWauxJzGYvunMGnVIqhjF/o8gokNf\nV0mcad022SJ/Mng/PICKKrUtdQoQ+blCTRpOJw44nA/NIfp8mWkeCNB9BjaXtMQz\nxRHMo3Cw6mpPbxgzpTLvpiVyn7i3RK+k97oSk3VDlWOGjmchaBrGqVD77BuzbUQW\nZRkkgHF70b7/8pOESWgo7YGlTE/qbQ1VddGaIatnpm/9cyAj3TJRR3OaUm56d8sm\n48b0IGQkESxjtdHuLcDSo5/Fp0DhAFkabWMRU8d3pBEIxpcOWOGjps50Au4y/46S\np9x0vnW6lLEAVHfWoRqqN2S21yJZAHYDFwIDAQAB\n-----END RSA PUBLIC KEY-----\n",
"name": "Unnamed key"
} ],
"request_created": "2017-07-05T09:23:04.988Z",
"request_id": 2,
"request_processed": "2017-07-05T09:23:05.034Z",
"request_status": "Ok",
"request_user_id": 1,
"total": 1,
"user":
{
"class_name": "CmonUser",
"email_address": "[email protected]",
"first_name": "Laszlo",
"groups": [
{
"class_name": "CmonGroup",
"group_id": 4,
"group_name": "testgroup"
} ],
"last_login": "2017-07-05T09:22:55.587Z",
"last_name": "Pere",
"title": "",
"user_id": 3,
"user_name": "pipas"
}
}
  • public-keys: Contains the public keys of the user.
  • key: The actual key string.
  • name: The human readable name of the key, a reminder that the user set when adding the key.
  • total: Shows how many keys has been found.
  • user: The user that has these keys. This field is sent back for convenience.

addKey

A call to add/register a new public key for a user.

{
"operation": "addKey",
"public_key":
{
"key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAr6KS5KsnwVuoOzFGJsF2oPhUokBEEdrcGfVPX5XbdlIn0E/Gn44i\nx4HBt0rDKNe//tbDPBPItqYyBJInKwwaSmT5u68WiD9XKd4faDUImogmV/eVEod2\nMHkcvkg6a9zYDnSVj8QsCJgN865HEsEPgmShw3Kuqhy3c4h33pbvjbzIuX8d6svt\ncGLhMpL9Lia3WEf8ef2yd78rUY6KqNMcUdOyUJU1p0bsLLjXxOcCAArcBk1LbRVL\n0g3uBEYGzuCrx+JARPWu+Oih+z/ad4HC8hXZXW2ESXO769P1rOoWX4CRu/AkamN6\nQNfAM7WOdTgUrv3YXXBOu5oF0aAmEUIUcQIDAQAB\n-----END RSA PUBLIC KEY-----\n",
"name": ""
},
"request_created": "2017-07-05T11:55:57.885Z",
"request_id": 3,
"user":
{
"class_name": "CmonUser",
"user_name": "pipas"
}
}
  • public_key: The public key to add.
  • user: The user to whom the key will be added. Only the class name and the user name has to be specified here.

The reply to this request will look like this:

{
"request_created": "2017-07-05T11:55:57.885Z",
"request_id": 3,
"request_processed": "2017-07-05T11:55:57.939Z",
"request_status": "Ok",
"request_user_id": 3
}

deleteKey

This call can be used to delete a public key by its name. The call looks very similar to the "addKey" call:

{
"operation": "deleteKey",
"public_key":
{
"name": "The name"
},
"request_created": "2017-07-05T12:35:43.135Z",
"request_id": 3,
"user":
{
"class_name": "CmonUser",
"user_name": "pipas"
}
}

createGroup

{
"operation": "createGroup",
"group":
{
"class_name": "CmonGroup",
"group_name": "GroupName"
}
}

This RPC call is untested.

deleteGroup

This call is for deleting an existing Cmon Group. Groups can only be created if they are empty (no Cmon User is assigned to them) and the authenticateduser has a write access to the actual group itself.

Currently only the group name is used to identify the group that will be deleted.

{
"group":
{
"class_name": "CmonGroup",
"group_name": "pip"
},
"operation": "deleteGroup",
"request_created": "2019-08-05T07:44:39.972Z",
"request_id": 3
}
{
"request_created": "2019-08-05T07:44:39.972Z",
"request_id": 3,
"request_processed": "2019-08-05T07:44:39.995Z",
"request_status": "Ok",
"request_user_id": 4
}

getGroups

A call to read the user database. In its basic form the RPC call looks like this:

{
"operation": "getGroups",
"request_created": "2017-06-26T06:42:12.902Z",
"request_id": 3
}
{
"groups": [
{
"class_name": "CmonGroup",
"group_id": 1,
"group_name": "admins"
},
{
"class_name": "CmonGroup",
"group_id": 2,
"group_name": "users"
},
. . .
{
"class_name": "CmonGroup",
"group_id": 6,
"group_name": "rpc_group"
} ],
"reply_received": "2017-07-24T08:53:33.498Z",
"request_created": "2017-07-24T08:53:33.493Z",
"request_id": 3,
"request_processed": "2017-07-24T08:53:33.538Z",
"request_status": "Ok",
"request_user_id": 3,
"total": 6
}
  • groups: The list of user groups the controller has.
  • total: The total number of groups the controller has.

addToGroup

Adds the user to a new group or moves the user from the current primary group to a new group.

{
"group_name": "admins",
"operation": "addToGroup",
"replace_primary_group": true,
"request_created": "2018-01-12T11:07:48.963Z",
"request_id": 2,
"user":
{
"class_name": "CmonUser",
"user_name": "pipas"
}
}
{
"request_created": "2018-01-12T11:07:48.963Z",
"request_id": 2,
"request_processed": "2018-01-12T11:07:48.984Z",
"request_status": "Ok",
"request_user_id": 1,
"user":
{
"cdt_path": "/",
"class_name": "CmonUser",
"created": "2018-01-12T07:26:13.677Z",
"disabled": false,
"email_address": "[email protected]",
"first_name": "Laszlo",
"groups": [
{
"cdt_path": "/groups",
"class_name": "CmonGroup",
"group_id": 1,
"group_name": "admins"
} ],
"last_failed_login": "",
"last_login": "2018-01-12T10:54:53.222Z",
"last_name": "Pere",
"n_failed_logins": 0,
"owner_group_id": 1,
"owner_group_name": "admins",
"owner_user_id": 3,
"owner_user_name": "pipas",
"suspended": false,
"user_id": 3,
"user_name": "pipas"
}
}

removeFromGroup

Removes the user from a group if it is indeed the member of that group. Here is how the rquest should look like:

{
"group_name": "admins",
"operation": "removeFromGroup",
"request_created": "2018-10-08T06:03:36.983Z",
"request_id": 3,
"user":
{
"class_name": "CmonUser",
"user_name": "sisko"
}
}

And here is the reply:

{
"request_created": "2018-10-08T06:03:36.983Z",
"request_id": 3,
"request_processed": "2018-10-08T06:03:37.006Z",
"request_status": "Ok",
"request_user_id": 4,
"user":
{
"acl": "user::rwx,user:pipas:rwx,group::r--,other::r--",
"cdt_path": "/",
"class_name": "CmonUser",
"created": "2018-10-05T14:09:14.660Z",
"disabled": false,
"email_address": "[email protected]",
"first_name": "Benjamin",
"groups": [
{
"cdt_path": "/groups",
"class_name": "CmonGroup",
"group_id": 5,
"group_name": "ds9",
"owner_group_id": 1,
"owner_group_name": "admins",
"owner_user_id": 1,
"owner_user_name": "system"
} ],
"last_failed_login": "",
"last_login": "2018-10-05T14:09:18.111Z",
"last_name": "Sisko",
"n_failed_logins": 0,
"owner_group_id": 1,
"owner_group_name": "admins",
"owner_user_id": 5,
"owner_user_name": "sisko",
"suspended": false,
"title": "Captain",
"user_id": 5,
"user_name": "sisko"
}
}

canCreateUser

This call can be used to find out if the currently authenticated user is allowed to create a new user. For the user creation the authenticated user must be the superuser or has write access to a user group and a folder.

{
"operation": "canCreateUser",
"request_created": "2017-10-19T08:02:53.276Z",
"request_id": 3
}

Here is the answer. Please note that the request will be successfull (the request_status will be "Ok") even if the user can not possibly create a new cmon user.

{
"can_create_user": true,
"folders_available_for_write": [ "/", "/core1/containers" ],
"groups_available_for_write": [ "admins", "users", "nobody" ],
"request_created": "2017-10-19T08:02:53.276Z",
"request_id": 3,
"request_processed": "2017-10-19T08:02:56.341Z",
"request_status": "Ok",
"request_user_id": 4
}
  • can_create_user: True if the authenticated user has a chance to create a new user.
  • folders_available_for_write: The folders that are writable and so can hold a new user. The full path of the folders are enumerated in a list.
  • groups_available_for_write: The names of the groups that are writable for the user and so can hold the new user.

canCreateGroup

This call is to decide if the authenticated user is allowed to create user groups.

{
"operation": "canCreateGroup",
"request_created": "2017-10-19T09:49:43.632Z",
"request_id": 3
}
{
"can_create_group": true,
"folders_available_for_write": [ "/", "/core1/containers", "/galera_001/databases" ],
"request_created": "2017-10-19T09:49:43.632Z",
"request_id": 3,
"request_processed": "2017-10-19T09:49:46.692Z",
"request_status": "Ok",
"request_user_id": 4
}
  • can_create_group: True if the user is allowed to create cmon user groups.
  • folders_available_for_write: The list of folders the user has write access.