Key Takeaways
|
|
The UIMAPI is a RESTful API that allows you to perform almost any action in your DX UIM environment programmatically. The Swagger front-end serves as a guide, enabling you to execute REST endpoints manually, but many customers prefer to automate these actions using a program.
This blog post focuses on using a Java program to interact with the API, and includes examples. However, you can use any programming language that supports a RESTful interface. A RESTful endpoint is essentially a URL that supports a few actions: GET, POST, PUT, and DELETE. For UIMAPI, you will also need credentials. Most GET actions can be performed via a web browser by navigating to the URL. The Swagger documentation for UIMAPI helps you understand the type of each endpoint:
The scenario covered in this blog involves putting a device in maintenance mode for a specified period, which may not cover the entire maintenance window. We will define a maintenance window, and then add and remove the device from maintenance mode.
Note: All maintenance mode actions are directed at the maintenance_mode probe running on the primary hub, never to the device. All examples use the base URL to my localhost/UIMAPI endpoints but they will also work with a remote hostname/IP address as well:
All examples are using JSON but the UIMAPI also supports XML. |
All UIMAPI endpoints require authentication, which is a DX UIM user with Web Service ACL permissions, Base64 encoded as a single string {user}:{password}. This can be done once at the start of your program and the encoded string can be passed to every endpoint call. The bytesEncoded string is sent to each endpoint call made.
The java code example:
// Encode the credentials – define user & pwd variables with your credentials
bytesEncoded = Base64.getEncoder().encodeToString((user+":"+pwd).getBytes());
A certificate may be required if using the HTTPS protocol. The process of adding a certificate to a keystore is detailed in the DX UIM documentation. Every endpoint call in these examples requires the keystore and the keystore password to establish a secure connection.
If using the HTTP protocol, set the keystore and keystore password values to null.
To put a device in maintenance mode, first create a maintenance window using the add_schedule endpoint. POST the definition of the new schedule in a payload. The add_schedule POST endpoint requires environment values in the URL string, plus the payload:
{base_url}/maintenance_mode/{domin}/{hub}/{robot}/add_schedule/
These values point to the primary hub where the maintenance_mode probe is running. In this example, the URL will be:
{base_url}/maintenance_mode/Validation/Office/primary/add_schedule/
The payload is the actual schedule definition, which must be in the future from when you create it, considering any time zone differences. The payload is:
payload = "{"
+ " \"name\": \"ExampleBlogWindow\","
+ " \"description\": \"Example Schedule for Blog\","
+ " \"account_id\": \"-1\","
+ " \"start_date_time\": {"
+ " \"month\": 9,"
+ " \"day\": 5,"
+ " \"year\": 2024,"
+ " \"timestamp\": {"
+ " \"hours\": 10,"
+ " \"minutes\": 35,"
+ " \"seconds\": 39"
+ " }"
+ " },"
+ " \"end_time\": {"
+ " \"type\": \"end_date_time\","
+ " \"end_date_time\": {"
+ " \"month\": 9,"
+ " \"day\": 5,"
+ " \"year\": 2024,"
+ " \"timestamp\": {"
+ " \"hours\": 23,"
+ " \"minutes\": 59,"
+ " \"seconds\": 59"
+ " }"
+ " },"
+ " \"duration\": {"
+ " \"hours\": 1,"
+ " \"minutes\": 0,"
+ " \"seconds\": 0"
+ " }"
+ " },"
+ " \"recurrence_pattern\": \"daily\","
+ " \"recurrence_period\": \"1\","
+ " \"recurrence_days_of_the_week\": \"\","
+ " \"recurrence_day_of_the_month\": \"0\","
+ " \"recurrence_end_date_time\": {"
+ " \"month\": 0,"
+ " \"day\": 0,"
+ " \"year\": 0,"
+ " \"timestamp\": {"
+ " \"hours\": 0,"
+ " \"minutes\": 0,"
+ " \"seconds\": 0"
+ " }"
+ " },"
+ " \"recurrence_instance\": \"1\","
+ " \"timezone\": \"Europe/London\""
+ "}";
Using the URL (updated with your environment) plus the payload, POST the schedule definition (payload) along with the encoded credentials (bytesEncoded).
Here is the Java code example:
// Add a new maintenance schedule
responseBody = RestCalls.runRestPost(baseUrl, "/maintenance_mode/Validation/Office/primary/add_schedule/", payload, bytesEncoded, keystore, keypwd);
System.out.println("Added Schedule: " + responseBody);
This will output the schedule ID:
Added Schedule: {
"schedule_id" : "6"
}
You can view the schedule in the Operator Console by selecting a group and navigating to Maintenance:
Edit Maintenance to view the details:
Adding a device to a maintenance window requires the device and schedule IDs. This involves querying existing data using a GET endpoint. This does not require a payload but still requires the encoded credentials and no changes to the URL. Here are the steps:
// Get a list of devices
responseBody = RestCalls.runRestGet(baseUrl, "/devices", "", bytesEncoded, keystore, keypwd);
JSONArray array = new JSONArray(responseBody);
System.out.println("Number of devices: " +array.length());
name = null;
id = 0;
// Looking for device centos4 - change this for a device of your own
for (int i = 0 ; i < array.length() ; i++){
name = array.getJSONObject(i).getString("name");
id = array.getJSONObject(i).getInt("id");
if (name.compareToIgnoreCase("centos4") == 0) {
System.out.println("Name: " + name + " - ID: " + id);
scheduled_device_id = id;
scheduled_device_name = name;
break;
}
}
Number of devices: 18
Name: centos4 - ID: 22
{base_url}/maintenance_mode/{domin}/{hub}/{robot}/get_all_maintenance_schedules/
{base_url}/maintenance_mode/Validation/Office/primary/get_all_maintenance_schedules/
// Get a list of all schedules
responseBody = RestCalls.runRestGet(baseUrl, "/maintenance_mode/Validation/Office/primary/get_all_maintenance_schedules", "", bytesEncoded, keystore, keypwd);
System.out.println("Schedules: " + responseBody);
aa = new JSONObject(responseBody);
array = aa.getJSONArray("maintenanceSchedules");
System.out.println("length: " +array.length());
name = null;
id = 0;
for (int i = 0 ; i < array.length() ; i++){
name = array.getJSONObject(i).getString("name");
id = array.getJSONObject(i).getInt("id");
System.out.println("Name: " + name + " - ID: " + id);
if (name.compareToIgnoreCase("ExampleBlogWindow") == 0) {
maintenance_schedule_id = id;
break;
}
}
Schedules: {
"maintenanceSchedules" : [ {
"id" : 6,
"name" : "ExampleBlogWindow",
"description" : "Example Schedule for Blog",
"recurrenceDayOfTheMonth" : 5,
"recurrencePeriod" : 1,
"recurrencePattern" : "Daily",
"recurrenceEndTime" : "",
"timeZone" : "Europe/London",
"duration" : 804,
"accountId" : -1,
"startTime" : "2024-09-05 10:35:39.747",
"recurrenceInstance" : 1
} ]
}
length: 1
Add device to the maintenance schedule using the add_computer_systems_to_schedule POST endpoint. The payload is an array of devices; one or many can be specified in the same call. This example only adds the centos4 device identified by the cs_id saved in the scheduled_device_id variable.
The add_computer_systems_to_schedule POST endpoint requires environment values in the URL string itself:
{base_url}/maintenance_mode/{domin}/{hub}/{robot}/add_computer_systems_to_schedule/{schedule id}
These values point to the primary hub in which the maintenance_mode probe is running. Plus, the schedule id value is also required. In this example, the URL will be:
{base_url}/maintenance_mode/Validation/Office/primary/add_computer_systems_to_schedule/6
Using the URL (updated with your environment), POST the device id in the payload plus the encoded credentials (bytesEncoded):
// Add device(s) to the schedule – JSON array comma separated list: { "cs" : [ 16, 3, 9, 11 ] }
payload = "{\"cs\":["+ scheduled_device_id + "]}";
responseBody = RestCalls.runRestPost(baseUrl, "/maintenance_mode/Validation/Office/primary/add_computer_systems_to_schedule/6", payload, bytesEncoded, keystore, keypwd);
This can be viewed in the schedule:
Viewing the device details will show the maintenance schedules for this device:
To list the devices already in the maintenance schedule and confirm the device has been added, use the get_computer_systems_for_maintenance_schedule GET endpoint. No payload is required. Once the list of devices is returned, loop through to find your device using the scheduled_device_id variable.
The add_computer_systems_to_schedule GET endpoint requires environment values in the URL string itself:
{base_url}/maintenance_mode/{domin}/{hub}/{robot}/get_computer_systems_for_maintenance_schedule/{schedule id}
These values point to the primary hub where the maintenance_mode probe is running. Plus, the schedule id value is also required. In this example, the URL will be:
{base_url}/maintenance_mode/Validation/Office/primary/get_computer_systems_for_maintenance_schedule/6
Using the URL (updated with your environment), GET schedule devices using the encoded credentials (bytesEncoded):
responseBody = RestCalls.runRestGet(baseUrl, "/maintenance_mode/Validation/Office/primary/get_computer_systems_for_maintenance_schedule/6", "", bytesEncoded, keystore, keypwd);
System.out.println("Devices: " + responseBody);
aa = new JSONObject(responseBody);
array = aa.getJSONArray("cs");
System.out.println("Number of devices in the schedule: " +array.length());
name = null;
id = 0;
add_device = true;
for (int i = 0 ; i < array.length() ; i++){
System.out.println("cs_id: " + array.getInt(i));
if (array.getInt(i) == scheduled_device_id) {
System.out.println("Device centos4(" + scheduled_device_id + ") Found in this Maintenance Schedule");
add_device = false;
break;
}
}
Output for this environment:
Number of devices in the schedule: 1
cs_id: 22
Device centos4(22) Found in this Maintenance Schedule
To remove device(s) from a maintenance schedule, use the remove_computer_systems_from_schedule POST endpoint. The payload is an array of devices, which is a comma separated list, e.g., { "cs" : [ 16, 3, 9, 11 ] }. One or many can be specified in the same call. This example only removes the centos4 device, which is identified by the cs_id saved in the scheduled_device_id variable.
The remove_computer_systems_to_schedule POST endpoint requires environment values in the URL string itself:
{base_url}/maintenance_mode/{domin}/{hub}/{robot}/remove_computer_systems_from_schedule/{schedule id}
These values point to the primary hub where the maintenance_mode probe is running. Plus, the schedule id value is also required. In this example, the URL will be:
{base_url}/maintenance_mode/Validation/Office/primary/remove_computer_systems_from_schedule/6
Using the URL (updated with your environment), remove the device from the schedule with the payload and encoded credentials (bytesEncoded):
payload = "{\"cs\":["+ scheduled_device_id + "]}";
responseBody = RestCalls.runRestPost(baseUrl, "/maintenance_mode/Validation/Office/primary/remove_computer_systems_from_schedule/6", payload, bytesEncoded, keystore, keypwd);
The device is removed from the schedule and no longer in maintenance mode:
Viewing the device details will show there are no maintenance schedules for this device:
Here are all the REST methods used in the examples. These methods are in a class named RestCalls.
public static String runRestPost(String baseUri, String path, String payload, String credentials, String keystore, String keypwd) throws ClientProtocolException, IOException {
String responseBody = null;
CloseableHttpClient client = setupHttpClient(baseUri, keystore, keypwd);
logger.log(NimLog.DEBUG, "URL: "+ baseUri+path);
HttpPost request = new HttpPost(baseUri+path);
request.addHeader(HttpHeaders.AUTHORIZATION,"Basic " + credentials);
request.setHeader(HttpHeaders.ACCEPT, "application/json");
request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
request.setEntity(new StringEntity(payload, StandardCharsets.UTF_8));
HttpResponse response = client.execute(request);
int rc = response.getStatusLine().getStatusCode();
//System.out.println("RC: " + rc + " : response: " +response);
if (response.getEntity() != null) {
responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
}
if (rc >= 200 && rc < 300) {
// do some error checking here
return responseBody;
} else {
logger.log(NimLog.ERROR, "Got response code "+rc+" from method: "+response.getStatusLine().getReasonPhrase());
}
return null;
}
public static String runRestGet(String baseUri, String path, String payload, String credentials, String keystore, String keypwd) throws ClientProtocolException, IOException {
String responseBody = null;
CloseableHttpClient client = setupHttpClient(baseUri, keystore, keypwd);
HttpGet request = new HttpGet(baseUri+path+payload);
request.addHeader(HttpHeaders.AUTHORIZATION,"Basic " + credentials);
request.setHeader(HttpHeaders.ACCEPT, "application/json");
request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
HttpResponse response = client.execute(request);
int rc = response.getStatusLine().getStatusCode();
responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
//System.out.println("Response body: " + responseBody);
if (rc >= 200 && rc < 300) {
return responseBody;
} else {
logger.log(NimLog.ERROR, "Got response code "+rc+" from method: "+response.getStatusLine().getReasonPhrase());
}
return responseBody;
}
private static CloseableHttpClient setupHttpClient(String baseUri, String keyfile, String keypwd) {
CloseableHttpClient httpclient = null;
try {
if (baseUri.startsWith("https")) {
logger.log(NimLog.DEBUG, "setupHttpClient: Setting up https client");
//Creating SSLContextBuilder object
SSLContextBuilder SSLBuilder = SSLContexts.custom();
// Loading the Keystore file
File file = new File(keyfile);
// Update the password for your key file
SSLBuilder = SSLBuilder.loadTrustMaterial(file,"#####".toCharArray());
// Building the SSLContext
SSLContext sslcontext = SSLBuilder.build();
// Creating SSLConnectionSocketFactory object
SSLConnectionSocketFactory sslConSocFactory = new SSLConnectionSocketFactory(sslcontext, new NoopHostnameVerifier());
// Creating HttpClientBuilder
HttpClientBuilder clientbuilder = HttpClients.custom();
// Setting the SSLConnectionSocketFactory
clientbuilder = clientbuilder.setSSLSocketFactory(sslConSocFactory);
// Building the CloseableHttpClient
httpclient = clientbuilder.build();
return httpclient;
} else {
logger.log(NimLog.DEBUG, "setupHttpClient: Setting up http client");
httpclient = HttpClients.custom()
.setConnectionTimeToLive(20, TimeUnit.SECONDS)
.setMaxConnTotal(400).setMaxConnPerRoute(400)
.setDefaultRequestConfig(RequestConfig.custom()
.setSocketTimeout(120000).setConnectTimeout(5000).build())
.setRetryHandler(new DefaultHttpRequestRetryHandler(5, true))
.build();
return httpclient;
}
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException | CertificateException | IOException e) {
logger.log(NimLog.ERROR, "setupHttpClient: Exception trying to create the http client: " + e.getLocalizedMessage());
logger.logStackTrace(NimLog.ERROR, e);
}
return httpclient;
}