How to use Morpheus API through Plugin

We need to collect some information from onprem service which the Morpheus App node has access. Hence, workflow/ task is the only option to get that info in Morpheus UI. Now, this info I want to put it under a instance tab.

To do this I need to execute a workflow using REST Api. So, any suggestions on how can this be achieved from Plugin code.

In the Morpheus Uplink plugin I use this to auth to Morpheus API:

def login(HttpApiClient client, rpcConfig) {
        def rtn = [success:false]
        try {
            HttpApiClient.RequestOptions requestOptions = new HttpApiClient.RequestOptions(ignoreSSL: rpcConfig.ignoreSSL,contentType: 'form')

            def username = rpcConfig.username.toString()
            def password = rpcConfig.password.toString()

            requestOptions.queryParams = ['client_id':'morph-automation','grant_type':'password','scope':'write']
            requestOptions.headers = ['Content-Type':'application/x-www-form-urlencoded']

            // requestOptions.body = "username=${username}&password=${password}"
            requestOptions.body = ['username':username,'password':password]

            def apiUrl = cleanServiceUrl(rpcConfig.serviceUrl)
            def apiPath = getServicePath(rpcConfig.serviceUrl) + authPath

            def results = client.callJsonApi(apiUrl,apiPath,requestOptions,'POST')
            if(results?.success && results?.error != true) {
                log.debug("login: ${results}")
                rtn.token = results.data?.access_token?.trim()
                rtn.success = true
            } else {
                return ServiceResponse.error("Get Token Error")
            }
        } catch(e) {
            log.error("Get Token Error: ${e}", e)
            return ServiceResponse.error("Failed to authenticate to Morpheus")
        }
        return rtn
    }

Here is an example of using the token generated to list all networks in a Morpheus:

private ServiceResponse listZones(HttpApiClient client, String token, NetworkPoolServer poolServer, Map opts = [:]) {
        def rtn = new ServiceResponse()
        rtn.data = [] // Initialize rtn.data as an empty list
        try {
            def rpcConfig = getRpcConfig(poolServer)
            def apiUrl = cleanServiceUrl(rpcConfig.serviceUrl)
            def apiPath = getServicePath(rpcConfig.serviceUrl) + networkDomainsPath
            def hasMore = true
            def attempt = 0
            def doPaging = opts.doPaging != null ? opts.doPaging : true
            def start = 0
            def maxResults = opts.maxResults ?: 1000

            log.debug("url: ${apiUrl} path: ${apiPath}")

            if(doPaging == true) {
                
                while(hasMore && attempt < 1000) {
                    attempt++
                    HttpApiClient.RequestOptions requestOptions = new HttpApiClient.RequestOptions(ignoreSSL: rpcConfig.ignoreSSL)
                    requestOptions.headers = [Authorization: "Bearer ${token}".toString()]
                    requestOptions.queryParams = [max:maxResults.toString(),offset:start.toString()]

                    def results = client.callJsonApi(apiUrl,apiPath,null,null,requestOptions,'GET')

                    if(results?.success && results?.error != true) {
                        rtn.success = true
                        if(results.data?.networkDomains?.size() > 0) {
                            rtn.data += results.data.networkDomains

                            if(doPaging == true) {
                                start += maxResults
                                hasMore = true
                            } else {
                                hasMore = false
                            }

                        } else {
                            hasMore = false
                        }
                    } else {
                        hasMore = false

                        if(!rtn.success) {
                            rtn.msg = results.error
                        }
                    }
                }
            } else {
                HttpApiClient.RequestOptions requestOptions = new HttpApiClient.RequestOptions(ignoreSSL: rpcConfig.ignoreSSL)
                requestOptions.headers = [Authorization: "Bearer ${token}".toString()]
                requestOptions.queryParams = [max:maxResults.toString(),offset:start.toString()]

                def results = client.callJsonApi(apiUrl,apiPath,null,null,requestOptions,'GET')

                if(results?.success && results?.error != true) {
                    rtn.success = true
                    if(results.data?.networkDomains?.size() > 0) {
                        rtn.data = results.data.networkDomains
                    }
                } else {
                    if(!rtn.success) {
                        rtn.msg = results.error
                    }
                }
            }
        } catch(e) {
            log.error("listZones error: ${e}", e)
        }

        log.debug("List Zones Results: ${rtn}")
        return rtn
    }

You can check out the example here:

Thanks for the reply.

I see a function name getRpcConfig.
def rpcConfig = getRpcConfig(poolServer)
Can I know what other ways can I get this rpcConfig. I am referring to https://developer.morpheusdata.com/api/index.html

Thanks, this helped!!

@cbunge is there any way we can use cyphers in Plugin code?

Yes, the cypher service is accessible from within plugins. The following is a snippet from an older version of the DataDog plugin code that referenced cypher for credentials.

		// Define the Morpheus account/tenant in which
		// to search for the DataDog credentials. The plugin
		// assumes the master account shoud be searched.
		def account = new Account(id: 1)
		def cypherAccess = new CypherAccess(account)

		// Retrieve the DataDog API key from Cypher
		// The plugin expects the name of the secret to be dd-api-key
		def intApiKey = morpheus.getCypher().read(cypherAccess, "secret/dd-api-key")
		def apiKey = ""
		intApiKey.subscribe(
			{ secretData -> 
                 apiKey = secretData
        	},
        	{ error ->
                 println error.printStackTrace()
        	}
		)
1 Like

Am I right that the results variable is of type ServiceResponse?

If so, why is it possible to access its private variables which we can see in the following?

if(results?.success && results?.error != true)