3 Working with the Programmatic Intent-Based Network REST API

This chapter describes the Oracle Communications IP Service Activator programmatic intent-based network Representational State Transfer (REST) API. You can use the REST API to provision customer-defined services and to integrate IP Service Activator with Oracle Communications Order and Service Management (OSM).

About the REST API

You can use REST 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. The REST API uses the Groovy scripting language to create high-level API functions. See "Working with the Groovy Scripting Language" for more information.

The REST API uses Secure Sockets Layer (SSL) so all the connections are secure and encrypted. See "Setting Up WebLogic Server Security" for more information.

About 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.

About Installing the REST API

If you want to use the REST API with IP Service Activator, you can select the Web Service optional integration component when you run the Oracle Universal Installer. Alternatively, if you are running a silent installation, you can use the Web Service response file to install IP Service Activator with REST API capability. For more information about hardware requirements, selecting the Web Service component, and using the Web Service response file, see IP Service Activator Installation Guide.

Installing IP Service Activator with the Web Service component allows you to use the REST web service. If the Web Service component was installed on a previous version of IP Service Activator, upgrading to the latest version and then redeploying the web service gives you access to the REST API.

To use a REST web service, you must set up Secure Sockets Layer (SSL) in WebLogic Server. See "Setting Up WebLogic Server Security" for more information.

Setting Up WebLogic Server Security

To use the REST API web service you must have an Oracle WebLogic Server installed and configured with SSL. For more information about installing and using WebLogic Server, see WebLogic Server product documentation.

Configuring Identity and Trust Keystores in WebLogic Server

To configure the WebLogic Server to use SSL, you must have an SSL certificate for the server that is running WebLogic. Production servers should have a trusted certificate. Lab and testing servers can use self-signed certificates. Use java to generate custom keystore files and then configure WebLogic Server to use those files.

To configure the WebLogic Server to use custom identity and trust keystore files:

  1. Generate custom SSL identity and trust files by entering the following in the Java utility:

    keytool -genkey -alias mykey -keyalg RSA -keysize 1024
            -sigalg SHA256withRSA -validity 128 -keypass 123456 -keystore identity.jks -storepass 123456
    keytool -export -alias mykey -file root.cer
            -keystore identity.jks -storepass 123456
    keytool -import -alias mykey -file root.cer -keystore trust.jks
            -storepass 123456
    

    where mykey is the key name, and 123456 is the key password.

    The system generates the following two files:

    • identity.jks

    • trust.jks

  2. Copy the .jks files to the security directory of the WebLogic Server, for example, OracleHome/user_projects/domains/domain_name/security.

  3. Log in to the WebLogic Server.

    The WebLogic Administration Console is displayed.

  4. In the left pane, expand Environment, and select Servers.

  5. Select the server where you want to configure the identity and trust keystores.

  6. Select Configuration, Keystores.

  7. Select the Custom Identity and Custom Trust option, and specify the identity.jks and trust.jks files that you generated in step 1.

    Note:

    The key name must match the key password that you entered when you generated the files.

Testing the SSL Configuration

You can test whether SSL is set up correctly in the WebLogic Server.

To test the SSL configuration:

  1. In a browser, go to the WebLogic Administration Console, for example, enter:

    http://hostname:7001/console

    If SSL is configured correctly, the browser connects to the WebLogic Administration Console using a secure connection and the URL changes from http:// to https://, for example:

    https://hostname:7002/console

    The default port number for SSL is 7002 and if your browser connects, SSL is configured correctly.

    Note:

    If you are using a self-signed certificate for authentication, the browser might need to import the certificate before you make the connection.

Security and Authentication

The web service supports only secure connections with authentication.

When you deploy the REST web service, the system creates and configures a WebLogic group called IpsaDomainController. The REST web service user, which is called ipsa_ws_user by default, is configured in the Web Service/Common section of the IP Service Activator Configuration graphical user interface (GUI). This web service user is automatically added to the IpsaDomainController group. See "Deploying and Undeploying Web Services" for information about deploying the web service for use with the REST API.

The REST web service is configured to accept calls only from a user that belongs to the IpsaDomainController group, as authenticated by WebLogic Server, or is a specific user with the name IpsaDomainController.

You can configure additional users and add them to this group using the WebLogic Administration Console. For information about managing users in the WebLogic Administration Console, see WebLogic Server documentation.

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. Oracle recommends that you map each combination of REST method and resource to a single script.

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 web service 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 "About 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. Each script is run with certain input and output available, and an API is provided for interacting with IP Service Activator.

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

Map of the output JSON resource that will be returned. This gets converted from a map back into JSON when it is returned to the caller.

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. This allows 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.

helper

The API that is provided for assistance. This API has its own javadocs, but is provided for doing 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 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.

Add the commands in the output map to a single transaction, which ensures that the transaction has been successfully submitted into IP Service Activator before the REST response is sent. The transaction could still fail and have configuration that could not be applied. Further GET methods should be performed when the status of the changes is required.

About Polling Using the GET Method

The REST protocol does not automatically poll resources. If you want to know when the state of a resource has changed, you must poll manually. Use the GET method to retrieve the current state of the resource.

It is important to 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. See "Configuring Web Services" for information about setting log levels in the configuration GUI.

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")