Extension Structure
===================

The basic bare-bone structure of an extension is as follows:

.. code:: bash

    my_extension
    ├── README.md
    ├── activation.json
    ├── extension
    │   ├── activationSchema.json
    │   └── extension.yaml
    ├── my_extension
    │   ├── __init__.py
    │   └── __main__.py
    └── setup.py

This structure can be generated by running the :doc:`/cli/create` command
``dt-sdk create my_extension``.

Extension Definition
--------------------

``extension.yaml``

This is the main configuration file of the extension. It contains information
about the extension name, version, and so on.

Here is what a sample extension definition looks like:

.. code:: yaml

    name: custom:my-extension
    version: 0.0.1
    minDynatraceVersion: "1.285"
    author:
      name: "Dynatrace"

    python:
      runtime:
        module: my_extension
        version:
          min: "3.10"

      activation:
        remote:
          path: activationSchema.json
        local:
          path: activationSchema.json

Extension Name
^^^^^^^^^^^^^^

``name:``

All extensions must have a unique name. The name must be in the ``custom:`` namespace,
when it is a custom extension.

.. admonition:: Official extensions
   :class: warning

   Only Dynatrace can create extensions in the ``com.dynatrace.extension`` namespace.
   These extensions are signed by Dynatrace and are considered official.

Extension Version
^^^^^^^^^^^^^^^^^

``version:``

The version field must be bumped every time the extension is uploaded to the environment.
Otherwise an error will be thrown. The environment does not accept the same version twice.
Each uploaded version must always be unique for the audit tracking purposes.

Minimal Dynatrace Version
^^^^^^^^^^^^^^^^^^^^^^^^^

``minDynatraceVersion:``

The minDynatraceVersion field specifies the minimal Dynatrace version that is required
to run the extension. If the ActiveGate or OneAgent version is lower than the
specified one, then the extension will not be activated on that agent.

This is useful when the extension uses some of the new features that are not yet
available in the older versions of Dynatrace environment or extension execution controller.

Python
^^^^^^

``python:``

This is a specification of datasource that Extension Framework understands. There are
different datasources that can be used to collect data from the multitude of sources.

.. admonition:: Datasources
   :class: seealso

   Other datasources include `snmp`_, `jmx`_, `prometheus`_, `sql`_, `wmi`_.

They can't be used within the same extension, but all EF2.0 extensions follow the same
format, where there is the ``extension.yaml`` file, which contains the required
definition of the datasource.

Python Runtime
^^^^^^^^^^^^^^

``runtime:``

Here we can specify which minimal version of Python is required to run the extension.
This does not play a big role, since the version of Python is determined by what
is received from ActiveGate or OneAgent. This field is mandatory.

.. admonition:: Don't bring your own Python
   :class: warning

   Custom Python runtime is not supported. ActiveGate and OneAgent supply the Python
   interpreter for the extension to be able to run. It is impossible to specify a
   custom Python interpreter or use one that is installed on the system.

The ``module`` field specifies the name of the main Python module that contains the
extension logic. This module must be importable by the Python interpreter, thus it
must always contain the ``__init__.py`` file to be considered a valid Python module.

Datasource Activation
^^^^^^^^^^^^^^^^^^^^^

``activation:``

This is a specification of the datasource activation. It might contain ``local:`` and
``remote:`` sections or only one of them.

* ``local:`` means the extension can run on OneAgent. When you deploy the extension,
  the environment will automatically read this value and will understand that an active
  OneAgent is required to run this extension.
* ``remote:`` means the extension can run on ActiveGate. When you deploy the extension,
  the environment will automatically read this value and will understand that an active
  ActiveGate is required to run this extension.

The ``path:`` within the activation section contains a path to the configuration
that specifies a full definition of the monitoring configuration. What fields must be
there? Of what type? Does it contain lists? What are the possible values? All of these
can be describied in the ``activationSchema.json`` file.

Activation Schema
-----------------

``activationSchema.json``

This is a schema file that describes the structure of the monitoring configuration.

For the purposes of giving an example, here is a very simple schema with a single
field that will run on ActiveGate. The schema generated by the :doc:`/cli/create`
command is much larger and contains more fields, passwords, list items, and so on.

.. code:: json

    {
        "types": {
            "pythonRemote": {
                "type": "object",
                "properties": {
                    "host": {
                    "displayName": "Host",
                    "type": "text",
                    "nullable": false,
                    "default": ""
                    }
                }
            }
        },
        "dynatrace": "1",
        "description": "Extension configuration",
        "schemaId": "python-extension.activation",
        "displayName": "Extension configuration",
        "ownerProductManagement": "Albus Dumbledore",
        "ownerDevelopment": "Peter Parker",
        "maturity": "GENERAL_AVAILABILITY",
        "allowedScopes": ["environment"],
        "multiObject": false,
        "properties": {
            "pythonRemote": {
            "displayName": "Python Remote Extension",
                "type": {
                    "$ref": "#/types/pythonRemote"
                }
            }
        }
    }


Activation Config
-----------------

``activation.json``

This is a config file that can be used for local testing, when extension instance
is launched using the :doc:`/cli/run` command. It must contain all of the mandatory
fields as defined in the `Activation Schema`_.

When extension is deployed to the Dynatrace environment and monitoring configuration
is created, then the environment provides an individual activation config for each
instance of the extension.

Here is what a sample activation config looks like:

.. code:: json

    {
        "enabled": true,
        "description": "my_extension activation",
        "version": "0.0.1",
        "activationContext": "REMOTE",
        "pythonRemote": {
            "endpoints": [
                {
                    "url": "http://127.0.0.1:15672",
                    "user": "guest",
                    "password": "guest"
                }
            ]
        }
    }

Setup.py
--------

``setup.py``

This is a standard Python setup file that is used to package the extension and
is used by the extension execution controller to install the extension on the
ActiveGate or OneAgent along with the required dependencies.

.. code:: python

    from setuptools import setup, find_packages

    setup(
        name="my_extension",
        version="0.0.1",
        description="My_extension python EF2 extension",
        author="Dynatrace",
        packages=find_packages(),
        python_requires=">=3.10",
        include_package_data=True,
        install_requires=["dt-extensions-sdk"],
        extras_require={"dev": ["dt-extensions-sdk[cli]"]},
    )

.. admonition:: Dependencies
   :class: important 

   The ``setup.py`` file is the place where any dependencies must be specified
   in the ``install_requires`` section.
   
   The ``dt-extensions-sdk`` package must always be specified as a dependency. However,
   theoretically, it is possible to write a Python code that will be capable of
   communication with the extension execution controller without using the SDK.

   We do not recommend doing that, since the SDK provides everything that is needed
   and getting rid of it is an equivalent of reinventing the wheel.

.. admonition:: Bumping version
   :class: important

   When bumping the version of the extension in the ``extension.yaml``,
   the ``setup.py`` file must be updated as well. The two versions must match.

.. admonition:: Extension size
   :class: danger

   Be careful when adding dependencies to the extension. The size of the extension
   must not exceed 15MB. If it does, then the extension will not be accepted by
   the environment.

   When building the extension using the :doc:`/cli/build` command, the dependencies
   can be downloaded for all target platforms (Linux, Windows) and the size of the
   dependencies for some of them might be quite large. 

.. _snmp: https://docs.dynatrace.com/docs/extend-dynatrace/extensions20/data-sources/snmp-extensions
.. _jmx: https://docs.dynatrace.com/docs/extend-dynatrace/extensions20/data-sources/jmx
.. _prometheus: https://docs.dynatrace.com/docs/extend-dynatrace/extensions20/data-sources/prometheus-extensions
.. _sql: https://docs.dynatrace.com/docs/extend-dynatrace/extensions20/data-sources/sql
.. _wmi: https://docs.dynatrace.com/docs/extend-dynatrace/extensions20/data-sources/wmi-extensions
