3 Working with the Programmatic Intent-Based Network REST API
About the IP Service Activator REST API
HTTP requests to IP Service Activator execute actions (such as Post, Get, Delete, or Put) against objects defined in custom Groovy scripts. The custom scripts also include information to map the actions and objects to IP Service Activator operations.
If the HTTP request executes an action that initiates a device discovery or a transaction, the IP Service Activator REST API enables you to monitor the result with the JMS action queue.
REST API
You use REST API constraints to create a software architecture style that is based on resources. A resource is an object with a type, associated data, relationships to other resources, and a set of methods that operate on it. It is similar to an object in an object-oriented programming language; however, only a few standard methods are defined for a resource, while an object typically has many methods.
In the REST-based architecture, you access resources by using a common interface that is based on HTTP standard. A REST server manages and provides access to the resources, and a REST client accesses and modifies the resources through the common API. The common API is called the REST API and the services that support the API are called the REST web service.
The REST API includes an API Software Development Kit (SDK) that enables you to define API calls with a high level of granularity, which simplifies the logic that is required to provision complex services.
REST API Methods
Every resource supports some or all of the HTTP common methods. A resource is identified by a global ID that is typically a URI. A resource can be in a variety of formats, such as XML, JavaScript Object Notation (JSON), plain text, HTML, and user-defined data format. A REST client application can require a specific representation format by using the HTTP/HTTPS protocol content negotiation.
The common REST API methods are the following:
-
GET: Retrieves one or more resources. You can use this method to check the state of a resource.
-
PUT: Updates a resource. The PUT method updates the full definition of a resource, regardless of what has changed.
-
DELETE: Removes one or more resources.
-
POST: Creates a new resource.
-
PATCH: Updates only the parts of a resource definition that have changed.
A common flow includes using a method to perform an action on a resource, and then using the GET method to check the state of that action. For example, you can create a resource using the POST method (which includes a URI that points to the new resource), and then, because it might take a long time for that resource to be applied to the network, use the GET method to periodically check the state of the new resource. See "About Polling Using the GET Method" for more information.
JMS Action Queue
When the IP Service Activator REST API receives a request that includes an X-Request-ID value in the HTTP request header, it saves the X-Request-ID value to a correlation ID. During a triggering event (when a transaction is created or when a device discovery is initiated), the REST API sends a JMS message to the JMS action queue with the correlation ID and with a type and a result. The type can hold the values Transaction or Discovery, and the result can hold the values Succeeded, Failed, Invalid, and Timedout (the REST API uses the same time out value for device discovery and for transaction monitoring).
Note:
The REST API posts to a single JMS action queue. Multiple IP Service Activator clients can monitor the action queue if you use sufficiently descriptive correlation ID String values.
The REST API can complete a request immediately with an HTTP return code 200 (OK) or with an error code (for example, 400 bad request). The REST API posts nothing to the JMS action queue when a request completes with an error. If the return code is 202 (Accepted or Pending), the REST API posts the state change to the JMS action queue.
Note:
The REST API monitoring processes persist information over restarts.
Transactions
If REST methods are intended to modify the system, the system creates transactions. REST methods such as POST, PUT, PATCH, and DELETE can modify the system. If there are no commands in the output map, the system does not need to be modified and no transaction is created. See IP Service Activator Concepts for information about transactions.
When commands are generated based on a REST API request and a transaction is generated in IP Service Activator, the REST API periodically polls the transaction status in the Integration Manager. When the transaction completes, the REST API posts the result to the JMS action queue. When Groovy script generation fails or when a command delivery to IP Service Activator fails, the REST API returns an error and no JMS message is sent.
Device Discovery
When an SNMP discovery operation completes, the REST API posts a message to the JMS action queue based on the Boolean value defined for a device object. The Boolean value defined for the device object is called discovery and is set to true while the SNMP discovery process is in progress. The value is set to false when the discovery operation completes.
The REST API returns Failed if an error is detected. Otherwise, the REST API returns Succeeded. If the device discovery does not complete before the time out expires, then the REST API returns Timedout.
The REST API monitors for the following errors:
-
IDS_DISCOVERY_ERX_NOT_ALLOWED (1633: Core discovery is not allowed for Juniper E)
-
IDS_DISCOVERY_DEVICE_FAIL (1634: Device discovery failed)
-
IDS_DISCOVERY_DEVICE_TYPE_NOT_ALLOWED (1635: Core discovery is not allowed for this device type)
Working with the Groovy Scripting Language
Groovy script is a general-purpose scripting language that runs on the Java Virtual Machine (JVM). The syntax that is used for Groovy scripts is similar to the syntax for Java code. Most Java code is also valid Groovy script.
REST resources are mapped to Groovy scripts using a registry. Each REST call is done to a specific resource.
For example, a REST request to activate an Ethernet service could be done using a REST PUT method to a resource called SCA_ETH_FDFr_EC. In this example, the URI that is called using the REST service would be the following:
https://hostname:7002/Oracle/CGBU/IPSA/DomainController/resources/data/SCA_ETH_FDFr_EC.
The first part of the URI references the server with the web service, that is: https://hostname:7002. The next part references the IP Service Activator REST API, that is: Oracle/CGBU/IPSA/DomainController/resources/data. The last part references the resource, and can also contain a hierarchy, for example, Ethernet/SCA_ETH_FDFr_EC. The corresponding Groovy registry entry is like the following example:
<groovyScript> <name>groovy/Post_SCA_ETH_FDFr_EC.groovy</name> <target>SCA_ETH_FDFr_EC</target> <operation>POST</operation> </groovyScript>
The registry entry has the following components:
-
Name: The name of the Groovy script that you want to run. In the example, the Groovy script is contained in a directory.
-
Operation: The REST methods that are supported by the script. You can include multiple method entries. See "REST API Methods" for supported methods and their definitions.
-
Target: The resource supported by the script. This can be a single resource (for example, EthernetConnection), or a hierarchy with a resource (for example, Services/Ethernet/EthernetConnection).
The registry is loaded from the following directory: Service_Activator_home/DomainController/groovy.registry
A sample Groovy registry and Groovy scripts are provided in the Service_Activator_home/ServiceActivator/DomainController/sample directory. You can copy the registry and scripts directly into the Service_Activator_home/ServiceActivator/DomainController directory for testing. Sample JSON input is also provided in corresponding .txt files.
Note:
When using sample Groovy scripts, you must change the input to match the specific devices and interfaces that are configured in IP Service Activator.
Developing Custom Groovy Scripts
You run Groovy scripts to process REST requests. Groovy scripts interface with IP Service Activator in the Integration Manager by executing commands (find operations, for example) or by adding Integration Manager commands to an output variable to be executed in a trackable transaction (using the X-Request-ID value specified in the HTTP request header). See Oracle Communications IP Service Activator OSS Integration Manager Guide for more information about integration manager commands.
Table 3-1 lists and describes the variables that are available for creating custom Groovy scripts.
Table 3-1 Variables for Creating Custom Groovy Scripts
Variable | Description |
---|---|
json |
The input JSON format payload, converted to a map representation. If no JSON payload is provided (for example, with a GET method), this variable is an empty map. |
output |
An ArrayList of strings. These are the OIM commands that the script will generate. They are processed as a single transaction after the Groovy script returns its results. For information about commands and their formats, see IP Service Activator OSS Integration Manager Guide. |
returnedJson |
A HashMap that is converted to the JSON String returned from the request. This is a component of the Response (javax.ws.rs.core.Response) and is returned with the return code. |
uriArray |
An array of the elements of the URI. This is useful when you are specifying a hierarchy in the registry with multiple resources mapping to the same script, and enables the script to see what resource and hierarchy it is called with. |
queryMap |
Map of any query parameters passed on the request. For example, a GET request might specify: https://.../Layer3Ethernet?Customer=MyCustomer The parts after the '?' will be parsed into the queryMap. |
transactionNameArray |
An array of strings that are used when constructing the name of the transaction. This is optional in the Groovy script. The following code illustrates how transactions are named: <operation><objectName>_<transNameArray0>_<transNameArray1>_<timestamp>_<counter> <operation> : The operation that is performed (Post, Get, Delete). <objectName>: The object against which the action is performed. < transNameArrayX>: (Optional) If a string is added to transactionNameArray, each entry is added, followed by an underscore. <timestamp>: System date in the following format: yyyMMddHHmmssSS <counter>: A counter to ensure uniqueness of the transaction name. The value can contain up to 3 digits and resets to 0 after 999. |
domainControllerDir |
The absolute path to the Service Activator Domain Controller directory. This value is useful when a script calls another script, or when a path name is required. |
requestId |
The value of X-Request-ID from the HTTP request header. This is used to track the request's result up to the JMS action queue, where the ID is used as the correlation ID.Also, this can be used to log messages for reporting. |
helper |
The API that is provided for assistance. This API has its own javadoc and is provided to facilitate IP Service Activator operations (for example, looking up resources or attributes) and constructing some OIM commands automatically without needing to explicitly create and add them to the output variable. |
return |
This is not a variable, but is the return code from the script. It is returned as the status of the REST call. If the status is not successfully, for example it is 400 or greater, any IP Service Activator operations are not performed. If the script returns successfully, the REST call might still receive an error if the methods are invalid or if IP Service Activator does not accept the transaction. |
Groovy Script Examples
The examples in this section are intended to give further guidance about using the sample Groovy scripts that are provided with IP Service Activator.
Example: Generating CTM Commands
This example implements a REST-based mechanism for generating CTM commands. The sample Groovy script is available in the following location: Service_Activator_home/DomainController/sample/groovy/Post_CTM.groovy.
Note:
Oracle recommends using the POST method to implement this script because generating these commands is similar to the commands for creating a resource, even though this example does not create resources.
Step 1: Configuring JSON
Using JSON format, you must first design the input/output of the service that you want to implement. The input must indicate the template that you want to use (name, versions, driver type, and so on), as well as a list of attributes (name/value pairs).
The input is the following:
{ "templateName":"String", "deviceRoll":"String", "interfaceRoll":"String", "schemaRelease":"Number", "templateVersion":"Number", "driverType":"String", "objectType":"String", "interfaceType":"String", "templateVariables": { "Name1":"Value1", "Name2":"Value2", … } }
In this example, the template name is mandatory and all other template information is optional. You can use wildcards in the CTM call for non-mandatory template information. The structure of the templateVariables attribute contains a list of the variables that are part of the template. The list of variables depends on the type of template that you are using. The Groovy script does not enforce the variables in the list; however, CTM generates an error if the variables are incorrect for the template.
The output is the following simple JSON with an array of strings that contain the generated commands:
{ "Commands": [ "StringCommand1", "StringCommand2", … "StringCommandN" ] }
Step 2: Developing the Groovy Script
This section of the example shows the Groovy script that you can develop to accomplish the task of implementing a REST-based mechanism for generating CTM commands.
You can use the helper API to check for the mandatory parts of the incoming JSON code. Create a map that contains the parts of the JSON code that are mandatory, in this example that is the templateName and templateVariables. Note that this example does not check for specific variable names in the templateVariables because these can change based on the specific template.
The input JSON is located in the variable json, which is included in the call to isJsonValid, as in the following:
def expectedJson = [ templateName:"", templateVariables:"" ] if (!helper.isJsonValid(expectedJson, json)) { returnedJson.BadRequestErrorType = [ "title":"Exception", "detail":'Invalid ctm input json'] logger.log(Level.SEVERE, "Input is missing required ctm fields") return 400; }
The next part of this step is to put the variables into a hashtable so that they can be passed to CTM when generating the template. Groovy provides a way to iterate over the items in the JSON format map, and allows you to add each key/value pair to the new hashtable. Using validation, you can ensure that all the types are strings, in case an incorrect structure was accidentally passed into this part of the JSON. Even numeric fields are strings because JSON format does not differentiate numbers from strings. For example:
def Hashtable<String, String> fieldMap = new Hashtable<String, String>() json.templateVariables.each { key, value -> if (value.getClass() == String) { fieldMap.put(key, value) } }
You create Groovy variables that store the values that are needed to specify the template. In this way, you can also check when a value is not specified and then set it to null. Null is used by the CTM call to indicate that the value should not be used when searching for the template and acts as a wildcard. The template name is also stored in a variable for convenience. For example:
def templateName = json.templateName def deviceRoll = null if (json.containsKey("deviceRoll")) { deviceRoll = json.deviceRoll } def interfaceRoll = null if (json.containsKey("interfaceRoll")) { interfaceRoll = json.interfaceRoll } def schemaRelease = null if (json.containsKey("schemaRelease")) { schemaRelease = Integer.valueOf(json.schemaRelease) } templateVersion = null if (json.containsKey("templateVersion")) { templateVersion = json.templateVersion } def driverType = null if (json.containsKey("driverType")) { driverType = json.driverType } def objectType = null if (json.containsKey("objectType")) { objectType = json.objectType } def interfaceType = null if (json.containsKey("interfaceType")) { interfaceType = json.interfaceType }
All the data that is required for generating the commands from the template is now prepared, and you can call the CTM method using the helper API. This returns a vector containing the string commands:
def Vector<String> cmds = helper.generateCtmCommands(templateName, deviceRoll, interfaceRoll, schemaRelease, templateVersion, driverType, objectType, interfaceType, fieldMap)
If there is an error and the commands could not be generated, the helper API returns null. If this occurs, you can construct a different JSON output that indicates the failure. Use the variable returnedJson to construct the JSON map that gets sent back. In this example, the JSON has two elements, a "title" and "detail." It also returns a status 400, which indicates a "Bad Request," because something was wrong with the data that prevented the commands from being generated. This return code is passed back to the calling system.
if (cmds == null) { returnedJson.BadRequestErrorType = [ "title":"Exception", "detail":'Error generating template'] return 400 }
If the command vector is successfully generated, you can put the vector into the Commands element of the returned JSON. The vector maps to an array in the JSON and then returns the status of 200 to indicate that it was successful.
returnedJson.Commands = cmds return 200
The result is that the registry is edited with the following entry added for this service:
<groovyScript> <name>groovy/Post_CTM.groovy</name> <target>CTM</target> <operation>POST</operation> </groovyScript>
Example: Deleting a Layer 2 Ethernet Service
This example is for deleting a layer 2 service by using the sample that is in the following location: Service_Activator_home/DomainController/sample/groovy/ Post_SCA_ETH_FDFr_EC.groovy. This example uses Groovy functions.
The first Groovy function is used to substitute all ':' characters in a string with '_' and return the result, as in the following:
def String sanitizeIdentifier(String source) { if (source == null) return null return source.replaceAll(':', '_') }
One of the results of creating this service is that subinterfaces that do not already exist might be created. This method searches for all the subinterfaces under the customer. Subinterfaces are IP Service Activator objects. For information about finding and retrieving IP Service Activator objects, see IP Service Activator OSS Integration Manager Guide.
The following Groovy script searches all the subinterfaces and uses the helper API to find a generic rule with a specific name that matches the one used in the creation of the subinterface. For information about the RuleGeneric object type, see IP Service Activator OSS Integration Manager Guide. If the script locates a subinterface, it adds the delete command with the object ID to the variable output. This output is what gets processed when the script returns and performs operations on the OIM.
def void deleteGeneratedInterfaces(customerPath) { // First, find all the subs subs = helper.findObjects("Subinterface", customerPath, [:]) for (Map sub : subs) { // Now look for the subinterface creation policy underneath each subifCreation = helper.findObjectPath("RuleGeneric", sub.id, ["name":sub.name + "-Data"]) if (subifCreation != null && !subifCreation.isEmpty()) { output.add("delete [" + sub.id + "]") } } }
This is a delete method, so there is no JSON output. Instead, the parameters that are used to specify the instance that you want to delete are provided as part of the URI. For example:
https://hostname:7002/Oracle/CGBU/IPSA/DomainController/resources/data/SCA_ETH_FDFr_EC?evcCfgIdentifier=EVC_BOA_001_1_BOA_002
A "?" separates the resource (SCA_ETH_FDFr_EC) from the parameters. In this example, you must specify a parameter called evcCfgIdentifier, which identifies the specific instance of the resource that is to be deleted. Not specifying this parameter results in the following error:
if (queryMap.evcCfgIdentifier == null) { returnedJson.BadRequestErrorType = [ "title":"Exception", "detail":"No evcCfgIdentifier specified on delete operation" ] return 400 }
Begin deleting the service by using the evcCfgIdentifier parameter in the queryMap. This is a map that is provided with all the query parameters. You can do this using a loop that will support multiple evcEvcIdentifier parameters using a single URI.
Run the character conversion function on each identifier (which is also done on create), to get an ID without ':' characters. Then you can search for the customer that matches that ID. If there is no result, you can assume it is already removed and ignore the ID. In this way, if there is a duplicate or resent request, the system does not generate an error (idempotent).
If you find the customer, you can add a delete method to the output to delete the customer. Doing this also deletes everything contained within that customer (for example, sites, VPNs, and so on). At this point, the customer has not yet been deleted, but the delete command has been added to the output array.
Now you can call the method to remove any generated subinterfaces.
Note:
The commands put in the output are buffered and not executed in real time. They are processed only when the script completes, which makes it safe for the method deleteGeneratedInterfaces to search on the customer, even though the previous line adds the command to delete the customer to the output. It would not be safe for this method to reference the customer object in anything it added to the output buffer.
{ def String deleteId=sanitizeIdentifier(id) String customerPath = helper.findObjectPath("Customer", "/", ["name":"CE_" + deleteId]) if (customerPath != null) { // Start by removing any created interfaces output.add("delete " + customerPath) deleteGeneratedInterfaces(customerPath) } }
Next, you return a success code 202, accepted for processing. The processing of the output happens in the background after this returns.
return 202 // Accepted for processing, not completed
Finally, you must add an entry to the Groovy registry for this script, for example:
<groovyScript> <name>groovy/Delete_SCA_ETH_FDFr_EC.groovy</name> <target>SCA_ETH_FDFr_EC</target> <operation>DELETE</operation> </groovyScript>
About Polling Using the GET Method
If you're not using the JMS action queue for monitoring, you can poll manually to determine when the state of a resource has changed by using the GET method to retrieve the current state of the resource.
Plan your strategy for using the GET method to poll a resource for a state change (for example, when creating a resource). Running the GET method too frequently can negatively affect system performance, whereas running the GET method too infrequently means the system might not be responsive enough.
Determine the polling strategy by considering how long it takes for the service to typically be applied to the network and routers. For example, if the sample Ethernet service takes a minimum of 20 minutes to apply to the network (with slow routers and low bandwidth), it would not be useful to poll for the status every 20 seconds. In this case, polling in 5-minute intervals is more acceptable, although the polling interval also depends on the calling system's latency requirements.
About Logging
You can configure logging using the WebLogic Administration Console and the IP Service Activator Configuration GUI. You can configure logging for the REST web service by using Groovy scripts and the Java logging utilities.
Logging Using WebLogic Server Configuration
You can configure and manage logging by using WebLogic Server. You set logging levels in WebLogic for the server that is running the REST web service.
By default, the system logs errors at the ERROR level. When you are troubleshooting REST web service errors, you can change the logging to report DEBUG logs. If you change the logging option, you might have to restart the WebLogic server for changes to take effect.
For more information about setting up logging in WebLogic Server, see WebLogic Server documentation.
Configuring EOM Logging Using the IP Service Activator Configuration GUI
Configuring EOM logging by using the IP Service Activator Configuration GUI provides logging information only about the connection between the REST web service and IP Service Activator.
If you change this configuration, you must redeploy the REST web service for the change to take effect.
See IP Service Activator System Administrator's Guide for information about using configuration GUI log files.
Configuring Additional Logging Using Groovy Scripts
You can use Groovy scripts to configure additional logging on the REST web service by using the Java logging framework. This log output is included in the set of logs that is managed by WebLogic Server. See "Working with the Groovy Scripting Language" for more information.
Add logging to a Groovy script by importing the Java logging utilities at the beginning of the script, as in the following:
import java.util.logging.Level import java.util.logging.Logger def Logger logger = Logger.getLogger("MyClassOrFileName")
You can then use the logger within the Groovy script, for example:
logger.log(Level.SEVERE, "Error msg")