.. include:: ../external_links.txt .. |TOOLKIT_REST| replace:: REST: Toolkit .. |VERSION| replace:: 100 .. _build-da-with-low-code: ########################################################### How to Build Dynamic Applications with the |FRAMEWORK_NAME| ########################################################### This tutorial will guide you through building Dynamic Applications with the |FRAMEWORK_NAME|. By completing this tutorial, you will be able to create the same Dynamic Applications found in the |TOOLKIT_REST| available on the ScienceLogic Portal. |TOOLKIT_REST| is monitoring an SL1 stack by using the `ScienceLogic API`_. This document contains: * General concepts to have a better understanding of the framework (:ref:`summary`). * The explanation of how to create Dynamic Applications the |STEP_NAME_PLURAL| from REST: Toolkit (:ref:`creating_da_template_ppack`). * How to create Dynamic Applications using custom |STEP_NAME_PLURAL| (:ref:`custom-da`). .. _summary: ************************* |FRAMEWORK_NAME| Overview ************************* The |FRAMEWORK_NAME| can help you develop Dynamic Applications in an easy and standard way by maximizing code reuse between applications and technologies. In SL1, a collection is the sequence of actions in a specific order. These actions are defined in |STEP_NAME_PLURAL|. The |STEP_NAME_PLURAL| are pieces of code with a single purpose and can be re-used easily in different applications. The |FRAMEWORK_NAME| does not come with any available |STEP_NAME_PLURAL|, but the Toolkit PowerPacks provide the required |STEP_NAME_PLURAL| to build a Dynamic Application on a specific protocol. If a required |STEP_NAME| is missing, you can have the ability to create a custom |STEP_NAME_PLURAL|. To create a custom |STEP_NAME|, refer to the :ref:`Creating a Custom Step ` document. There are three type of |STEP_NAME_PLURAL| in the |FRAMEWORK_NAME|. #. **Syntax** - Convert a Dynamic Application Snippet Argument into a format that can be consumed by the |FRAMEWORK_NAME|. (This is the only type of |STEP_NAME| that has a non-customizable default). #. **Requestor** - A |STEP_NAME| that makes the request over the network using the proper technology (e.g. SSH, HTTP, JMX-remote, etc.) #. **Processors** - Transform and select the data from the result of a Requestor. The framework relies on the :ref:`Snippet Arguments ` to determine what to collect and how. This approach is known as “snippet argument-driven”. The |FRAMEWORK_NAME| uses a Syntax to interpret the syntax of the Snippet Argument. .. _creating_da_template_ppack: ********************************************************************************* Creating Dynamic Applications with Out-Of-The-Box |STEP_NAME_PLURAL_CAPITAL| ********************************************************************************* This section explains how to create Dynamic Applications without writing new code, by using the Available |STEP_NAME_PLURAL| from the |TOOLKIT_REST| PowerPack. We will create a Dynamic Application that reaches the SL1 API to get information. The image below shows the SL1 API found at: .. code-block:: none System > Tools > API Browser .. image:: _static/sl1api.png Start by creating a Dynamic Application by accessing the following SL1 API endpoint: .. code-block:: none /api/account .. note:: Media type specifies the format of the data as type/subtype (e.g. text/HTML, text/XML, application/JSON). In an HTTP request, the media type is specified in the request header using the Accept attribute, which specifies the response format. If you hit a SL1 endpoint directly on a browser, the response is in XML format. Browsers, by default, format the response in XML. We will create a Dynamic Application that gathers configuration data about the existing accounts in a particular SL1 server. The image below shows an example of the response for a particular server. .. image:: _static/sl1account.png For the creation of the following Dynamic Applications, we will utilize the following |STEP_NAME_PLURAL| from the |TOOLKIT_REST| PowerPack: * *low_code* Syntax to process the Snippet Argument to be consumable by the |FRAMEWORK_NAME| * *http* Requestor to gather API data from SL1 * *json* Processor to convert the API response into a Python dictionary * *jsonpath* to extract the data from the Python dictionary .. _discovery_da_creation: Discovery Dynamic Application Creation ====================================== First, create a "Snippet Configuration" Dynamic Application called *Accounts Discovery*. The creation of Dynamic Application happens in the Dynamic Application Manager, located at: .. code-block:: none System > Manage > Dynamic Applications > [Actions] button > Create New Dynamic Application You need to set the required configuration of this Dynamic Application as follows. .. list-table:: * - Application Name - Accounts Discovery * - Application Type - Snippet Configuration * - Execution Environment - REST: Toolkit |VERSION| * - Version Number - Version 1.0 * - Poll Frequency - Every 15 Minutes * - Component Mapping - Checkbox selected .. image:: _static/account_properties.png .. note:: This Dynamic Application can also be a Bulk Snippet Dynamic Application (BSDA) with no changes. However, since the substitution happens in the network requester, a BSDA will not save much execution time. In the snippet tab, paste the default snippet code. .. _default snippet: Default Snippet Code -------------------- .. literalinclude:: ../../../silo/low_code/sample_snippet.txt Save the snippet with the name *Default Snippet Code*. .. image:: _static/sl1snippetcode.png In the collection tab, create the following collection objects: * **Id** using the following configuration: .. list-table:: * - Object Name - Id * - Snippet Argument - .. code-block:: yaml low_code: id: Identifier version: 2 steps: - http: uri: "/account" - json - jsonpath: value: "$.result_set[*].URI" * - Class Type - Config Character * - Snippet - Default Snippet Code * - Component Identifiers - Unique Identifier (%U) .. image:: _static/sl1colobj1.png * **Account** using the following configuration: .. list-table:: * - Object Name - Account * - Snippet Argument - .. code-block:: yaml low_code: id: Description version: 2 steps: - http: uri: "/account" - json - jsonpath: value: "$.result_set[*].description" * - Class Type - Config Character * - Snippet - Default Snippet Code * - Component Identifiers - Device Name (%N) .. image:: _static/sl1colobj2.png .. note:: Notice that network requester only required ``/account``. The complete API endpoint is coming from the information provided by the credential (e.g. https://10.10.10.10/api). .. _configuration_dynamic_app_creation: Configuration Dynamic Application Creation ========================================== After completing creating the first Dynamic Application, create a second Dynamic Application to collect some configuration data. We will create a Snippet Configuration Dynamic Application called *Account Configuration*. For this application, the data source will be: .. code-block:: none /api/account/ .. image:: _static/account_info.png For the creation of this Dynamic Application, we use the following Out-Of-The-Box |STEP_NAME_PLURAL| from the framework: * The *Low_Code* as the argument parser. * The *HTTP Requester*, as the requester. * The *JSON Parser*, as the parser. * The *Simple Key* as the selector. In SL1, go to the Dynamic Application Manager and create the Dynamic Application with the following configuration: .. list-table:: * - Application Name - Account Configuration * - Application Type - Snippet Configuration * - Execution Environment - REST: Toolkit |VERSION| * - Version Number - Version 1.0 * - Poll Frequency - Every 5 Minutes We will use the same (:ref:`default snippet`). Save the snippet with the name *Custom Step Configuration*. To get the API endpoint to create this Dynamic Application, you will use automatic substitution. The previously created *Accounts Discovery* Dynamic Application returns the endpoints for each account in the *ID* Collection Object as displayed in the picture below. .. image:: _static/account_discovery.png To make this automatic substitution, use the value ```` as the parameter of the HTTP URI in the YAML configuration at the Snippet Argument (refer to the :ref:`substitution`). Define the following collection objects: **GROUP 1** * **Basic Information** using the following configuration: .. list-table:: * - Object Name - Basic Information * - Class Type - Label (Config Group) * - Snippet - Custom Step Configuration * - Group / Usage Type - Group 1 * **Contact First Name** using the following configuration. .. list-table:: * - Object Name - Contact First Name * - Snippet Argument - .. code-block:: yaml low_code: id: FirstName version: 2 steps: - http: uri: "" - json - simple_key: "contact_fname" * - Class Type - Config Character * - Snippet - Custom Step Configuration * - Group / Usage Type - Group 1 * **Contact Last Name** using the following configuration: .. list-table:: * - Object Name - Contact Last Name * - Snippet Argument - .. code-block:: yaml low_code: id: LastName version: 2 steps: - http: uri: "" - json - simple_key: "contact_lname" * - Class Type - Config Character * - Snippet - Custom Step Configuration * - Group / Usage Type - Group 1 * **Country** using the following configuration: .. list-table:: * - Object Name - Country * - Snippet Argument - .. code-block:: yaml low_code: id: Country version: 2 steps: - http: uri: "" - json - simple_key: "country" * - Class Type - Config Character * - Snippet - Custom Step Configuration * - Group / Usage Type - Group 1 **GROUP 2** * **User Data** using the following configuration: .. list-table:: * - Object Name - User Data * - Class Type - Label (Config Group) * - Snippet - Custom Step Configuration * - Group / Usage Type - Group 2 * **Email** using the following configuration: .. list-table:: * - Object Name - Email * - Snippet Argument - .. code-block:: yaml low_code: id: Email version: 2 steps: - http: uri: "" - json - simple_key: "email" * - Class Type - Config Character * - Snippet - Custom Step Configuration * - Group / Usage Type - Group 2 * **Phone** using the following configuration: .. list-table:: * - Object Name - Phone * - Snippet Argument - .. code-block:: yaml low_code: id: Phone version: 2 steps: - http: uri: "" - json - simple_key: "phone" * - Class Type - Config Character * - Snippet - Custom Step Configuration * - Group / Usage Type - Group 2 * **User** using the following configuration: .. list-table:: * - Object Name - User * - Snippet Argument - .. code-block:: yaml low_code: id: User version: 2 steps: - http: uri: "" - json - simple_key: "user" * - Class Type - Config Character * - Snippet - Custom Step Configuration * - Group / Usage Type - Group 2 Align this application to the *Accounts Discovery* Dynamic Application, by selecting the Component Mapping checkbox in the Properties tab, and selecting the Dynamic Application in Component tab. .. image:: _static/account_properties_comp_map.png .. image:: _static/align_config.png Create a new credential by navigating to the following location: .. code-block:: none Manage > Credentials Actions > Create New > Create Rest snippet framework Credential .. image:: _static/credential_basic.png Select the ``Basic Authentication`` as the **Authentication Type** and complete the required server fields: **URL**, **Username**, and **Password**. Select ``[True]`` or ``[False]`` for the **SSL Peer Verify** and **Logging Debug** fields, and configure the **Proxy Settings** if needed. Next, create a virtual device and align the Dynamic Applications to start the collection. The creation of a virtual device happens at the Device Manager, located at: .. code-block:: none Devices > Device Manager Actions > Create Virtual Device Align the Dynamic Application *Accounts Discovery* to the device using the just created credential. Once the data is collected, you will have a similar DCM tree like the one shown below. .. image:: _static/account_dcm.png .. _custom-da: ******************************************************************** Creating Dynamic Applications with Custom |STEP_NAME_PLURAL_CAPITAL| ******************************************************************** The |FRAMEWORK_NAME| allows you to create and define your |STEP_NAME_PLURAL| (refer to the :ref:`Creating a Custom Step ` document). In this section, you will learn how to create your |STEP_NAME_PLURAL|. In the example below, we will create a performance application that gets information about existing accounts of an SL1 server by accessing the following SL1 API endpoint: .. code-block:: none /api/account/ .. image:: _static/account_info.png For the creation of the following Dynamic Applications, we will use the existing |STEP_NAME_PLURAL| of the framework: * *low_code* Syntax to process the Snippet Argument to be consumable by the |FRAMEWORK_NAME| * *http* Requestor to gather API data from SL1 * *json* Processor to convert the API response into a Python dictionary And you need to define a new |STEP_NAME|: * The *Random Number Generator Based On Arg* as a Processor. This |STEP_NAME| will generate a Random number between 0 and the size/length of a provided argument. Performance Dynamic Application Creation ======================================== First, create a "Snippet Performance" Dynamic Application called *Account Performance*. The creation of Dynamic Application happens in the Dynamic Application Manager, located at: .. code-block:: none System > Manage > Dynamic Applications Actions > Create New Dynamic Application You need to set the required configuration of this Dynamic Application. .. list-table:: * - Application Name - Account Performance * - Application Type - Snippet Performance * - Execution Environment - REST: Toolkit |VERSION| * - Version Number - Version 1.0 * - Poll Frequency - Every 5 Minutes .. image:: _static/account_performance_properties.png In the Snippet tab, initially, we will use the same (:ref:`default snippet`). Save the snippet with the name *Custom Step Performance*. This time, we define a new |STEP_NAME| in the "User Editable" area of the snippet. This |STEP_NAME| is a new selector. Create a method named ``random_generator`` that generates a random number between 0 and the size/length of the argument that gets, so that this method allows you to simulate performance data. .. code-block:: python # ===================================== # =========== User Editable =========== # ===================================== import random @register_processor @typechecked(input=dict, output=int) def random_generator(result, action_arg): """Generates random number between 0 and action_arg value size. :param object result: Result from the parser :param int action_arg: Action argument for the step, in this case, the simple_key specified to get data using it. :return: Random number between 0 and action_arg value size :rtype: Integer """ attribute_leng = len(result[action_arg]) generated_number = random.randint(0, attribute_leng) return generated_number # List any custom substitutions that need to occur within the snippet arguments custom_substitution = {} # Custom functions that need to get executed during the framework # @register_processor # def my_func: # pass # ===================================== # ========= End User Editable ========= # ===================================== When creating this Dynamic Application, the ID of the account in the URL should be substituted by the Id of the discovery application previously created (URI of the account). In the Collections tab, define the following collection objects: * **Based on Aligned Ticket Queues** using the following configuration: .. list-table:: * - Object Name - Based on Aligned Ticket Queues * - Snippet Argument - .. code-block:: yaml low_code: id: TicketQueues version: 2 steps: - http: uri: "" - json - random_generator: "aligned_ticket_queues" * - Class Type - Performance Gauge * - Snippet - Custom Step Performance * - Group / Usage Type - No Group * **Based on Email** using the following configuration: .. list-table:: * - Object Name - Based on Email * - Snippet Argument - .. code-block:: yaml low_code: id: Email version: 2 steps: - http: uri: "" - json - random_generator: "email" * - Class Type - Performance Gauge * - Snippet - Custom Step Performance * - Group / Usage Type - No Group As you can see in these collection objects, the |STEP_NAME| you just defined generates two random numbers. The first one, between 0 and the size of tickets in queues, and the second number, between 0 and the length of the account's email address. Align this application to the "Accounts Discovery" Dynamic Application. .. image:: _static/align_performance.png After SL1 collects the data, you will have simulated performance information like the one shown below: .. image:: _static/account_performance_email.png Another Example of the Creation of a Custom |STEP_NAME_CAPITAL| ==================================================================== Next, you have a second example of the creation of a custom |STEP_NAME|. For this, we modify the snippet code in the *Account Configuration* Dynamic Application. We define a custom |STEP_NAME| in the "User Editable" area. This |STEP_NAME| is a new selector. Create a method named ``index_selector``. This method gets a list and returns a dictionary with elements of the list as keys and either the elements of the list as values (if the word "key" is provided) or just the last part of each element of the list after the ``/`` character (if the word "value" is provided). .. code-block:: python # ===================================== # =========== User Editable =========== # ===================================== from typing import Any, Iterable @register_processor @typechecked(input=Iterable[Any], output=dict) def index_selector(result, action_arg): """Store data based on the action_arg, if it's going to be keys, or values. :param object result: Result from the parser :param int action_arg: Action argument for the step :return: Selected value from result :rtype: object """ if isinstance(result, list): if (action_arg=="key"): result = {item: item for item in result} if (action_arg=="value"): result = {item: item.split("/")[-1] for item in result} return result # List any custom substitutions that need to occur within the snippet arguments custom_substitution = {} # Custom functions that need to get executed during the framework # @register_processor # def my_func: # pass # ===================================== # ========= End User Editable ========= # ===================================== In the Collections tab, add the following collection objects. **GROUP 3** * **Indexed Data example** using the following configuration: .. list-table:: * - Object Name - Indexed Data example * - Class Type - Label (Config Group) * - Snippet - Custom Step Configuration * - Group / Usage Type - Group 3 * **Permission Keys key** using the following configuration: .. list-table:: * - Object Name - Permission Keys key * - Snippet Argument - .. code-block:: yaml low_code: id: key version: 2 steps: - http: uri: "" - json - simple_key: "permission_keys" - index_selector: "key" * - Class Type - Config Character * - Snippet - Custom Step Configuration * - Group / Usage Type - Group 3 * **Permission Keys value** using the following configuration: .. list-table:: * - Object Name - Permission Keys value * - Snippet Argument - .. code-block:: yaml low_code: id: value version: 2 steps: - http: uri: "" - json - simple_key: "permission_keys" - index_selector: "value" * - Class Type - Config Character * - Snippet - Custom Step Configuration * - Group / Usage Type - Group 3 After SL1 collects the data you will have configuration information like the one shown below: .. image:: _static/account_config_data.png