RightScale Integration with ServiceNow

I had a customer ask some questions around feasibility of integrating with RightScale, a cloud-based software company that specializes in managing multiple cloud-base infrastructures in one location. The customer was hoping to implement the integration with our Runbook Automation workflows as well. While this post will not cover the situation of RightScale within workflow, we will talk about potential functions or libraries that you can create that could be consumed by “Run Script” workflow activities or business rules and beyond.

The RightScale API Overview

RightScale uses a REST based web service interface. The API supports a username/password authentication scenario which I used. Essentially, before issuing API commands, you need to perform a Login web service call to receive a token. Once you have received the token, you use that token in subsequent web service calls. At the time of this article, the default token life is about two hours since the last use.

In this article we use RightScale with an Amazon EC2 account that I play with.

For more information on the RightScale REST API, feel free to visit: RightScale REST API Documentation.

Login

Let’s cover the login. The login takes place by using a RESTful GET command to the Login Resource. This resource is in the following format: https://my.rightscale.com/api/acct/ACCOUNT_NUMBER/login/. Substitude the ACCOUNT_NUMBER text with your RightScale account number. This request will require Basic Authentication where you use your RightScale username and password.

The following is the REST Message “get” record that I designed for this call:

Function: get
REST Endpoint: https://my.rightscale.com/api/acct/${account}/login/
Use Basic auth: True
Basic auth user ID: myemailaddress@mycompany.com
Basic auth user password: mypassword

REST Message Function Headers

  • X-API-VERSION : 1.0

Here is what it looks like in the UI:

Login - Get - RightScale

Notice the “${account}” variable that I have in my REST endpoint URL. This will be replaced with the value of the account number when a script executes this web service call.

Now that we have a REST Message record, we can write some script that can be reused within other areas of the product to obtain and authentication token for RightScale.

When the request is made, one of the Response HTTP Headers will contain cookie information that includes a session key. The cookie information is returned in the “Set-Cookie” header. The value of that cookie string will look like this:

rs_gbl=eNotkFtvgjAYhv_K0mswlGMlWTIymUwcnjJrdmNKaRGBMk4KM_731cSb7-Z93jf5nhsgwAXlCBSQtMC9gb5lDXBtCxrmXQEdBS40LGRABxpQAVki6Sli3EbEVC0uD4RMVx
GMHTWeMm7EFuGONpV7HXt2ddN4dOU8ONHerD7SfeCkzvZQrLDGONb08jvX0FdYhYl2nZlB
cawbux5XNApWASJj0W_W2zDp2Tz_dZan3sC1l2JzZ_mLat8FtRdhgeM9gpfkVy3ercs5D5t
PSqJoE_y1y7Dk-Mryyt8m1oGr580RD0Pn8d6fpT87Z-Pjss9o5g1GOD-IdbBE9lhqrw8lw0OJIKV8BSyqk3jxRMKalgkZEkqrXnRSlol0pABWkq
yQ3FlyE_Lk3qTOS0aZqK4TWpXgfv8H8-Nzpg%3D%3D
; domain=.rightscale.com; path=/; HttpOnly

The portion of the string that is in “bold” is the actual session token.

Here is a sample example of calling the REST API for login, grabbing the Set-Cookie response header, and parsing out the session key:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var account = "55555";

//Login and get a Session ID
var sessionID = login(account);

function login(account) {
    //Make the REST Call
    var r = new RESTMessage('RightScale Login', 'get');
    r.setStringParameter("account", account);
    var response = r.execute();

    //The cookie for the session ID is found in the response Header
    var cookie = response.getHeader("Set-Cookie");

    //Break out the session ID from the cookie string
    var tokens = /rs_gbl\=(.*)\;.*domain.*/ (cookie);
    if (tokens && tokens.length > 1) {
        var sessionKey = tokens[1];
        gs.log("Session Key: " + sessionKey);
        return sessionKey;
    }
    return "";
}

Create a Server in Amazon EC2 Cloud

Now that we can get a session token, let’s do something real. We are going to use the API to create a Server object in RightScale for my Amazon EC2 cloud.

The creation of a Server takes place by using a RESTful POST command to the “servers” Resource. This resource is in the following format: https://my.rightscale.com/api/acct/ACCOUNT_NUMBER/servers. Substitude the ACCOUNT_NUMBER text with your RightScale account number.

The following is the REST Message “post” record that I designed for this call:

Function: post
REST Endpoint: https://my.rightscale.com/api/acct/${account}/servers
Use Basic auth: False

REST Message Function Headers

  • X-API-VERSION : 1.0
  • Authorization : Bearer ${sessionKey}

Content: (No line breaks)
server[deployment_href]=${deployment}&server[nickname]=${nickname}
&server[server_template_href]=${serverTemplate}
&server[ec2_ssh_key_href]=${sshkey}&server[instance_type]=
${instanceType}&cloud_id=${cloudID}&server[vpc_subnet_href]={$vpcSubnet}

Notice that I have variables in the REST Endpoint (for account information), in the Authorization function header (to submit the session key), and throughout the content (for information about the server we are going to create).

The GUI for this record might look like:

Create New RightScale Server

After making a request with this information, the web service should return a status code of 201 along with an HTTP Header for “Location” which contains a URL for the server object that is created.

Here is some sample code that could be used to test this feature out (Please note that I reuse functions from the previously listed code):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//Set up information for an Amazon EC2 Server
var server = new Object();
server.deployment = "https://my.rightscale.com/api/acct/64828/deployments/345576001";
server.nickname = "RESTTest1";
server.sshKey = "https://my.rightscale.com/api/acct/64828/clouds/1/ec2_ssh_keys/340675001";
server.instanceType = "t1.micro";
server.serverTemplate = "https://my.rightscale.com/api/acct/64828/server_templates/280453001";
server.cloudID = "1";
server.vpcSubnet = "https://my.rightscale.com/api/acct/64828/clouds/1/vpcs/202414001";

//Create the EC2 Server
var serverLoc = createServer(account, sessionID, server);


function createServer(account, sessionID, server) {

    //Set up and send the REST Message
    var r = new RESTMessage('RightScale', 'post');
    r.setStringParameter("sessionKey", sessionID);
    r.setStringParameter("account", account);
    r.setStringParameter('deployment', server.deployment);
    r.setStringParameter('nickname', server.nickname);
    r.setStringParameter('sshkey', server.sshKey);
    r.setStringParameter('instanceType', server.instanceType);
    r.setStringParameter('serverTemplate', server.serverTemplate);
    r.setStringParameter('cloudID', server.cloudID);
    r.setStringParameter('vpcSubnet', server.vpcSubnet);
    var response = r.execute();

    //Check the Status Code.  201 indicates that it created
    if (response.getStatusCode() == "201") {
        var loc = response.getHeader("Location");
        return loc;
    } else {
        return false;
    }
}

Delete a Server

We can also use the API to delete a Server object in RightScale for my Amazon EC2 cloud.

The deletion of a Server takes place by using a RESTful DELETE command to the “servers” Resource. This resource is in the following format: https://my.rightscale.com/api/acct/ACCOUNT_NUMBER/servers/SERVER_ID. Substitude the ACCOUNT_NUMBER text with your RightScale account number and SERVER_ID with the ID (not full URL) of the server object you want to delete..

The following is the REST Message “delete” record that I designed for this call:

Function: delete
REST Endpoint: https://my.rightscale.com/api/acct/${account}/servers/${serverID}
Use Basic auth: False

REST Message Function Headers

  • X-API-VERSION : 1.0
  • Authorization : Bearer ${sessionKey}

The UI representation for this record might look like:

Delete a RightScale Server

Here is some code that we could leverage to delete a server using our web service:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//Delete a specific Server
var deleted = deleteServer(account, sessionID, serverID);
if (deleted) {
    gs.log(serverLoc + " was deleted Properly");
} else {
    gs.log(serverLoc + " could not be deleted");
}

function deleteServer(account, sessionID, serverLoc) {
    //Set up and submit the REST Message for deletion
    var r = new RESTMessage('RightScale', 'delete');
    r.setStringParameter("sessionKey", sessionID);
    r.setStringParameter("account", account);
    r.setStringParameter("serverID", serverLoc);
    var response = r.execute();

    //The Delete response should be 200 if successful
    if (response.getStatusCode() == "200") {
        return true;
    } else {
        return false;
    }
}

Round Trip Coding Example

Let’s take the various scripts that compiled above and write a script that authenticates, creates a server object, and then deletes that server object:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
var account = "55555";

//Login and get a Session ID
var sessionID = login(account);

if (sessionID) {
    //Set up information for an Amazon EC2 Server
    var server = new Object();
    server.deployment = "https://my.rightscale.com/api/acct/64828/deployments/345576001";
    server.nickname = "RESTTest1";
    server.sshKey = "https://my.rightscale.com/api/acct/64828/clouds/1/ec2_ssh_keys/340675001";
    server.instanceType = "t1.micro";
    server.serverTemplate = "https://my.rightscale.com/api/acct/64828/server_templates/280453001";
    server.cloudID = "1";
    server.vpcSubnet = "https://my.rightscale.com/api/acct/64828/clouds/1/vpcs/202414001";

    //Create the EC2 Server
    var serverLoc = createServer(account, sessionID, server);
    if (serverLoc) {
        gs.log("Server Location: " + serverLoc);

        //Break out the Server ID from the Server Location URL
        var serverID = getID(serverLoc);

        //Delete the Server that was just created
        var deleted = deleteServer(account, sessionID, serverID);
        if (deleted) {
            gs.log(serverLoc + " was deleted Properly");
        } else {
            gs.log(serverLoc + " could not be deleted");
        }
    }
}

function login(account) {
    //Make the REST Call
    var r = new RESTMessage('RightScale Login', 'get');
    r.setStringParameter("account", account);
    var response = r.execute();

    //The cookie for the session ID is found in the response Header
    var cookie = response.getHeader("Set-Cookie");

    //Break out the session ID from the cookie string
    var tokens = /rs_gbl\=(.*)\;.*domain.*/ (cookie);
    if (tokens && tokens.length > 1) {
        var sessionKey = tokens[1];
        gs.log("Session Key: " + sessionKey);
        return sessionKey;
    }
    return "";
}

function createServer(account, sessionID, server) {

    //Set up and send the REST Message
    var r = new RESTMessage('RightScale', 'post');
    r.setStringParameter("sessionKey", sessionID);
    r.setStringParameter("account", account);
    r.setStringParameter('deployment', server.deployment);
    r.setStringParameter('nickname', server.nickname);
    r.setStringParameter('sshkey', server.sshKey);
    r.setStringParameter('instanceType', server.instanceType);
    r.setStringParameter('serverTemplate', server.serverTemplate);
    r.setStringParameter('cloudID', server.cloudID);
    r.setStringParameter('vpcSubnet', server.vpcSubnet);
    var response = r.execute();

    //Check the Status Code.  201 indicates that it created
    if (response.getStatusCode() == "201") {
        var loc = response.getHeader("Location");
        return loc;
    } else {
        return false;
    }

}



function deleteServer(account, sessionID, serverLoc) {

    //Set up and submit the REST Message for deletion
    var r = new RESTMessage('RightScale', 'delete');
    r.setStringParameter("sessionKey", sessionID);
    r.setStringParameter("account", account);
    r.setStringParameter("serverID", serverLoc);
    var response = r.execute();

    //The Delete response should be 200 if successful
    if (response.getStatusCode() == "200") {
        return true;
    } else {
        return false;
    }
}

function getID(rightScaleURL) {
    /*
    RightScale URLs are in the following format
    https://my.rightscale.com/api/acct/ACCNTNUM/RESOURCE/ID
 
    We will break it up by "/" delimiters and use the last
    delimited string
    */
    var parts = rightScaleURL.split("/");
    if (parts) {
        return parts[(parts.length - 1)];
    }
    return false;
}