Example: Simple Provisioning System

Download this manual as a PDF file

The following sections describe a simple provisioning system written in PHP. The provisioning system is designed to be used by a managed service provider that uses SL1 to provide monitoring services to its customers.

Using customer information supplied through a simple user interface, the example code makes requests to the API to:

  • Create an organization record for the customer.
  • Configure SNMP credentials using the supplied community strings.
  • Create and run a discovery session.
  • Display a list of devices for a specific customer.
  • Configure selected devices using device templates.
  • Remove a customer from SL1 by deleting devices, discovery sessions, credentials, and the organization record.

Use the following menu options to navigate the SL1 user interface:

  • To view a pop-out list of menu options, click the menu icon ().
  • To view a page containing all the menu options, click the Advanced menu icon ().

This section includes the following topics:

System Design

The example provisioning system comprises the following front-end files that display the user interface:

  • index.php. Provides a user interface for provisioning a new customer and discovering additional devices for an existing customer.
  • devices.php. Provides a user interface for configuring customer devices that have been discovered in SL1.
  • remove.php. Provides a user interface for removing a customer from SL1.

The following back-end files handle the provisioning procedures:

  • provision_customer.php. Processes the input values from index.php and performs the following provisioning tasks:
  • If an organization record does not currently exist for the customer, creates an organization record for the customer.
  • Configures SNMP credentials for each supplied SNMP community string.
  • Creates a discovery session for the customer using the configured SNMP credentials and the supplied list of IP addresses.
  • Runs the discovery session.

    If all of these tasks are successful, provision_customer.php redirects to configure_devices.php, which will return a list of discovered devices to the devices.php page. If a provisioning task is unsuccessful, provision_customer.php returns an error message to index.php.

  • configure_devices.php. The configure_devices.php script returns a list of devices and associated device classes for a specified customer. The list of devices can be all devices associated with the customer's organization record, all devices from the last discovery session for that customer, or new devices from the last discovery session for that customer. Additionally, if a user selects the Configure Devices button in the devices.php page, the configure_devices.php script applies the device templates selected by the user to the specified devices.
  • delete_customer.php. Takes a customer name as input; deletes all devices, credentials, and discovery sessions associated with that customer's organization record; and then deletes the organization record for that customer.

The following diagram shows the control flow between the files when all procedures are successful:

When a back-end procedure is unsuccessful, an error message is returned to the appropriate front-end page.

The six main PHP files use the following additional files:

  • header.php. Includes the common elements used by all three user interface pages.
  • provisioning.css. Includes style information for the user interface pages. In this example, minimal style is applied to the user interface pages. You can customize the user interface pages by adding style information to this file.
  • utils.php. Includes a set of PHP functions that are used by the three back-end files.

Prerequisites

To use the example code described in this section to interact with your instance of SL1, you must:

  • Upgrade your system to version 7.5.5 or later. Some API requests used in the provisioning code are not compatible with older versions of SL1.
  • Manually create a device template in your instance of SL1 that will be applied to all devices discovered using the provisioning system.

  • Edit utils.php to include:
  • The IP address of an Administration Portal, Database Server, or All-In-One Appliance in your system.

  • An administrator username and password.
  • The URI of the device template that will be applied to all devices discovered using the provisioning system.

See the System-Specific Functions section for a description of the required changes to utils.php.

  • Copy the example files to a web server. All the example files must be in the same directory on the web server. The web server must:
    • Be able to make HTTP requests to your Administration Portal, Database Server, or All-In-One Appliance.
    • Use a PHP processor module that includes cURL support. The code in this example uses cURL to communicate with an Administration Portal, Database Server, or All-In-One Appliance. For more information about cURL support in PHP, see http://www.php.net/manual/en/book.curl.php.
    • Use PHP version 5.2.0 or later. The code in this example uses JSON format for all requests and uses the json_encode and json_decode functions. For more information about JSON support in PHP, see http://php.net/manual/en/book.json.php.
  • Manually add a custom attribute to the /device resource. The example code uses this custom attribute to track the last device template that was applied to each device. To add the custom attribute, "c-last_dev_tpl", POST the following JSON content to the /custom_attribute/device resource index:

{

"name":"last_dev_tpl",

"label":"last_dev_tpl",

"type":"string",

"index":"none",

"extended":"0"

}

 

For more information about custom attributes, see the Custom Attributes section.

System-Specific Functions

This example includes two functions in utils.php that return information about the instance of SL1 with which the provisioning code interacts:

  • get_admin_uri. Returns the URL of an Administration Portal, Database Server, or All-In-One Appliance with the username and password of an administrator user embedded in the URL. This value is a required parameter for most functions in utils.php.
  • get_base_template. Returns the relative URI of a device template. This device template specifies the basic monitoring parameters for customer devices and is applied to every device discovered using the provisioning system.

To use the example code with your instance of SL1, you must edit the get_admin_uri function to include the IP address of your Administration Portal, Database Server, or All-In-One Appliance, the username for an administrator user, and the password for that administrator user:

function get_admin_uri() {

 

$is_ip = "10.100.100.15";

$is_user = "em7admin";

$is_pass = "em7admin";

 

$base_uri = "https://" . $is_user . ":" . $is_pass . "@" . $is_ip;

 

return $base_uri;

}

 

To use the example code with your instance of SL1, you must edit the get_base_template function to include the relative URI of a device template in your system:

function get_base_template() {

return "/api/device_template/3";

}

 

Utility Functions (utils.php)

Most tasks performed by the back-end code for this example are performed using a set of generic functions that can be re-used multiple times. If you are developing code that interacts with the ScienceLogic API and are using a different programming language, you might want to start by developing similar generic functions. In this example, the functions are included in the file utils.php, which is used by every back-end PHP file. The utils.php file includes functions that perform the following procedures:

In addition, utils.php includes two functions that return information about the instance of SL1 with which the provisioning code interacts. For this example, the information returned by these system-specific functions is hard-coded.

The following sections describe each function in utils.php.

Performing Requests

To perform a request to the ScienceLogic API in PHP, you must:

  • Create and configure a cURL session.
  • For requests that use the POST method, encode a PHP array as JSON content.
  • Execute the cURL request.
  • Parse the response and decode the JSON content in to a PHP array.

The perform_request function is designed to perform these steps and return the response in a PHP array that has the following structure:

(

['http_code'] => HTTP status code in the response

['headers'] => Array of headers that were included in the response. Each key in this array is the name of the header, which points to the value for that header.

['content'] => Array that contains the decoded JSON body of the response.

['error'] => If the HTTP code in the response is not healthy (i.e. not 200, 201, or 202), a human-readable error message that includes all error information that was included in the response.

)

 

The perform_request function requires the following parameters:

  • $base. The URL of an Administration Portal, Database Server, or All-In-One Appliance.
  • $resource. The relative URI of the resource to request.

The perform_request function has the following optional parameters:

  • $type. The type of request to perform. By default, the perform_request function performs a GET request ($type = "GET"). The function accepts the following string values in the $type parameter:
    • POST. The function will POST JSON content to the specified $resource. This method can be used to create or update resources.
    • APPLY. The function will POST a resource URI to the specified $resource. This method is used to perform asynchronous operations such as starting discovery sessions and applying device templates to devices.
    • DELETE. The function will perform an HTTP DELETE request on the specified $resource.
  • $content. For $type values that require a POST operation ("POST" or "APPLY"), the content to POST must be passed in this parameter. For a $type value of "POST", $content must be an array, which will be encoded in JSON format. For a $type value of "APPLY", $content must be the relative URI to POST.

The perform_request function uses the $base and $resource values to construct the full URI of the resource, then creates a cURL session:

function perform_request($base, $resource, $type = "GET", $content = FALSE) {

$uri = $base . $resource;

$ch = curl_init($uri);

 

For every request, the following cURL options are configured in the cURL session:

  • CURLOPT_RETURNTRANSFER. Set to TRUE. By default, the PHP function that executes a request outputs the response to standard out. By specifying this option, the function will return the response as a string.
  • CURLOPT_HEADER. Set to TRUE. By specifying this option, the response headers will be included in the output.
  • CURLOPT_SSL_VERIFYPEER and CURLOPT_SSL_VERIFYHOST. Set to FALSE. To enable the use of the example code in a test environment, the verification of the SSL certificate on the API appliance is disabled.

curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

curl_setopt($ch, CURLOPT_HEADER, TRUE);

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);

curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);

 

If the $type parameter is set to "POST" and content is supplied, the following additional cURL options are set to perform a create/update POST request:

  • CURLOPT_POST. Set to TRUE to perform an HTTP POST request.
  • CURLOPT_POSTFIELDS. Set to the value of $content (in this case, a PHP array) encoded as JSON content.
  • CURLOPT_HTTPHEADER. Specifies the appropriate content-type header to include in the request.

if($type == "POST" AND $content) {

$json_content = json_encode($content);

curl_setopt($ch, CURLOPT_POST, TRUE);

curl_setopt($ch, CURLOPT_POSTFIELDS, $json_content);

curl_setopt($ch, CURLOPT_HTTPHEADER, array('content-type: application/json'));

}

 

If the $type parameter is set to "APPLY" and content is supplied, the following additional cURL options are set to perform POST request that applies a resource URI:

  • CURLOPT_POST. Set to TRUE to perform an HTTP POST request.
  • CURLOPT_POSTFIELDS. Set to the value of $content (in this case, a the URI of a resource).
  • CURLOPT_HTTPHEADER. Specifies the appropriate content-type header to include in the request.

if($type == "APPLY" AND $content) {

curl_setopt($ch, CURLOPT_POST, TRUE);

curl_setopt($ch, CURLOPT_POSTFIELDS, $content);

curl_setopt($ch, CURLOPT_HTTPHEADER, array('content-type: application/em7-resource-uri'));

}

 

If the $type parameter is set to "DELETE", the CURLOPT_CUSTOMREQUEST option is set to perform an HTTP DELETE in the cURL session:

if($type == "DELETE") {

curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");

}

 

If the $type parameter is set to "POST" or "APPLY" and the $content parameter is not supplied, the perform_request function returns FALSE without performing a request:

elseif(($type == "POST" OR $type == "APPLY") AND !$content) {

return FALSE;

}

 

The perform_request function executes the cURL request and stores the HTTP status code from the response in the output array ($response):

$output = curl_exec($ch);

$response['http_code'] = curl_getinfo($ch, CURLINFO_HTTP_CODE);

 

The response from the API includes the following information that must be included in the output of the function:

  • Each response header on a separate line.
  • The JSON content in the body of the response on a single line.

To parse this information, an array called $output_array is created with each line of the response as an array element. Because the HTTP status code has already been stored, the first line of the response, which contains the HTTP version and status code, is removed from the array:

$output_array = explode("\n", $output);

array_shift($output_array);

 

The function iterates through each line of the response. If a line begins with an opening brace, it is assumed to be the JSON content and is added to the output array ($response):

foreach($output_array as $line) {

if(strpos($line, "{") < 2 AND strpos($line, "{") !== FALSE) {

$response['content'] = json_decode($line, TRUE);

}

 

If a line is not content and includes a colon, it is assumed to be a header and is added to the output array ($response):

elseif(strpos($line, ":") !== FALSE) {

$header_array = explode(":", $line);

$response['headers'][$header_array[0]] = trim($header_array[1]);

}

}

 

To allow other functions to assume that the "content" key always exists in the output array, the "content" key in the output array ($response) is initialized as an empty array if it is not already initialized:

if(!array_key_exists('content', $response)) {

$response['content'] = array();

}

 

In addition to HTTP status codes, every response from the API includes headers that provide additional details about the result of a request:

  • X-EM7-Implemented-methods. A comma-delimited list of methods that are supported by the requested resource. This header is intended to provide information on the actions that can be performed on a given resource. For example, if you perform a GET request on the /device resource index, X-EM7-Implemented-methods will contain "GET,POST", the two methods supported by /device. If you perform a GET request on a specific device (e.g. /device/1), the X-EM7-Implemented-methods header will contain "GET,POST,PUT,DELETE", because a specific device resource supports all available methods.
  • X-EM7-Applicable-resources. A comma-delimited list of base URIs for resources that can be applied to the requested resource. For example, to start a discovery session through the API, you would POST a specific /discovery_session resource to the /discovery_session_active resource index; therefore, if you perform a GET request on the /discovery_session_active resource index, the response will include a X-EM7-Applicable-resources header of "/discovery_session". For more information on applying resource URIs to other resources, see the Asynchronous Operations section.
  • X-EM7-authenticated-user. The URI of the user account that authenticated the request. If the request included the X-EM7-run-as header, the X-EM7-authenticated-user will return the run-as user.
  • X-EM7-status-code. Typically a human-readable version of the HTTP Status Code. For certain errors, X-EM7-status-code might include additional information about why a request was unsuccessful. For example, if a response has the HTTP Status code "400 Bad Request", the X-EM7-status-code might be "FAILED_INPUT_VALIDATION".
  • X-EM7-status-message. A human-readable description of the result of a request. The X-EM7-status-message can contain multiple messages delimited by a newline character (\n). For example, if a response has the HTTP Status code "302 Found", the X-EM7-status-message might be "ticket index requires a limit", indicating the request was missing the required limit option.
  • X-EM7-Last-updated. This header is returned only when requesting device configuration data from the API. Returns the date and time that at least one value in the returned data changed.

If the HTTP status code from the response is not 200, 201, or 202 (i.e. 301 or above), the "error" key in the output array ($response) is set to an appropriate message, which includes the values from the X-EM7-status-message and X-EM7-info-message headers:

if($response['http_code'] > 300) {

$response['error'] = "HTTP status " . $response['http_code'] . " returned. ";

if(array_key_exists("X-EM7-status-message", $response['headers'])) {

$response['error'] .= $response['headers']['X-EM7-status-message'] . ". ";

}

if(array_key_exists("X-EM7-info-message", $response['headers'])) {

$response['error'] .= $response['headers']['X-EM7-info-message'] . ". ";

}

}

 

Finally, the cURL session is closed and the output array ($response) is returned:

curl_close($ch);

return $response;

}

 

Requesting a List of Entities

All resource indexes in the API require the inclusion of the "limit" option in all GET requests; therefore, to obtain a full list of entities from a resource index, you might need to perform multiple requests. For example, if 300 devices are discovered in the system and you use the default limit of "100" when performing a request on the "/device" resource index, you must perform three requests to obtain a list of all devices: one request with an offset of 0, one request with an offset of 100, and one request with an offset of 200.

The get_all function is designed to return a list of all available entities for a given resource index URI. The get_all function includes a do-while loop that handles cases where multiple requests are required. For example, if the URI is "/device", the get_all function returns a list of all devices in the system.

The get_all function requires the following parameters:

  • $base_uri. The URL of an Administration Portal, Database Server, or All-In-One Appliance.
  • $uri. The relative URI of a resource index. The limit and offset parameters are added to the URI by the get_all function; the URI must not include limit or offset parameters. The logic in the get_all function requires that responses from the API include the total_matched value; therefore, the passed URI must not include the hide_filterinfo parameter.

The get_all function returns:

  • On success, an array of entities. The structure of the array of entities is identical to the structure returned in the result_set section of the response from the specific resource URI. The array of entities can be empty if the request to the resource URI was successful, but no results were returned.
  • On failure, an error message.

Any function that calls the get_all function can check success/failure by determining if the returned value is an array or a string.

Before executing the do-while loop in which requests to the resource URI are performed, the array of entities is initialized, initial offset value is set to 0, and the limit and offset values are added to the URI:

function get_all($base_uri, $uri) {

 

$offset = 0;

$request_uri = $uri . "&limit=100&offset=";

$entities = array();

 

The the $request_uri variable does not include a value for the offset option. For each iteration of the do-while loop, the current offset is appended to the end of $request_uri.

The do-while loop performs a GET request for the URI with the current offset. If the request was successful (the HTTP status code is 200), the result_set from the request is added to the list of entities:

do {

$response = perform_request($base_uri, $request_uri . $offset, "GET");

 

if($response['http_code'] == 200 AND array_key_exists("result_set", $response['content']) AND count($response['content']['result_set']) > 0) {

$entities = array_merge($entities, $response['content']['result_set']);

}

 

If the request is unsuccessful, the $message variable is initialized with an error message:

elseif($response['http_code'] != 200) {

$message = "An error occured while requesting entities. ";

if(array_key_exists("error", $response)) {

$message .= $response['error'];

}

}

 

Because the limit parameter is set to 100 in the URI, the offset value is incremented by 100 on each iteration. The do-while loop iterates if the previous request was successful and more entities are available. The "total_matched" value from the previous response indicates the total number of entities that can be returned by this specific URI; more entities are available if the current offset value is lower than "total_matched"":

$offset = $offset + 100;

 

} while(!isset($message) AND ($offset < $response['content']['total_matched']));

 

If an error message was generated by any request performed by the get_all function, the returned value is the error message generated by the failed request. If no error messages were generated, the array of entities is returned:

if(isset($message)) {

return $message;

}

else {

return $entities;

}

}

 

Organization Lookup

The input forms used in this example include a field for customer name. The lookup_organization function is designed to return the URI for a customer's organization record using the name of a customer.

The lookup_organization function requires the following parameters:

  • $base_uri. The URL of an Administration Portal, Database Server, or All-In-One Appliance.
  • $customer. The customer name.

The lookup_organization function returns:

  • On success, the URI of the organization record for the specified customer.
  • On failure, boolean FALSE.

The lookup_organization function constructs a request to the /organization resource index using the customer name as a search parameter. The customer name is URL encoded to handle names that include spaces:

function lookup_organization($base_uri, $customer) {

$resource = "/api/organization?limit=1&hide_filterinfo=1&filter.company=" . rawurlencode($customer);

$response = perform_request($base_uri, $resource, "GET");

 

If the request was successful (the HTTP status code is 200) and at least one organization is returned, the URI of the first organization in the response is returned. Because the request specified that the customer name must be matched exactly and because all organization names in an instance of SL1 must be unique, the assumption is made that the response will not include more than one organization:

if($response['http_code'] == 200 AND count($response['content']) > 0 AND array_key_exists("URI", $response['content'][0])) {

return $response['content'][0]['URI'];

}

else {

return FALSE;

}

}

 

Creating Entities

The create_entity function is designed to create an entity using the resource index URI for that entity and an array of field/value pairs for the entity.

The create_entity function requires the following parameters:

  • $base_uri. The URL of an Administration Portal, Database Server, or All-In-One Appliance.
  • $entity_uri. The resource index URI for the entity to be created. For example, to create an organization, supply "/api/organization" in the $entity_uri parameter.
  • $entity_array. A PHP array that contains field/value pairs of the attributes for the entity. This PHP array will be converted to JSON format and POSTed to the specified URI.

The create_entity function returns an array:

  • The first array value (array index 0) is a boolean that indicates whether the entity was created successfully.
  • The second array value (array index 1) is a string. On success, the string is the URI of the created entity. On failure, the string is an error message.

The create_entity function uses the perform_request function to create the entity. The perform_request function handles the conversion of the PHP array to JSON format and the options required to perform a POST request:

function create_entity($base_uri, $entity_uri, $entity_array) {

$response = perform_request($base_uri, $entity_uri, "POST", $entity_array);

 

If the request was successful (the HTTP status code is 201), the function returns TRUE at array index 0 and the contents of the "Location" header at array index 1, which contains the relative URI of the created element:

if($response['http_code'] == 201) {

return array(TRUE, $response['headers']['Location']);

}

 

If the request was unsuccessful, the function returns FALSE at array index 0 and the error message at array index 1:

else {

$error_message = "Could not create " . substr($entity_uri, 1) . ". ";

if(array_key_exists("error", $response)) {

$error_message .= $response['error'];

}

return array(FALSE, $error_message);

}

}

 

Deleting Entities

The multi_delete function is designed to delete multiple entities.

The multi_delete function requires the following parameters:

  • $base_uri. The URL of an Administration Portal, Database Server, or All-In-One Appliance.
  • $entities. An array that contains the entities to be deleted. The $entities array must be multi-dimensional; each element in the $entities array must be an array that includes "URI" as a key. The function uses the value of "URI" as the relative URI in a delete request. The structure of the $entities array is the same as an array returned by the get_all function.

The multi_delete function returns NULL on success or an error message on failure.

The multi_delete function initializes $bad_entities as an array. The $bad_entities array is used to track entities that could not be deleted:

function multi_delete($base_uri, $entities) {

$bad_entities = array();

 

If the input is valid ($entities is an array), the multi_delete function iterates through each element in the array. For each element, if the element is an array that contains the key "URI", the function performs a delete request using the value that corresponds to the key "URI". If the element was not an array, did not contain the key "URI", or the delete request fails, the element is added to the $bad_entities array:

if(is_array($entities)) {

foreach($entities as $entity) {

if(is_array($entity) AND array_key_exists('URI', $entity)) {

$response = perform_request($base_uri, $entity['URI'], "DELETE");

if($response['http_code'] >= 400) {

$bad_entities[] = $entity;

}

}

else {

$bad_entities[] = $entity;

}

}

 

If all elements in the $entities array were deleted, the multi_delete function returns NULL, indicating success:

if(count($bad_entities) == 0) {

return NULL;

}

 

If one or more elements in the $entities array could not be deleted, an error message is constructed by concatenating the contents of each element in $entities that could not be deleted. Instead of determining the data type of each element, the print_r function is used to output the human-readable string for the element:

else {

$error_message = "Could not delete: ";

foreach($bad_entities as $entity) {

$error_message .= print_r($entity, TRUE) . ". ";

}

return $error_message;

}

}

else {

return "Must pass an array of entities";

}

}

 

Configuring SNMP Credentials

The configure_credentials function is designed to return an array of SNMP v2 credentials for a specific organization using a list of community strings. The configure_credentials function creates new credentials if a credential with the same community string does not already exist for the organization.

The configure_credentials function requires the following parameters:

  • $base_uri. The URL of an Administration Portal, Database Server, or All-In-One Appliance.
  • $customer. The name of the customer organization for which the credentials will be used.
  • $community_strings. A comma-delimited list of community strings. The configure_credentials function ensures that a credential associated with the $customer organization exists for each community string in the list.

The configure_credentials function returns an array of credential URIs on success or an error message on failure.

The configure_credentials function uses the array_walk PHP function when the list of community strings is parsed. The array_walk function takes the name of a function as a parameter and applies that function to each value in the array. In our example code, the array_walk applies the trim_value function to each value in the array. The trim_value function is included in the utils.php file and removes leading and trailing whitespace from each value passed in the parameter:

function trim_value(&$value) {

$value = trim($value);

}

 

The $community_strings parameter is split into an array of community strings. If a user enters spaces in the comma-delimited list, the trim_value function removes leading and trailing whitespace from each element in the array:

function configure_credentials($base_uri, $customer, $community_strings) {

$community_array = explode(",", $community_strings);

array_walk($community_array, 'trim_value');

 

All credentials created by the configure_credentials function are named "customer: community string". The configure_credentials function performs a request for all credentials associated with the specified customer by searching for credential names that include the string $customer:

$resource = "/api/credential/snmp?limit=1000&snmp_version=2&hide_filterinfo=1&filter.cred_name.contains=" . rawurlencode($customer);

$existing_credentials = perform_request($base_uri, $resource, "GET");

 

If the request for existing credentials is successful, the response is processed using the following arrays:

  • $existing_credentials. The response to the request for all credentials currently associated with the organization specified in $customer.
  • $community_array. The array of community strings passed in the $community_strings parameter.
  • $existing_communities. Initialized to an empty array. As the function iterates through $existing_credentials, the community string for each existing credential that matches a community string that was passed in the $community_strings parameter is added to this array.
  • $credentials. Initialized to an empty array. If a community string for an existing credential matches a community string that was passed in the $community_strings parameter, the URI for that credential is added to this array.

if($existing_credentials['http_code'] == 200) {

$credentials = array();

$existing_communities = array();

 

The configure_credenetials function iterates through the existing credentials for the organization in the $existing_credentials array. The community string is parsed from the name of the existing credential based on the standard naming scheme. If the community string matches a value in $community_array, the community string is added to the $existing_communities array and the URI is added to the $credentials array:

foreach($existing_credentials['content'] as $key => $credential) {

$existing_community = substr($credential['description'], strlen($customer) + 2);

$matched_community = array_search($existing_community, $community_array);

 

if($matched_community !== FALSE) {

$credentials[] = $credential['URI'];

$existing_communities[] = $community_array[$matched_community];

}

}

 

The configure_credentials function must now create a credential for any community string that appears in $community_array that does not appear in $existing_communities. The variable $error_message is initialized as an empty string; all error messages generated while credentials are added are appended to this string. The variable $diff is initialized as an array of community strings that appear in $community_array that do not appear in $existing_communities:

$error_message = "";

$diff = array_diff($community_array, $existing_communities);

 

If $diff is empty, i.e. no additional credentials need to be created, processing is complete. If new credentials need to be created, the variable $organization is initialized to the URI of the organization record associated with $customer:

if(count($diff) > 0) {

$organization = lookup_organization($base_uri, $customer);

 

If the organization URI is returned by the lookup_organization function, the configure_credentials function iterates through the community strings in $diff. For each community string, the credential name is constructed using the customer name and the community string:

if($organization !== FALSE) {

foreach($diff as $community) {

$cred_name = $customer . ": " . trim($community);

 

The variable $cred_post_array is initialized to an array that represents the content that will be used to create the credential. When the credential is created, the create_entity function encodes this array in JSON format. The array includes the following field/value pairs that are applicable to /credential/snmp resources:

(

['cred_name'] => The name of the credential.

['cred_host'] => The hostname associated with the credential. Always set to an empty string.

['cred_port'] => The port associated with the credential. Always set to the standard SNMP port, 161.

['cred_timeout'] => The timeout for the credential. Always set to a default timeout of 1500ms.

['all_orgs'] => This setting specifies whether the credential is visible to all organizations (1) or is restricted to specific organizations (0). All credentials created by the provisioning system are aligned only with the specific organization for which they are created, so this value is always set to 0.

['snmp_version'] => The SNMP version. For simplicity, this example creates only SNMP v2 credentials.

['snmp_ro_community'] =>The SNMP community string.

['aligned_organizations'] => A list of organizations to which the credential is visible. A list element in JSON is represented as an array in the equivalent PHP structure.

)

 

The $cred_post_array variable is passed to the create_entity function with the URI of an Administration Portal, Database Server, or All-In-One Appliance and the relative URI that is used to create SNMP credentials (/api/credential/snmp):

$cred_post_array = array('cred_name' => $cred_name, 'cred_host' => "", 'cred_port' => 161, 'cred_timeout' => 1500, 'all_orgs' => 0, 'snmp_version' => 2, 'snmp_ro_community' => trim($community), 'aligned_organizations' => array($organization));

$cred_response = create_entity($base_uri, "/api/credential/snmp", $cred_post_array);

 

The create_entity function returns an array of two values. Index 0 in the returned array is a boolean that indicates whether the entity was created successfully. Index 1 in the returned array is the URI of the created entity on success or an error message on failure. If the credential was created successfully, the URI of the new credential is added to the $credentials array. If the credential was not created, the error message from the create_entity function is appended to $error_message:

if($cred_response[0]) {

$credentials[] = $cred_response[1];

}

else {

$error_message .= $cred_response[1];

}

}

}

If no organization URI was returned by the lookup_organization function, an error message is appended to $error_message:

else {

$error_message .= "Could not find organization record for customer: " . $customer . ". ";

}

}

}

 

If the request for existing credentials is not successful, an the $error_message variable is set to an error message that includes the error message constructed by the perform_request function, if available:

else {

$error_message = "Could not get list of existing credentials. ";

if(array_key_exists("error", $existing_credentials)) {

$error_message .= $existing_credentials['error'] . ". ";

}

}

 

If an error message has been generated by the create_credentials function, that error message is returned. Otherwise, the array of credential URIs is returned:

if(strlen($error_message) == 0) {

return $credentials;

}

else {

return $error_message;

}

}

 

Requesting Discovery Session Logs

The get_discovery_result function is designed to return an array that contains information about a specified discovery session. The returned array has the following structure:

(

['status'] => An integer that specifies the result of the get_discovery_result function:

0 = The specified discovery session has completed and get_discovery_result was able to return a list of devices discovered by the discovery session.

1 = The specified discovery session is currently running and get_discovery_result was able to return a list of devices discovered by the discovery session.

2 = The specified discovery session has never been run.

3 = An error occurred in a request made by the get_discovery_result function.

 

['devices'] => If the returned status is 0 or 1, is set to an array of device arrays. Each device array includes "ip", "name", "uri", and "new" keys. The "new" key is a boolean that is set to TRUE if the device was discovered as a new device or FALSE if the device was discovered as an existing device.

 

['error'] => If the returned status is 3, is set to an error message.

)

 

The get_discovery_result function requires the following parameters:

  • $base_uri. The URL of an Administration Portal, Database Server, or All-In-One Appliance.
  • $session_uri. The URI for a discovery session resource.

The get_discovery_result has the following optional parameter:

  • $new_only. If TRUE is passed in this parameter, the list of devices returned by the function will include only newly discovered devices from the discovery session. By default, the function returns all discovered devices, both new and existing, from the discovery session.

function get_discovery_result($base_uri, $session_uri, $new_only = FALSE) {

 

The function includes a do-while loop in which all log messages for a discovery session are requested. Like the get_all function, a limit of 100 is specified in the logs URI and the offset is increased on each iteration of the do-while loop. The variable $discovery_logs is initialized as an array, to which the log messages in the responses will be added. The variable $not_started is initialized as FALSE. If the logic within the do-while loop determines that the discovery session is not running, this variable is set to TRUE:

$log_uri = $session_uri . "/log?extended_fetch=1&limit=100&offset=";

$offset=0;

$discovery_logs = array();

$not_started = FALSE;

 

do {

$response = perform_request($base_uri, $log_uri . $offset, "GET");

 

If the request for logs in this iteration of the do-while loop successfully returns logs, the returned logs are added to the $discovery_logs array:

if($response['http_code'] == 200 AND array_key_exists("result_set", $response['content']) AND count($response['content']['result_set']) > 0) {

$discovery_logs = array_merge($discovery_logs, $response['content']['result_set']);

}

 

If the request for logs is successful but does not return any logs, the function must determine whether the discovery session was never started or if the discovery session is running but has not yet generated any logs. To do this, the URI of the discovery session is manipulated to determine the equivalent /api/discovery_session_active URI:

elseif($response['http_code'] == 200 AND array_key_exists("total_matched", $response['content']) AND $response['content']['total_matched'] == 0) {

$uri_array = explode("/", $session_uri);

$uri_array[2] = "discovery_session_active";

$active_uri = implode("/", $uri_array);

 

The function performs a GET request on the /discovery_session_active URI for the specified discovery session. If the response includes an HTTP status code of 200, the discovery session is currently running. The output array ($result) is initialized with a status of 1 (running) with an empty array of devices:

$active_check = perform_request($base_uri, $active_uri, "GET");

 

if($active_check['http_code'] == 200) {

$result = array("status" => 1, "devices" => array());

}

 

If the response includes an HTTP status code of 303 (See Other), the discovery session exists but is not currently running. The output array ($result) is initialized with a status of 2 (never run) and an appropriate error message:

elseif($active_check['http_code'] == 303) {

$result = array("status" => 2, "error" => "Discovery Session has never run.");

}

 

If the response includes an HTTP status code other than 200 or 303, an error occurred with the request. The output array ($result) is initialized with a status of 3 (error) and an appropriate error message:

else {

$result = array("status" => 3, "error" => "Could not determine status of discovery session. ");

if(array_key_exists("error", $active_check)) {

$result['error'] .= $active_check['error'];

}

}

}

 

If the request for discovery session logs fails (HTTP status code is not 200), the output array ($result) is initialized with a status of 3 (error) and an appropriate error message:

else {

$result = array("status" => 3, "error" => "Could not get discovery session logs ");

if(array_key_exists("error", $response)) {

$result['error'] .= $response['error'];

}

}

 

The offset is increased for the next iteration for the do-while loop. The loop continues if the output array ($result) has not been initialized, i.e. the request for logs was successful and returned one or more logs, and if more logs are available. The "total_matched" value from the previous response indicates the total number of logs that can be returned; more logs are available if the current offset value is lower than "total_matched"":

$offset = $offset + 100;

 

} while(!isset($result) AND array_key_exists("total_matched", $response['content']) AND ($offset < $response['content']['total_matched']));

 

If the output array ($result) has not been initialized, all requests performed in the do-while loop were successful and one or more logs were returned. In this case, the status returned by the get_discovery_result function will be either 0 (logs were successfully returned and the discovery session is complete) or 1 (logs were successfully returned and the discovery session is still running). The function iterates through the array of returned log messages:

if(!isset($result)) {

$result = array("devices" => array());

foreach($discovery_logs as $log) {

 

Each discovery session log includes a "msg_id" field, which specifies the type of message in the log entry. To return a list of devices and to determine the state of the discovery session, the get_discovery_results function uses only log messages that have one of the following msg_id values:

  • 125. Associated with the log message that indicates the discovery session is complete.
  • 500. Associated with the log message that is generated when an existing device is found by the discovery session.
  • 501. Associated with the log message that is generated when a new device is found by the discovery session.

The "msg_id" field is used in a switch statement, which includes cases for the three values:

switch($log['msg_id']) {

 

If the log message indicates the discovery session is complete, the status key in the output array is set to 0:

case 125:

$result['status'] = 0;

break;

 

If the log message indicates an existing device is found and the $new_only parameter is set to FALSE, the device is added to the device array:

case 500:

if(!$new_only) {

$result['devices'][] = array("ip" => $log['ip'], "name" => $log['name'], "uri" => $log['device'], "new" => FALSE);

}

break;

 

New devices are always added to the device array:

case 501:

$result['devices'][] = array("ip" => $log['ip'], "name" => $log['name'], "uri" => $log['device'], "new" => TRUE);

break;

}

}

 

If the status key in the output array has not been set after all log messages have been evaluated, the discovery session is still running:

if(!array_key_exists("status", $result)) {

$result['status'] = 1;

}

}

 

return $result;

}

 

Requesting an Available Data Collection Unit

To create a discovery session using the API, you must specify the URI of an /appliance resource. The supplied /appliance resource must be an All-In-One Appliance or a Data Collector. The get_collector_id function is designed to return the URI of an appliance for discovery.

The get_collector_id function requires the following parameter:

  • $base_uri. The URL of an Administration Portal, Database Server, or All-In-One Appliance.

The get_collector_id function returns an array:

  • The first array value (array index 0) is a boolean that indicates whether an appropriate appliance resource was found.
  • The second array value (array index 1) is a string. On success, the string is the URI of an appropriate appliance. On failure, the string is an error message.

For systems that include All-In-One Appliances, the function returns the URI of the currently active All-In-One Appliance. For distributed systems, the function returns the URI of a Data Collector in a collector group.

An initial request is made on the /appliance resource index. The request includes filter criteria that specifies that only All-In-One Appliances (type = "ao") that are currently active (ha_status = 1) should be returned:

function get_collector_id($base_uri, $num_devices) {

 

$resource = "/api/appliance?limit=100&filter.type=ao&filter.ha_status=1&hide_filterinfo=1";

$response = perform_request($base_uri, $resource, "GET");

 

If the response includes at least one appliance, the URI of that appliance is returned:

if($response['http_code'] == 200 AND count($response['content']) > 0) {

return array(TRUE, $response['content'][0]['URI']);

}

 

If the initial request fails, the function returns an error message:

elseif($response['http_code'] != 200) {

$error_message = "Request for list of appliances failed. ";

if(array_key_exists("error", $response)) {

$error_message .= $response['error'];

}

return array(FALSE, $error_message);

}

 

If the initial request is successful, but does not return any appliances, a request is made for all collector groups in the system using the extended fetch option:

else {

$resource = "/api/collector_group?limit=100&hide_filterinfo=1&extended_fetch=1";

$response = perform_request($base_uri, $resource, "GET");

 

If the request for collector groups is successful and at least one collector group is returned, the function iterates through the array of returned collector groups. For each collector group, the function checks the data_collectors field. If a collector group includes at least one Data Collector, the URI of the first Data Collector in that collector group is returned:

if($response['http_code'] == 200 AND count($response['content']) > 0) {

foreach($response['content'] as $cug_id => $cug) {

if(array_key_exists("data_collectors", $cug) AND count($cug['data_collectors']) > 0) {

return array(TRUE, $cug['data_collectors'][0]);

}

}

}

 

If the request for collector groups is not successful (the HTTP Status Code in the response is not 200), an appropriate error message is returned. If an error message was returned by the perform_request function, it is included in the error message:

elseif($response['http_code'] != 200) {

$error_message = "Request for list of collector groups failed. ";

if(array_key_exists("error", $response)) {

$error_message .= $response['error'];

}

return array(FALSE, $error_message);

}

 

If the request for collector groups is successful but does not return any collector groups, an appropriate error message is returned:

else {

return array(FALSE, "No collector groups configured on system.");

}

}

}

 

Requesting a List of Referenced Entities

API resources that represent a specific entity can include references to other entities. These references are displayed as the relative URI of that other entity. For example, if you perform a GET request on "/api/device/1", the response will include a "class_type" field that contains the URI of the device class associated with the device. The get_join_resources function is designed to return an array of entities referenced in a particular field in a passed array of entities.

The get_join_resources function requires the following parameters:

  • $base_uri. The URL of an Administration Portal, Database Server, or All-In-One Appliance.
  • $entity_list. An array that contains the entities that include a field that references another entity. For example, if you want to retrieve an array of device classes that are associated with a set of devices, you would pass the array of devices in the $entity_list parameter.
  • $left_join_field. The name of the field associated with the entities in the $entity_list that reference the other entity. For example, all devices include a "class_type" field that specifies the URI of the device class associated with that device; therefore, if you want to retrieve an array of device classes that are associated with a set of devices, you would pass "class_type" in the $left_join_field parameter.
  • $right_join_field. For efficiency, the get_join_resources function performs a single request for all referenced entities instead of performing a request on each referenced URI. To request all referenced entities, the get_join_resources function performs a request to the appropriate resource index with the extended_fetch option enabled. To limit the request to only entities that are referenced by the entities in the $entity_list, the get_join_resources function concatenates the ID values of each referenced entity and passes them as a search value using the "in" function. To do this, the get_join_resources function must specify the field in the referenced entity that contains the ID value. You must pass the name of that field in the $right_join_field parameter. For example, if you want to retrieve an array of device classes that are associated with devices and the get_join_resources function determines that the devices in $entity_list are associated with device classes 2, 5, and 9, the get_join_resources function must pass the following URI to the get_all function to get the list of device classes:
  • /api/device_class?extended_fetch=1&filter.class_type.in=2,5,9

     

    The field used in the filter clause must be passed in the $right_join_field parameter. In this case, $right_join_field is "class_type".

The get_join_resources function returns an array of entities on success or an error message on failure. If no referenced entities are found, the returned array is empty.

The get_join_resources function initializes $in as an array. The $in array is used to track the ID values of the entities that must be requested:

function get_join_resources($base_uri, $entity_list, $left_join_field, $right_join_field) {

$in = array();

 

The get_join_resources function iterates through the array of entities passed in the $entity_list parameter. For each entity in the array, the function looks up the URI for the referenced entity using the field name passed in the $left_join_field parameter. Two variables are populated by the URI of the referenced entity:

  • $join_uri. Initialized to an array that contains each section of the URI (delimited by the "/" character) as an element. The specific resource ID is removed from the end of the array using the array_pop function; therefore, when the foreach loop completes, $join_uri is an array that contains each section of the URI for the resource index of the referenced entity.
  • For example, if the URI of the referenced entity is /credential/snmp/1, the$join_uri array looks like this when the foreach loop completes:

    (

    [0] => "credential"

    [1] => "snmp"

    )

     

  • $in. An element in this array is set to the ID value of the referenced entity.

foreach($entity_list as $entity) {

if(array_key_exists($left_join_field, $entity)) {

$join_uri = explode("/", $entity[$left_join_field]);

$in[] = array_pop($join_uri);

}

}

 

If at least one entity ID exists in the $in array, the get_join_resources function constructs a URI for the resource index of the referenced entities using:

  • The $join_uri array, which contains the base resource index URI.
  • The value in $right_join_field, which is the field in the referenced entity that contains the ID values for that entity.
  • The $in array, which contains the ID values of each referenced entity from the list of entities that was passed in the $entity_list array.

The URI is used to request a list of referenced entities:

if(count($in) > 0) {

$uri = implode("/", $join_uri) . "?extended_fetch=1&filter." . $right_join_field . ".in=" . implode(",", $in);

$join_list = get_all($base_uri, $uri);

 

The get_all function returns an array or entities on success and an error message on failure. Because the get_all function returns the same values as the get_join_resourcesfunction, the result of the get_all function can be returned without additional processing:

return $join_list;

}

 

If no entity IDs exist in the $in array, the function returns an empty array without performing a request:

else {

return array();

}

}

 

User Interface

The example provisioning system comprises the following front-end files to display the user interface:

  • index.php. Provides a user interface for provisioning a new customer and discovering additional devices for an existing customer.
  • devices.php. Provides a user interface for configuring customer devices that have been discovered in SL1.
  • remove.php. Provides a user interface for removing a customer from SL1.

The user interface files use the following additional files:

  • header.php. Includes the common elements used by all three user interface pages.
  • provisioning.css. Includes style information for the user interface pages. In this example, minimal style is applied to the user interface pages. You can customize the user interface pages by adding style information to this file.

The following sections describe each of these five files.

header.php

The header.php file is required in all user interface PHP files. The header.php file outputs links to each user interface page, includes utils.php, and outputs messages from the back-end PHP files:

<p><a href="index.php">Run Discovery</a> | <a href="devices.php">Configure Devices</a> | <a href="remove.php">Remove Customer</a></p>

<?php

require_once 'utils.php';

session_start();

 

if(isset($_SESSION['message'])) {

echo "<p>" . $_SESSION['message'] . "</p>";

unset($_SESSION['message']);

}

else {

echo "<p>&nbsp;</p>";

}

?>

 

index.php

The index.php file provides a user interface for provisioning a new customer and discovering additional devices for an existing customer:

<html>

<head>

<title>Provision Customer</title>

<link href="provisioning.css" rel="stylesheet" type="text/css">

</head>

<body>

<?php

require_once 'header.php';

?>

<form action="provision_customer.php" method="post">

Customer Name:<br />

<input type="text" name="customer" /><br />

Device IP List (Comma-separated):<br />

<input type="text" name="ip_addresses" /><br />

SNMP v2 Community Strings:<br />

<input type="text" name="community_strings" /><br />

<input type="checkbox" name="non_snmp" value="yes" /> Discover Non-SNMP Devices<br />

<input type="submit" value="Submit" />

</form>

</body>

</html>

 

When you enter customer information and select the Submit button, the provision_customer.php script performs the required tasks for provisioning that customer.

devices.php

The devices.php file provides a user interface for configuring customer devices in SL1:

When you enter a customer name and select the Show Devices button, the configure_devices.php script returns a list of devices that are then displayed in the devices.php page. For each device in the list of devices, the devices.php page displays a drop-down list of all device templates in the Service Level column. The last device template that was applied to the device is selected by default:

<html>

<head>

<title>Configure Devices</title>

<link href="provisioning.css" rel="stylesheet" type="text/css">

</head>

<body>

<?php

require_once 'header.php';

 

The configure_devices.php back-end script returns session variables that contain the values that were supplied in the Customer Name field and radio buttons. These session variables are stored in a local variable then unset. The local variables are used to populate the form fields:

if(isset($_SESSION['customer'])) {

$customer = $_SESSION['customer'];

unset($_SESSION['customer']);

}

else {

$customer = "";

}

if(isset($_SESSION['dev_type'])) {

$dev_type = $_SESSION['dev_type'];

unset($_SESSION['dev_type']);

}

else {

$dev_type = "";

}

?>

 

<p>Devices To Configure:</p>

<form action="configure_devices.php" method="post">

Customer Name:

<input type="text" name="customer" value="<?php echo $customer; ?>" /><br />

<input type="radio" name="dev_type" value="new_disc" <?php if($dev_type == "new_disc") echo checked; ?> /> New Devices from last Discovery<br />

<input type="radio" name="dev_type" value="all_disc" <?php if($dev_type == "all_disc") echo checked; ?> /> All Devices from last Discovery<br />

<input type="radio" name="dev_type" value="all_org" <?php if($dev_type == "all_org") echo checked; ?> /> All Devices in Organization<br />

<input type="submit" value="Show Devices" />

 

If the configure_devices.php script sets session variables for an array of one or more devices, an array of device classes, and an array of device templates, the devices.php page displays a table of devices:

<?php

if(isset($_SESSION['dev_list']) AND isset($_SESSION['class_list']) AND count($_SESSION['dev_list']) > 0 AND isset($_SESSION['templates'])) {

$templates = $_SESSION['templates'];

 

The variable $table is used to build the HTML that will display the table of devices. A foreach loop iterates through each device in the array of devices:

$table = "<table><tr><th>Device Name</th><th>Device IP</th><th>Device Type</th><th>Service Level</th></tr>";

 

foreach($_SESSION['dev_list'] as $key => $device) {

 

On each iteration of the foreach loop, the $device variable is set to an array of all fields for the current device. The configure_devices.php script sets the value of the custom field "c-last_dev_tpl" only if a template other than the base template is applied. Therefore, if "c-last_dev_tpl" is NULL, it is assumed that the base template was the last template to be applied to the device. The variable $service_level is initialized to the last device template applied to the current device:

if(is_null($device['c-last_dev_tpl']) or $device['c-last_dev_tpl'] == "") {

$service_level = get_base_template();

}

else {

$service_level = $device['c-last_dev_tpl'];

}

 

The Device Name and Device IP columns are populated using the appropriate values from the $device array. The Device Type column is populated using values from the array of device classes ($_SESSION['class_list']). The keys in the array of device classes are the device class URIs; the field from the device resource that references the associated device class (class_type) is used to look up the device class for this device. The class and description fields from the device class are combined to populate the Device Type column:

$table .= "<tr>";

$table .= "<td>" . $device['name'] . "</td>";

$table .= "<td>" . $device['ip'] . "</td>";

$table .= "<td>" . $_SESSION['class_list'][$device['class_type']]['class'] . " " . $_SESSION['class_list'][$device['class_type']]['description'] . "</td>";

 

The drop-down list in the Service Level column is constructed using the array of device templates ($templates). The name of the drop-down list (which will appear as a key in the $_POST array) is set to the URI of the current device (the current array key from $_SESSION['dev_list']):

$table .= "<td><select name=\"" . $key . "\">";

 

A drop-down list option is added for each device template. The last template that was applied to the device ($service_level) is selected as the default option. If the last template that was applied to the device is selected when the form is submitted, the value of the drop-down list is set to NULL so that it can be skipped by the configure_devices.php script:

foreach($templates as $template) {

if($service_level == $template['URI']) {

$table .= "<option value=NULL selected=\"selected\">" . $template['description'] . "</option>";

}

 

For all other device templates, the value of the drop-down list is set to the URI of the device template:

else {

$table .= "<option value=\"" . $template['URI'] . "\">" . $template['description'] . "</option>";

}

}

$table .= "</select>";

$table .= "</tr>";

}

 

The HTML for the table is completed and outputted. To prevent an error in the next execution of configure_devices.php from producing erroneous results in this page, the session variables that contain the array of devices, the array of device classes, and the array of device templates are unset:

$table .= "<tr><td colspan=\"3\"></td><td><input type=\"submit\" name=\"config\" value=\"Configure Devices\" /></td></tr>";

$table .= "</table>";

echo $table;

unset($_SESSION['dev_list']);

unset($_SESSION['class_list']);

unset($_SESSION['templates']);

}

 

If an empty array of devices is returned by configure_devices.php, an informational message is displayed instead of an empty table:

elseif(isset($_SESSION['dev_list'])) {

echo "<p>No Devices Discovered</p>";

unset($_SESSION['dev_list']);

unset($_SESSION['class_list']);

}

?>

</form>

</body>

</html>

 

remove.php

The remove.php file provides a user interface for removing a customer from the system:

<html>

<head>

<title>Remove Customer</title>

<link href="provisioning.css" rel="stylesheet" type="text/css">

</head>

<body>

<?php

require_once 'header.php';

?>

<form action="delete_customer.php" method="post">

Customer Name:<br />

<input type="text" name="customer" /><br />

<input type="submit" value="Submit" />

</form>

</body>

</html>

 

When you enter a customer name and select the Submit button, the delete_customer.php script deletes all devices, credentials, and discovery sessions associated with that customer's organization record; deletes the organization record; and then returns a status message to remove.php.

provisioning.css

The provisioning.css file includes style information for the user interface pages. In this example, minimal style is applied to the user interface pages. You can customize the user interface pages by adding style information to this file:

table {width: 100%; border-collapse:collapse; text-align: center;}

th {border: solid 1px;}

td {border: solid 1px;}

 

Provisioning a Customer (provision_customer.php)

The provision_customer.php script processes the input values from index.php and performs the following provisioning tasks:

  • If an organization record does not currently exist for the customer, creates an organization record for the customer.
  • Ensures that SNMP credentials are configured for each supplied SNMP community string.
  • Creates a discovery session for the customer using the configured SNMP credentials and the supplied list of IP addresses.
  • Runs the discovery session.

If all of these tasks are successful, the script redirects to configure_devices.php. configure_devices.phpwill return a list of discovered devices to the devices.php page. If a provisioning task is unsuccessful, provision_customer.php returns an error message to index.php.

All back-end files:

  • Use PHP session variables to return values to the user interface files.
  • Use the functions defined in the utils.php file.
  • Use the URL of an Administration Portal, Database Server, or All-In-One Appliance.

The provision_customer.php script starts by initializing the session, requiring utils.php, and initializing $base_uri to the URL of an Administration Portal, Database Server, or All-In-One Appliance:

<?php

session_start();

require_once 'utils.php';

$base_uri = get_admin_uri();

 

The provision_customer.php script validates the input to ensure that a customer name, IP address list, and either a community string list or the discover Non-SNMP flag were supplied:

if(isset($_POST['customer']) AND $_POST['customer'] != "" AND isset($_POST['ip_addresses']) AND $_POST['ip_addresses'] != "" AND

((isset($_POST['community_strings']) AND $_POST['community_strings'] != "") OR (isset($_POST['non_snmp']) AND $_POST['non_snmp'] == "yes"))) {

 

The provision_customer.php script attempts to lookup the URI of the organization record associated with the customer name supplied in the input form. If no organization record is found, the script creates a new organization record using the create_entity function. The array of fields for the new organization record includes only the name of the organization:

$organization = lookup_organization($base_uri, $_POST['customer']);

if($organization === FALSE) {

$org_post_array = array('company' => $_POST['customer']);

$org_response = create_entity($base_uri, "/api/organization", $org_post_array);

 

If the request to create an organization record is successful (the create_entity function returns TRUE at array index 0), the $organization variable is set to the URI of the organization:

if($org_response[0]) {

$organization = $org_response[1];

}

 

If the request to create an organization fails, the $message variable is set to an appropriate error message:

else {

$message = "Failed to create org: " . $org_response[1];

}

}

 

If an organization record already exists for the supplied customer name, the provision_customer.php script deletes any existing discovery sessions associated with that organization record. By deleting existing discovery sessions, the provision_customer.php script maintains a 1:1 mapping between organization records and discovery sessions. Maintaining a 1:1 mapping reduces the amount of processing required to retrieve a list of devices from the last discovery session that was run for a particular customer. The provision_customer.php script constructs a URI for the /discovery_session resource index that includes a filter for the organization record ID. The organization record ID is appended to the URI by using the last element in an array that contains each piece of the organization URI:

else {

$uri = "/api/discovery_session?limit=10&hide_filterinfo=1&filter.organization=" . array_pop(explode("/", $organization));

$response = perform_request($base_uri, $uri, "GET");

 

If the request for a list of discovery sessions is successful, the multi_delete function is called to delete the discovery sessions in the response. The multi_delete function returns NULL if all the supplied entities are deleted or an error message if one or more supplied entities are not deleted. If the request for the list of discovery sessions fails or if multi_delete did not return null, the $message variable is set to an appropriate error message:

if($response['http_code'] == 200) {

$error = multi_delete($base_uri, $response['content']);

}

else {

$error = "Could not clean up existing discovery sessions for organization.";

}

if(!is_null($error)) {

$message = $error;

}

}

 

If no error message has been set in the $message variable, the provision_customer.php script continues with the provisioning process:

if(!isset($message)) {

 

If a list of community strings was supplied in the input form, the configure_credentials function is used to get an array of credential URIs for those community strings:

if(isset($_POST['community_strings']) AND $_POST['community_strings'] != "") {

$credentials = configure_credentials($base_uri, $_POST['customer'], $_POST['community_strings']);

}

 

If a list of community strings was not supplied in the input form, i.e. the discovery session will be configured to discover only non-SNMP devices, the $credentials variable is initialized as an empty array:

else {

$credentials = array();

}

 

If the $credentials variable is an array, i.e. no error message was returned by the configure_credentials function or the discovery session will be configured to discover only non-SNMP devices, the script explodes the supplied list of IP addresses in to an array:

if(is_array($credentials)) {

$ip_array = explode(",", $_POST['ip_addresses']);

 

To create a discovery session using the API, the JSON content must include a list of IP address ranges. Each IP address range must specify a start address and an end address. In PHP array format, the array that contains the discovery session fields must include an "ip_lists" key that points to an array that has the following structure:

(

[0] => array (

['start_ip'] =>

['end_ip'] =>

)

.

.

.

[N] => array (

['start_ip'] =>

['end_ip'] =>

)

)

 

The script initializes the variable $ip_lists, which will contain this structure. For each IP address in the array of IP addresses supplied in the input form, an element is added to $ip_lists. Each IP address is used as both the start and end address for each IP address "range":

$ip_lists = array();

foreach($ip_array as $address) {

$ip_lists[] = array('start_ip' => $address, 'end_ip' => $address);

}

 

The script then uses the get_collector_id function to get the URI of an appliance on which the discovery session can run:

$collector = get_collector_id($base_uri);

 

The get_collector_id returns an array. The boolean value at array index 0 indicates whether an appliance URI was successfully returned. The value at array index 1 is either an appliance URI or an error message. If an appliance URI was returned, a discovery session is created using the following field values:

  • organization. The organization URI ($organization).
  • aligned_collector. The appliance URI returned by the get_collector_id function.
  • aligned_device_template. The standard device template returned by the get_base_template function.
  • initial_scan_level. To limit what is monitored on each discovered device to only what is defined in the applied device templates, the initial scan level is set to 0 (Model Device Only).
  • ip_lists. The array of start and end IP addresses ($ip_lists).
  • credentials. The array of credentials returned by the configure_credentials function.
  • discover_non_snmp. If Discover Non-SNMP Devices was selected in the input form, this value is set to 1 (discover non-SNMP devices).

 

if($collector[0]) {

$disc_post_array = array('organization' => $organization,

'aligned_collector' => $collector[1],

'aligned_device_template' => get_base_template(),

'initial_scan_level' => 0,

'ip_lists' => $ip_lists,

'credentials' => $credentials);

 

if(isset($_POST['non_snmp']) AND $_POST['non_snmp'] == "yes") {

$disc_post_array['discover_non_snmp'] = 1;

}

$disc_response = create_entity($base_uri, "/api/discovery_session", $disc_post_array);

 

The create_entity function returns an array. The boolean value at array index 0 indicates whether the entity was successfully created. The value at array index 1 is either the entity URI or an error message. If the discovery session was created successfully, the discovery session is started by applying the URI of the discovery session to the /discovery_session_active resource index:

if($disc_response[0]) {

$run_discovery = perform_request($base_uri, "/api/discovery_session_active", "APPLY", $disc_response[1]);

 

If the response from the request to start the discovery session includes HTTP status code 202, the discovery session started correctly. If the discovery session starts correctly, the script redirects to the configure_devices.php script. The configure_devices.php script requires the customer name as input, either in the $_POST or $_SESSION array. In this case, the customer name is set as a session variable:

if($run_discovery['http_code'] == 202) {

$_SESSION['customer'] = $_POST['customer'];

header("Location: configure_devices.php");

}

 

If the request to start the discovery session failed, i.e. the HTTP status code in the response is not 202, the $message variable is set to an appropriate error message:

else {

$message = "Failed to run discovery session: " . $run_discovery['http_code'];

}

}

 

If an error was returned by the create_entity function, the $message variable is set to an appropriate error message:

else {

$message = "Failed to create discovery session: " . $disc_response[1];

}

}

 

If an error was returned by the get_collector_id function, the $message variable is set to an appropriate error message:

else {

$message = $collector[1];

}

}

 

If $credentials is not an array, i.e. the configure_credentials function returned an error message, the $message variable is set to an appropriate error message:

else {

$message = "Failed to configure credentials: " . $credentials;

}

}

}

 

If the values supplied in the input form fail validation, the $message variable is set to an appropriate error message:

else {

$message = "Form Incomplete";

}

 

If the $message variable is set, a failure occurred in the provision_customer.php script. The error is set in a session variable and the script redirects back to index.php:

if(isset($message)) {

$_SESSION['message'] = $message;

header("Location: index.php");

}

?>

 

Retrieving and Configuring Devices (configure_devices.php)

The configure_devices.php script returns a list of devices and associated device classes for a specified customer. The list of devices can be all devices associated with the customer's organization record, all devices from the last discovery session for that customer, or new devices from the last discovery session for that customer.

Additionally, if a user selects the Configure Devices button in the devices.php page, the configure_devices.php script applies the device templates selected by the user to the specified devices.

All back-end files:

  • Use PHP session variables to return values to the user interface files.
  • Use the functions defined in the utils.php file.
  • Use the URL of an Administration Portal, Database Server, or All-In-One Appliance.

The script starts by initializing the session, requiring utils.php, and initializing $base_uri to the URL of an Administration Portal, Database Server, or All-In-One Appliance:

<?php

session_start();

require_once 'utils.php';

$base_uri = get_admin_uri();

 

For each displayed device, the devices.php page displays a drop-down list that contains all device templates in the system. To populate the drop-down list, the devices.php page must be supplied a list of device templates. An array of all device templates is set as a session variable. Because the list of device templates is assumed to be static, the array of device templates is set only once per session and is never explicitly unset by the provisioning code. The code that creates the array of device templates is located in configure_devices.php because the script is always run before devices are displayed in devices.php.

If the templates variable is not currently set in the session variables, the script gets a list of all device templates using the get_all function. The get_all function returns an array of entities on success, or an error message on failure. If the return value is an array, that array is set as a session variable. If the return value is not an array, the $message variable is assigned the returned error message:

if(!isset($_SESSION['templates'])) {

$templates = get_all($base_uri, "/api/device_template?link_disp_field=template_name");

if(is_array($templates)) {

$_SESSION['templates'] = $templates;

}

else {

$message = $templates;

}

}

 

The configure_devices.php script takes a customer name as input. The customer name is passed either as post data from devices.php or in a session variable from provision_customer.php. The script uses the customer name to lookup the organization URI:

if(isset($_POST['customer']) AND $_POST['customer'] != "") {

$customer = $_POST['customer'];

$_SESSION['customer'] = $_POST['customer'];

$organization = lookup_organization($base_uri, $customer);

}

elseif(isset($_SESSION['customer'])) {

$customer = $_SESSION['customer'];

$organization = lookup_organization($base_uri, $customer);

}

 

If an organization URI is found for the supplied customer name, an error message has not been set, and the user selected the Configure Devices button ("config" is a key in the post data), the block of code that applies device templates to devices is executed:

if(array_key_exists("config", $_POST) AND $organization != FALSE AND !isset($message)) {

 

The variable $dev_type is initialized with the value from the radio buttons on the devices.php page. Later in the execution of the configure_devices.php script, $dev_type is used to set a session variable that the devices.php page uses as the default value of the radio buttons:

if(isset($_POST['dev_type'])) {

$dev_type = $_POST['dev_type'];

}

 

The script iterates through all values supplied by the input form. The variable $devices_updated is initialized to track the number of devices to which device templates are applied. The variable $in is initialized as an array, which will be used to track the device ID values for all devices that were previously displayed on devices.php. The $in array will be used to return the same list of devices to devices.php:

$in = array();

$devices_updated = 0;

foreach($_POST as $device => $template) {

 

The $_POST array includes all values supplied by the input form. This block of code must operate only on the values from the drop-down list for each device. Each drop-down list is named using the URI of the associated device; therefore, it is assumed that if a key in the $_POST array begins with a slash character ("/"), the array element represents a drop-down list:

if(strpos($device, "/") === 0) {

 

The device ID of all devices that were displayed on devices.php is added to the $in array:

$in[] = array_pop(explode("/", $device));

 

If the user did not select a new device template from the drop-down list for a device, the value for that drop-down list is "NULL". The block of code that applies a device template to a device is executed only if the value for the drop-down list is not "NULL". Note that the input form passes "NULL" as a string, not the NULL data-type:

if($template != "NULL") {

 

To apply a device template to a device, the script uses the perform_request function with a $type parameter of "APPLY":

$apply_template = perform_request($base_uri, $device, "APPLY", $template);

 

If the request to apply a device template to a device is successful (the response includes a HTTP status code of 200), the script must update the device resource with the new value for the c-last_dev_tpl field. To do this, the device resource is requested:

if($apply_template['http_code'] == 200) {

$dev = perform_request($base_uri, $device, "GET");

 

If the request for the device resource is successful, the new value of "c-last_dev_tpl" field is set in the array of attributes for that device and the array of attributes is POSTed back to the same device resource:

if($dev['http_code'] == 200) {

$dev['content']['c-last_dev_tpl'] = $template;

 

$update_device = perform_request($base_uri, $device, "POST", $dev['content']);

 

If the request to update a device resource fails, the variable $message is initialized with an error message:

if($update_device['http_code'] != 200) {

$message = "Could not update template status of " . $device . ". ";

if(array_key_exists("error", $update_device)) {

$message .= $update_device['error'];

}

}

 

If the request to update a device resource is successful, the number of devices that have been updated is incremented:

else {

$devices_updated++;

}

}

 

If the request for a device resource fails, the variable $message is initialized with an error message:

else {

$message = "Could not get information to update template status of " . $device . ". ";

if(array_key_exists("error", $udev)) {

$message .= $dev['error'];

}

}

}

 

If the request to apply a device template to a device fails, the variable $message is initialized with an error message:

else {

$message = "Could not apply " . $template . " to " . $device . ". ";

if(array_key_exists("error", $apply_template)) {

$message .= $apply_template['error'];

}

}

}

}

}

 

The $in array, which includes the device IDs of all devices that were previously displayed in devices.php, is used to re-request the list of devices:

$uri = "/api/device?extended_fetch=1&filter.id.in=" . implode(",", $in);

$device_list = get_all($base_uri, $uri);

 

If the request for the list of devices is successful, the script requests a list of device classes for those devices:

if(is_array($device_list)) {

$class_list = get_join_resources($base_uri, $device_list, "class_type", "class_type");

 

If the request for a list of device classes is successful, the array of devices and the array of device classes are passed back to devices.php in session variables:

if(is_array($class_list)) {

$_SESSION['class_list'] = $class_list;

$_SESSION['dev_list'] = $device_list;

}

 

If the request for the list of device classes fails, the variable $message is initialized with the error message returned by the get_join_resources function:

else {

$message = $class_list;

}

}

 

If the request for the list of all devices fails, the variable $message is initialized with the error message returned by the get_all function:

else {

$message = $device_list;

}

 

If the $message variable has not yet been initialized, all requests were successful and the $message variable is initialized with a success message:

if(!isset($message)) {

$message = $devices_updated . " Device(s) Updated.";

}

}

If the block of code that configures devices is not executed, but an organization URI has been found for the customer and an error message has not been set, the block of code that returns a list of devices is executed:

elseif($organization != FALSE AND !isset($message)) {

 

The variable $org_id is initialized with the ID of the organization for which a list of devices has been requested:

$org_id = array_pop(explode("/", $organization));

 

 

If a value has been supplied from the radio buttons on the devcies.php page, the variable $dev_type is initialized with that value. If no value has been supplied, e.g. the script was called by provision_customer.php, the script defaults to returning a list of new devices from the last discovery session:

if(isset($_POST['dev_type'])) {

$dev_type = $_POST['dev_type'];

}

else {

$dev_type = "new_disc";

}

 

The following block of code returns a list of all devices associated with the specified organization:

if($dev_type == "all_org") {

 

The organization ID is used as the filter criteria to request a list of devices. The get_all function will return an array of all devices that match the filter criteria:

$uri = "/api/device?extended_fetch=1&filter.organization=" . $org_id;

$device_list = get_all($base_uri, $uri);

 

If the request for a list of devices is successful, the get_all function returns an array and the script requests a list of device classes for those devices:

if(is_array($device_list)) {

$class_list = get_join_resources($base_uri, $device_list, "class_type", "class_type");

 

If the request for a list of device classes is successful, the array of devices and the array of device classes are passed back to devices.php in session variables:

if(is_array($class_list)) {

$_SESSION['class_list'] = $class_list;

$_SESSION['dev_list'] = $device_list;

}

 

If the request for the list of device classes fails, the variable $message is initialized with the error message returned by the get_join_resources function:

else {

$message = $class_list;

}

}

 

If the request for the list of devices fails, the variable $message is initialized with the error message returned by the get_all function:

else {

$message = $device_list;

}

}

 

If the block of code that returns a list of all devices associated with the specified organization is not executed, the script executes a block of code that returns a list of devices from the last discovery session:

else {

 

The organization ID is used as the filter criteria to request the discovery session for the customer:

$discovery_search = perform_request($base_uri, "/api/discovery_session?limit=100&hide_filterinfo=1&filter.organization=" . $org_id, "GET");

 

 

If the request for a discovery session is successful (the response includes HTTP status code 200) and at least one discovery session is returned, the script calls the get_discovery_result function using the first discovery session in the response. It is assumed that there is a 1:1 mapping between organizations and customers; a 1:1 mapping is maintained by the provision_customer.php script. The third parameter passed to get_discovery_result is a boolean that determines whether the function will return all devices discovered by the discovery session or only new devices discovered by the discovery session:

if($discovery_search['http_code'] == 200 AND count($discovery_search['content']) > 0) {

$device_list = get_discovery_result($base_uri, $discovery_search['content'][0]['URI'], ($dev_type == "new_disc"));

 

The status code of the get_discovery_result is used in a switch statement that performs the required actions of each possible result:

switch($device_list['status']) {

 

A status code of 1 indicates that a list of devices has been returned, but the discovery session is still running. If the discovery session is still running, the $message variable is initialized to an appropriate status message. The required actions for a status code of 0 must also be performed if the status code is 1, so no break statement is included for case 1:

case 1:

$message = "Note: Discovery Session is not complete, additional devices might be discovered.";

 

A status code of 0 indicates that a list of devices has been returned and the discovery session is complete. If the array of devices is not empty, the $in variable is initialized as an array that will be used to track the device IDs of all devices in the array of devices:

case 0:

if(count($device_list['devices']) > 0) {

$in = array();

 

The script iterates through the array of devices. For each device, the device ID is derived from the device URI and is added to the $in array:

foreach($device_list['devices'] as $device) {

$in[] = array_pop(explode("/", $device['uri']));

}

 

A URI to request all devices is constructed using the device IDs in the $in array. The get_all function is used to request all the devices:

$uri = "/api/device?extended_fetch=1&filter._id.in=" . implode(",", $in);

$device_list = get_all($base_uri, $uri);

 

If the request for the list of devices is successful, the script requests a list of device classes for those devices:

if(is_array($device_list)) {

$class_list = get_join_resources($base_uri, $device_list, "class_type", "class_type");

 

If the request for a list of device classes is successful, the array of devices and the array of device classes are passed back to devices.php in session variables:

if(is_array($class_list)) {

$_SESSION['class_list'] = $class_list;

$_SESSION['dev_list'] = $device_list;

}

 

If the request for the list of device classes fails, the variable $message is initialized with the error message returned by the get_join_resources function:

else {

$message = $class_list;

}

}

 

If the request for the list of devices fails, the variable $message is initialized with the error message returned by the get_all function:

else {

$message = $device_list;

}

}

 

If the get_discovery_result function returned an empty array of devices, an empty array of devices is passed to devices.php in a session variable:

else {

$_SESSION['dev_list'] = array();

}

break;

 

If the get_discovery_result returns status 2 (discovery session has never been run), status 3 (error), or a status other than 0, 1, 2, or 3, script sets the $message variable to an appropriate error message:

case 2:

$message = $device_list['error'];

break;

case 3:

$message = $device_list['error'];

break;

default:

$message = "Error occurred retrieving discovery logs.";

}

}

 

If the request for a discovery session returned an HTTP status code of 200 but did not return any discovery sessions, the script sets the $message variable to an appropriate error message:

elseif($discovery_search['http_code'] == 200) {

$message = "No discovery session exists for customer.";

}

 

If the request for a discovery session returned an HTTP status code other than 200, the script sets the $message variable to an appropriate error message:

else {

$message = "Error finding discovery session for customer. ";

if(array_key_exists("error", $discovery_search)) {

$message .= $discovery_search['error'];

}

}

 

}

}

 

If no organization URI was found for the supplied customer name, the script sets the $message variable to an appropriate error message:

elseif(!isset($message)){

$message = "Could not find customer record." ;

}

 

If the $message and/or $dev_type variables have been set during the execution of the script, they are passed to devices.php using session variables. The script always redirects back to devices.php:

if(isset($message)) {

$_SESSION['message'] = $message;

}

if(isset($dev_type)) {

$_SESSION['dev_type'] = $dev_type;

}

header("Location: devices.php");

?>

Removing a Customer (delete_customer.php)

The delete_customer.php script takes a customer name as input; deletes all devices, credentials, and discovery sessions associated with that customer's organization record; and then deletes the organization record for that customer.

All back-end files:

  • Use PHP session variables to return values to the user interface files.
  • Use the functions defined in the utils.php file.
  • Use the URL of an Administration Portal, Database Server, or All-In-One Appliance.

The script starts by initializing the session, requiring utils.php, and initializing $base_uri to the URL of an Administration Portal, Database Server, or All-In-One Appliance:

<?php

session_start();

require_once 'utils.php';

$base_uri = get_admin_uri();

 

If a customer name was supplied in the input form, the script looks up the URI for the organization record associated with that customer name. If no organization record is found, the $message variable is set to an error message:

if(isset($_POST['customer']) AND $_POST['customer'] != "") {

$organization = lookup_organization($base_uri, $_POST['customer']);

if($organization === FALSE) {

$message = "Organization does not exist";

}

 

If an organization record exists for the customer, the ID for that organization record is parsed from the URI:

else {

$org_id = array_pop(explode("/", $organization));

 

An array of resource index URIs is constructed. The script will iterate through this array and delete all entities returned by each URI. The organization ID is used as filter criteria in each URI. If a request fails, the array keys are used to indicate the entity type where the problem occurred. To delete additional entities associated with the customer organization, for example, asset records, you can add additional URIs to this array:

$entity_types = array("devices" => "/api/device?limit=100&hide_filterinfo=1&filter.organization=" . $org_id,

"credentials" => "/api/credential/snmp?limit=100&hide_filterinfo=1&filter.cred_name.contains=" . $_POST['customer'],

"discoveries" => "/api/discovery_session?limit=100&hide_filterinfo=1&filter.organization=" . $org_id,

);

 

The script iterates through the array of URIs. For each URI:

  • A GET request is performed.
  • If the GET request is successful, the content in the response is passed to the multi_delete function, which will delete all the returned entities.
  • If the GET request is unsuccessful, an error message is set in the $error variable.
  • The steps are repeated until either an error occurs or the GET request returns no entities.

If an error occurs for a URI, the iteration through the array of URIs stops:

foreach($entity_types as $key => $entity) {

do {

$response = perform_request($base_uri, $entity, "GET");

if($response['http_code'] == 200) {

$error = multi_delete($base_uri, $response['content']);

}

else {

$error = "Could not get list of " . $key . " to delete";

}

} while(count($response['content']) > 0 AND $error == NULL);

 

if(!is_null($error)) {

break;

}

}

 

To delete an organization record from SL1, the organization must be "empty", that is, have no entities associated with it. If no error was generated when the other entities were deleted, the organization is deleted:

if(is_null($error)) {

$response = perform_request($base_uri, $organization, "DELETE");

if($response['http_code'] == 200) {

$message = "Customer removed";

}

 

If the request to delete the organization failed with a 400 HTTP status code, the organization is not empty and an error message is set in the $message variable:

elseif($response['http_code'] == 400) {

$message = "Could not delete organization because organization is not empty.";

}

 

If the request to delete the organization failed with a different HTTP status code, a generic error message is set in the $message variable:

else {

$message = "Could not delete organization. ";

if(array_key_exists("error", $response)) {

$message .= $response['error'];

}

}

}

 

If the $error variable has already been set because deleting an entity other than the organization failed, $message is set to the value of $error:

else {

$message = $error;

}

}

}

 

If no customer name is specified in the input form, an appropriate error message is set in the $message variable:

else {

$message = "Form Incomplete";

}

 

If the $message variable has been set, its value is returned to remove.php using a session variable:

if(isset($message)) {

$_SESSION['message'] = $message;

}

header("Location: remove.php");

?>