Thursday, April 30, 2020

vRealize Automation 8.1 - Python and the REST API

In the vRealize Automation 8.1 API reference material.  I wanted to go through the examples in the section for acquiring a token and verifying roles.  I prefer Python and thought this may help others.  With the code below, this should be a good starting point and make the other API calls in the documentation easier to convert to Python.

At the start of the Authenticate() function, you'll see that I'm using configparser to read a configuration file named config.ini.  Place the configuration file into the same directory as the script.

Here is the relevant configuration I'm using for vRA:

file: config.ini

[vra user]
usrName=adm-user
usrPass=P@ssw0rd
usrTenant=corp.local

[vra server]
srvName=vm-vra.corp.local 

vRA will require a token when making RESTful API calls.  The first function called in the script will acquire the necessary tokens.  Once we have the access token, we can make the subsequent calls to acquire the Organization ID, Organization Roles and Organization Service Roles for the authenticated user.

The documentation states: 
"To use the API, a vRealize Automation user must be an organization member with at least a user service role. You use the access token to verify user roles"

Here is the code derived from the API documentation:
vra-1.py

#!/usr/bin/python3
# vRA8.1 API Calls

import sys, os, time, configparser, json, requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)


def Authenticate():
# Read Config parameters
config = configparser.ConfigParser()
config.read('config.ini')


#Your vROps environment parameters
usrName = config['vra user']['usrName']
usrPass = config['vra user']['usrPass']
usrTenant = config['vra user']['usrTenant']
srvName = config['vra server']['srvName']


global baseURL
baseURL = "https://" + srvName


vraURL = baseURL + "/csp/gateway/am/api/login?access_token"
apiJSON = {"username": usrName,"password": usrPass,"tenant":usrTenant}
apiHeaders = {"Content-Type":"application/json","Accept":"application/json"}
apiToken = requests.post(vraURL,data=json.dumps(apiJSON),headers=apiHeaders,verify=False)
refreshToken=apiToken.json()['refresh_token']


vraURL = baseURL + "/iaas/api/login"
accessJSON = {"refreshToken":apiToken.json()['refresh_token']}
accessHeaders = {"Content-Type":"application/json","Accept":"application/json"}
accessToken = requests.post(vraURL,data=json.dumps(accessJSON),headers=accessHeaders,verify=False)
return(accessToken.json()['token'])


def GetOrgId(token):
vraURL = baseURL + "/csp/gateway/am/api/loggedin/user/orgs"
authToken = "Bearer " + token
Headers = {"accept": "application/json", "authorization":authToken}
apiReq = requests.get(vraURL,headers=Headers,verify=False)
return(apiReq.json()['items'][0]['id'])

def GetOrgRole(token,OrgId):
vraURL = baseURL + "/csp/gateway/am/api/loggedin/user/orgs/"+OrgId+"/roles"
authToken = "Bearer " + token
Headers = {"accept": "application/json", "authorization":authToken}
apiReq = requests.get(vraURL,headers=Headers,verify=False)
print("Org Id: " + apiReq.json()[0]['name'])
return(apiReq.json()[0]['name'])

def GetServiceRole(token,OrgId):
vraURL = baseURL + "/csp/gateway/am/api/loggedin/user/orgs/"+OrgId+"/service-roles"
authToken = "Bearer " + token
Headers = {"accept": "application/json", "authorization":authToken}
apiReq = requests.get(vraURL,headers=Headers,verify=False)
print("Service Roles:")
for x in range(len(apiReq.json()['serviceRoles'])):
for y in range(len(apiReq.json()['serviceRoles'][x]['serviceRoleNames'])):
print(apiReq.json()['serviceRoles'][x]['serviceRoleNames'][y])

# Script starts here
def main():


authToken=Authenticate()
OrgId = GetOrgId(authToken)
GetOrgRole(authToken,OrgId)
GetServiceRole(authToken,OrgId)


if __name__ == "__main__":
main()

Sunday, April 26, 2020

Install the vRealize Operations monitoring agent via REST

New to vRealize Operations 8.1 are the REST calls to Install, Upgrade and Uninstall the vRealize Operations application monitoring agent.  In the script below, I show how to make the REST calls to accomplish each process.  The intent or hope is that someone can use this an even more automated process than I'm using here. 

Conditions for this example:


  • vRealize Operations 8.1 Advanced or higher license
  • Testing against a Microsoft Windows virtual machine (this works with Linux too, just change the authentication parameters for the VM)

For Python, an Install process will look for a data section like this:

{
  "resourcecredentials" : [ {
    "resourceid" : "fe64428f-e48e-48c9-8675-cd8737dc8118",
    "username" : "vm_username1",
    "password" : "vm_password1",
    "addruntimeuser" : true,
    "others" : [ ],
    "otherattributes" : { }
  }, {
    "resourceid" : "6ec2fea7-d0cf-4659-a93e-cde7221cc313",
    "username" : "vm_username2",
    "password" : "vm_password2",
    "addruntimeuser" : true,
    "others" : [ ],
    "otherattributes" : { }
  } ],
  "others" : [ ],
  "otherattributes" : { }
}
You can see that the resourceID used in the request is the vRealize Operations Instance ID.  The data structure also requires OS username and password for each resourceId.  If you are not familiar with the Instance ID, it's simply a unique identifier used by vRealize Operations and generated with a resource is created in the vRealize Operations database.  I have a search script in another blog that can be used to return the Instance ID if you want to check that out.

The agent upgrade request uses a much simpler structure but still needs the Instance ID.

{
  "contextresourceids" : [ "39de518e-057d-4594-adf5-91b3c517cfd0", "559b0279-ceca-435a-bb93-516c5178f474" ],
  "others" : [ ],
  "otherattributes" : { }
}
The 'contextResourceIDs' above can be an array/list of IDs.  You will notice that the upgrade data structure does not require a username and password like we do for installing or removing the agent.

For Uninstalling or Removing the agent, the data structure is similar to an install:

{
  "resourcecredentials" : [ {
    "resourceid" : "d1b39d3e-9da3-4602-ba75-2c6dfabb7ef6",
    "username" : "vm1_username",
    "password" : "vm1_password",
    "addruntimeuser" : true,
    "others" : [ ],
    "otherattributes" : { }
  }, {
    "resourceid" : "d8adc9bb-be2b-47d0-9d22-383f1a99079c",
    "username" : "vm2_username",
    "password" : "vm2_password",
    "addruntimeuser" : true,
    "others" : [ ],
    "otherattributes" : { }
  } ],
  "others" : [ ],
  "otherattributes" : { }
}
The agent uninstall process still uses the same resourceId and OS credentials to remove the agent.

FYI:  The script below uses  configparser  which allows a developer to use an input file for parameters.  You can replace the usrName, usrPass and srvName with you own values or configure your own configuration parameter file.  The usrName references a vRealize Operations local account.


Here is a sample configuration file

jwhite@vm-linux : ~/bin $ cat config.ini 
[vrops user]
usrName=vrops-user
usrPass=P@ssw0rd
[vrops server]
srvName=vm-vrops.corp.local

[windows user]
usrName=winuser
usrPass=P@ssw0rd
usrDomain=corp.local


The script (opsagent.py) to perform the processes:

#!/usr/bin/python3

#imports
import sys, getopt, json, requests, configparser
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

# Authenticate (vROps local user - consider updating script to allow for IDM)
def Authenticate():
#Read Config parameters
    config = configparser.ConfigParser()
    config.read('config.ini')

    #Your vROps environment parameters
    usrName = config['vrops user']['usrName']
    usrPass = config['vrops user']['usrPass']
    srvName = config['vrops server']['srvName']

    global baseURL
    baseURL = "https://" + srvName

    tokenURL = baseURL + "/suite-api/api/auth/token/acquire"
    authJSON = {"username": usrName,"authSource": "Local","password": usrPass,"others": [],"otherAttributes": {}}
    authHeaders = {"Content-Type":"application/json","Accept":"application/json"}
    authResponse = requests.post(tokenURL,data=json.dumps(authJSON),headers=authHeaders,verify=False)
    if (authResponse.status_code != 200):
        print('probably invalid credentials')
        print('Returned Status Code: ' + authResponse.status_code)
        sys.exit
    else:
        authToken = "vRealizeOpsToken " + authResponse.json()['token']
        return authToken

# Install an agent
def InstallAgent(token,vm_id):

    config = configparser.ConfigParser()
    config.read('config.ini')

    #Common VM OS Username and Password
    OSusrName = config['windows user']['usrName']
    OSusrDom = config['windows user']['usrDomain']
    OSusrPass = config['windows user']['usrPass']
    usrName = OSusrDom+"\\"+OSusrName

    reqData = {
        "resourceCredentials" : [ {
        "resourceId" : vm_id,
        "username" : usrName,
        "password" : OSusrPass,
        "addRuntimeUser" : True,
        "others" : [ ],
        "otherAttributes" : { }
        }]
    }
    Headers = {"Content-Type":"application/json","Authorization":token,"Accept":"application/json"}
    vropsURL = baseURL+"/suite-api/api/applications/agents"
    response = requests.post(vropsURL,headers=Headers,data=json.dumps(reqData),verify=False)
    if (response.status_code == 200):
        print ('Agent Install Submitted to ARC')
    else:
        print (str(response.text))

# Upgrade an agent
def UpgradeAgent(token,vm_id):
    reqData = {
        "contextResourceIDs" : [ vm_id ],
        "others" : [ ],
        "otherAttributes" : { }
    }

    Headers = {"Content-Type":"application/json","Authorization":token,"Accept":"application/json"}
    vropsURL = baseURL+"/suite-api/api/applications/agents/upgrade"
    response = requests.put(vropsURL,headers=Headers,data=json.dumps(reqData),verify=False)
    if (response.status_code == 200):
        print ('Agent Upgrade submitted to ARC')
    else:
        print (str(response.text))

# Remove an agent
def RemoveAgent(token,vm_id):

    config = configparser.ConfigParser()
    config.read('config.ini')

    #Common VM OS Username and Password
    OSusrName = config['windows user']['usrName']
    OSusrDom = config['windows user']['usrDomain']
    OSusrPass = config['windows user']['usrPass']
    usrName = OSusrDom+"\\"+OSusrName

    reqData = {
        "resourceCredentials" : [ {
        "resourceId" : vm_id,
        "username" : usrName,
        "password" : OSusrPass,
        "addRuntimeUser" : True,
        "others" : [ ],
        "otherAttributes" : { }
        } ],
    "others" : [ ],
    "otherAttributes" : { }
    }

    Headers = {"Content-Type":"application/json","Authorization":token,"Accept":"application/json"}
    vropsURL = baseURL+"/suite-api/api/applications/agents"
    response = requests.delete(vropsURL,headers=Headers,data=json.dumps(reqData),verify=False)
    if (response.status_code == 200):
        print ('Agent Uninstall Submitted')
    else:
        print (str(response.text))

def Logout(token):
    releaseURL = baseURL + "/suite-api/api/auth/token/release"
    authHeaders = {"Content-Type":"application/json","Authorization":token,"Accept":"application/json"}
    authResponse = requests.post(releaseURL,headers=authHeaders,verify=False)

def Usage():
    print("Script Usage:")
    print()
    print("To install the agent: opsagent.py -I <OPsID for VM>")
    print("To upgrade the agent: opsagent.py -U <OPsID for VM>")
    print("To remove the agent: opsagent.py -R <OPsID for VM>")

def main():
# Check for passed arguments
    if len(sys.argv)==1:
        Usage()
        sys.exit()

    try:
        opts, args = getopt.getopt(sys.argv[1:], "hI:U:R:",["help","install=","upgrade=","remove="])
    except getopt.GetoptError as err:
        print(err)
        Usage()
        sys.exit(2)
    authToken = Authenticate()
    
    for key,value in opts:
        if key == ("-h", "--help"):
            Usage()
            sys.exit()
        elif key in ("-I", "--install"):
            InstallAgent(authToken, value)
        elif key in ("-U", "--upgrade"):
            UpgradeAgent(authToken, value)
        elif key in ("-R", "--remove"):
            RemoveAgent(authToken, value)
        else:
            Usage()

    Logout(authToken)

# Script Starts Here
if __name__ == "__main__":
    main()

When executing the script, the REST call will pass the process to Application Remote Collector.  You can watch the process in vRealize Operations -> Administration -> History -> Recent Tasks.


To execute the processes:


 The removal process:
  ./opsagent.py -R c836c3bd-a7b9-4b93-91a6-200a3f974e3a

 The install process:
  ./opsagent.py -I c836c3bd-a7b9-4b93-91a6-200a3f974e3a

The upgrade process:
  ./opsagent.py -U c836c3bd-a7b9-4b93-91a6-200a3f974e3a


Thanks for checking this out.

Different vROps search methods

I had written a simple search script for an earlier post.  When I was working on another script to install the vRealize Operations application monitoring agent, I thought it would be useful to search for virtual machines with varying methods.  With the search script below, I wrote it to search for the virtual machine name, the vCenter managed object ID (MOID) or the vRealize Operations Instance ID.  Thanks to John Dias for his search suggestions.

A sample of inputs:

jwhite@vm-linux:~/bin$ ./searchv2.py --name vm-win7
vm-win7: c836c3bd-a7b9-4b93-91a6-200a3f974e3a vm-352

jwhite@vm-linux:~/bin$ ./searchv2.py --moid vm-352
vm-win7: c836c3bd-a7b9-4b93-91a6-200a3f974e3a vm-352

jwhite@vm-linux:~/bin$ ./searchv2.py --opsid c836c3bd-a7b9-4b93-91a6-200a3f974e3a
vm-win7: c836c3bd-a7b9-4b93-91a6-200a3f974e3a vm-352

The search by name and the search by moid can be broad searches as well.  For example, to search for any virtual machine where the MODI contains vm-3, you will see the following:

jwhite@vm-linux:~/bin$ ./searchv2.py --moid vm-3
vm-lcm: 1b5146d6-719f-4100-84f2-49b88c673d28 vm-351
vm-idm: 20ffd1d5-21a4-45fa-95ea-73e256470dbf vm-350
vm-linux: 32b35c09-8878-4a3a-947e-fcab2e3c4186 vm-354

or to broadly search for any virtual machine that contains 'win' in its name:

jwhite@vm-linux:~/bin$ ./searchv2.py --name win
VM-WIN10: 8d37e9eb-19fa-43c1-8e8a-902597322efb vm-324
Win2012: b9bc6577-d645-42b1-a32a-2640f2840cc9 vm-401
vm-win7: c836c3bd-a7b9-4b93-91a6-200a3f974e3a vm-352

The search for 'opsid' is an exact search.  You will need the entire OPs ID to have a successful search.

jwhite@vm-linux:~/bin$ ./searchv2.py --opsid c836c3bd-a7b9-4b93-91a6-200a3f974e3a
vm-win7: c836c3bd-a7b9-4b93-91a6-200a3f974e3a vm-352
jwhite@vm-linux:~/bin$ ./searchv2.py --opsid c836c3bd
jwhite@vm-linux:~/bin$ 


FYI:  The script below uses configparser which allows a developer to use an input file for parameters.  You can replace the usrName, usrPass and srvName with you own values or configure your own configuration parameter file.  The usrName references a vRealize Operations local account.

Here is a sample configuration file
jwhite@vm-linux:~/bin$ cat config.ini 
[vrops user]
usrName=vrops-user
usrPass=P@ssw0rd

[vrops server]
srvName=vm-vrops.corp.local

#!/usr/bin/python3

#imports
import getopt, sys, json, requests, configparser
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

# Authenticate (vROps local user - consider updating script to allow for IDM)
def Authenticate():
    #Read Config parameters
    config = configparser.ConfigParser()
    config.read('config.ini')

    #Your vROps environment parameters
    usrName = config['vrops user']['usrName']
    usrPass = config['vrops user']['usrPass']
    srvName = config['vrops server']['srvName']

    global baseURL
    baseURL = "https://" + srvName

    tokenURL = baseURL + "/suite-api/api/auth/token/acquire"
    authJSON = {"username": usrName,"authSource": "Local","password": usrPass,"others": [],"otherAttributes": {}}
    authHeaders = {"Content-Type":"application/json","Accept":"application/json"}
    authResponse = requests.post(tokenURL,data=json.dumps(authJSON),headers=authHeaders,verify=False)
    if (authResponse.status_code != 200):
        print('probably invalid credentials')
        print('Returned Status Code: ' + authResponse.status_code)
        sys.exit
    else:
        authToken = "vRealizeOpsToken " + authResponse.json()['token']
        return authToken

# Search vROps database for the MOID
def SearchMOID(token, vm_name):
    Headers = {"Content-Type":"application/json","Authorization":token,"Accept":"application/json"}
    vropsURL = baseURL+"/suite-api/api/adapterkinds/VMWARE/resourcekinds/virtualmachine/resources?identifiers[VMEntityObjectID]="+vm_name
    response = requests.get(vropsURL,headers=Headers,verify=False)
    if response.json()['pageInfo']['totalCount'] == 0:
        return ('Searched term not found')
    else:
        for x in range(int(response.json()['pageInfo']['totalCount'])):
            msg = response.json()['resourceList'][x]['resourceKey']['name']+": "
            msg += response.json()['resourceList'][x]['identifier']+" "
            msg += response.json()['resourceList'][x]['resourceKey']['resourceIdentifiers'][2]['value']
            print(msg)
        return ('Searched term was successful')

# Search vROps database for the VM Name
def SearchName(token, vm_name):
    Headers = {"Content-Type":"application/json","Authorization":token,"Accept":"application/json"}
    vropsURL = baseURL+"/suite-api/api/adapterkinds/VMWARE/resourcekinds/virtualmachine/resources?name="+vm_name
    response = requests.get(vropsURL,headers=Headers,verify=False)
    if response.json()['pageInfo']['totalCount'] == 0:
        return ('Searched term not found')
    else:
        for x in range(int(response.json()['pageInfo']['totalCount'])):
            msg = response.json()['resourceList'][x]['resourceKey']['name']+": "
            msg += response.json()['resourceList'][x]['identifier']+" "
            msg += response.json()['resourceList'][x]['resourceKey']['resourceIdentifiers'][2]['value']
            print(msg)
        return ('Searched term was successful')

# Search vROps database for the vROPS ID
def SearchOPSID(token, vm_name):
    Headers = {"Content-Type":"application/json","Authorization":token,"Accept":"application/json"}
    vropsURL = baseURL+"/suite-api/api/resources/"+vm_name
    response = requests.get(vropsURL,headers=Headers,verify=False)
    if response.status_code != 200:
        return ('Searched term not found')
    else:
        msg = response.json()['resourceKey']['name']+": "+response.json()['identifier']+" "
        msg += response.json()['resourceKey']['resourceIdentifiers'][2]['value']
        print(msg)
        return ('Searched term was successful')

def Logout(token):
    releaseURL = baseURL + "/suite-api/api/auth/token/release"
    authHeaders = {"Content-Type":"application/json","Authorization":token,"Accept":"application/json"}
    authResponse = requests.post(releaseURL,headers=authHeaders,verify=False)

def Usage():
    print("We need either:")
    print(" -M <vCenter MOID>")
    print("or")
    print(" -N <VM Name>")
    print("for parameters")

def main():
# Check for passed arguments
    if len(sys.argv)==1:
        Usage()
        sys.exit()

    try:
        opts, args = getopt.getopt(sys.argv[1:], "hM:N:I:",["help","moid=","name=","opsid="])
    except getopt.GetoptError as err:
        print(err)
        Usage()
        sys.exit(2)
    authToken = Authenticate()
    
    for key,value in opts:
        if key == ("-h", "--help"):
            Usage()
            sys.exit()
        elif key in ("-M", "--moid"):
            SearchMOID(authToken, value)
        elif key in ("-N", "--name"):
            SearchName(authToken, value)
        elif key in ("-I", "--opsid"):
            SearchOPSID(authToken, value)
        else:
            Usage()

    Logout(authToken)

# Script Starts Here
if __name__ == "__main__":
   main()