Plugin System¶
genie.telemetry
is designed around a modular plugin-based architecture.
The end goal is to allow maximum developer configurability & extendability
without sacrificing overall structure, flow and code-base integrity.
Note
Genie Telemetry plugins are not for the faint of heart: it is intended for
advanced developers to provide optional pluggable genie.telemetry
features for other developers to use.
Concept & Rules¶
Plugins offers optional functionalities that may be added to
genie.telemetry
.
Each plugin must be configured first via a configuration YAML file before they
can be loaded, instantiated and executed.
All plugins must obey the following rules of development:
plugins may be configured locally (for this run only) by passing in a config YAML via a command-line argument called
-configuration
(when used with command line executable).plugins shall be independent from all other plugins.
plugins must inherit from
genie.telemetry.plugin.BasePlugin
classplugins may contain its own argument parser which standardizes on double-dash
--
style arguments, and shall not contain positional arguments.plugin developers are expected to read and understand
genie.telemetry
plugin template. It is mostly not possible to develop useful plugins by simply reading this document.
There is only one stage where plugins may run its actions.
Stage |
Description |
---|---|
|
core plugin execution logic |
Plugins are run in the designated interval in the configuration yaml file.
Creating Plugins¶
To create a plugin, simply subclass genie.telemetry.plugin.BasePlugin
class and define the stages where your plugin needs to run.
# Example
# --------
#
# hello-world plugin
import logging
import argparse
import datetime
from genie.telemetry.plugin import BasePlugin
logger = logging.getLogger(__name__)
class Plugin(BasePlugin):
'''HelloWorld Plugin
Saluting the world and printing the device name and runtime if a custom
flag is used.
'''
# each plugin may have a unique name
# set it by setting the 'name' class variable.
# (defaults to the current class name)
__plugin_name__ = 'HelloWorld'
# each plugin may have a version
# set the plugin version by setting the 'version' class variable.
# (defaults to 1.0.0)
__version__ = '1.0.0'
# each plugin may have a list of supported os
# set the plugin supported os by setting the 'token' class variable.
# (defaults to [])
__supported_os__ = ['nxos', 'iosxr', 'iosxe']
# each plugin may have a parser to parse its own command line arguments.
# these parsers are invoked automatically by the parser engine during
# easypy startup. (always use add_help=False)
parser = argparse.ArgumentParser(add_help = False)
# always create a plugin's own parser group
# and add arguments to that group instead
hello_world_grp = parser.add_argument_group('Hello World')
# custom arguments shall always use -- as prefix
# positional custom arguments are NOT allowed.
hello_world_grp.add_argument('--print_timestamp',
action = 'store_true',
default = False)
# plugins may define its own class constructor __init__, though, it
# must respect the parent __init__, so super() needs to be called.
# any additional arguments defined in the plugin config file would be
# passed to here as keyword arguments
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# define your plugin's core execution logic as method.
# define the execution action
# the current device object is provided as input to this action method
# when called.
def execution(self, device):
# plugin parser results are always stored as 'self.args'
if self.args.print_timestamp:
self.execution_start = datetime.datetime.now()
logger.info('Current time is: %s' % self.execution_start)
logger.info('Execution %s: Hello World!' % device.name)
Configuration YAML Schema¶
After defining a plugin class, it needs to be configured in order to run. The
genie.telemetry
plugin manager automatically reads plugin configurations
from the file path that’s provided with -configuration
parameter when
passed through command line executable.
# Example
# -------
#
# example genie telemetry configuration file for plugins
plugins: # top level key for plugins
HelloWorldPlugin: # this is the plugin name we defined
# enabled, module and order keys are
# mandatory. Any additional key/values are
# used as arguments to the plugin class
# constructor.
module: module.where.plugin.is.defined # module path where this
# plugin can be imported
interval: 30 # defines the interval of execution of
# plugins, in seconds only.
devices: [] # device filter list: if not defined, the
# plugin will be applied to all devices,
# otherwise, only the included devices will
# be applied.
And genie.telemetry
automatically discovers, loads your plugin, and runs its
actions as part of its standard execution stage.
Plugin Device Filter¶
By default, plugin will be applied to all devices within the testbed. User can fine tuning the devices filter by supplying a devices list in the configuration file. Monitoring plugin will only executed on devices that is in the inclusive list.
The following example indicates plugin HelloWorldPlugin only be executed on
device with name Tonystark-sjc
.
plugins:
HelloWorldPlugin:
module: module.where.plugin.is.defined
interval: 30
devices: [Tonystark-sjc]
Plugin Errors¶
Because plugins are a fundamental building block of genie.telemetry
, any
unhandled exceptions raised from plugin actions result in catastrophic failures:
make double sure that your plugin is well tested and robust against all
possible environments and outcomes.
By default, all plugin errors are automatically caught and handled which prevent the system from crashing.
Whenever plugins error out, the status of the execution will be ERRORED along with exception message in the result, your email report will contain the detailed exception in the log.
Plugin Meta Data¶
By default, plugin meta data is collected through HealthStatus.meta
,
which stores any python picklable value and display at notification.
Plugin Execution¶
Plugin Templates can be found in the template folder of genietelemetry_libs
Steps for executing your plugin:
Move your plugin package to any location that is accessible via PYTHONPATH
[tony@jarvis:template]$ cp -r plugin/ $VIRTUAL_ENV/projects/genietelemetry_libs/plugins/hello [tony@jarvis:template]$ ls -al $VIRTUAL_ENV/projects/genietelemetry_libs/plugins/hello total 24 drwxr-xr-x 3 tony eng 4096 Sep 30 23:50 . drwxr-xr-x 4 tony eng 4096 Sep 30 23:39 .. drwxr-xr-x 5 tony eng 4096 Sep 30 23:39 iosxe drwxr-xr-x 5 tony eng 4096 Sep 30 23:39 iosxr drwxr-xr-x 5 tony eng 4096 Sep 30 23:39 nxos -rw-r--r-- 1 tony eng 8273 Sep 30 23:50 plugin.py
Create your config.yaml file
plugins: plugin: interval: 30 module: genietelemetry_libs.plugins.hello
Execute genietelemetry for on-demand monitoring:
genietelemetry /path/to/testbed.yaml -configuration /path/to/config.yaml --print_timestamp false
You should see the following lines show up in the log.
Loading genie.telemetry Configuration
Loading genie.telemetry Plugins
Initializing genie.telemetry Plugins for Testbed Devices
Initializing plugins for Jarvis
- loading plugin plugin
Starting TimedManager ...
Setting up connection to device (Jarvis)
Abstraction Plugin Package¶
First make sure you have read pyATS abstraction, especially the section on Lookup Decorator as it is the root of abstraction in Genie Telemetry.
plugins
|-- __init__.py <-- Package declaration
|-- plugin.py <-- Base Plugin Structure file
|-- iosxe <-- Token
| |-- __init__.py <-- Token declaration
| `-- plugin.py <-- Plugin core logic implementation
|-- nxos <-- Token
| |-- __init__.py <-- Token declaration
| `-- plugin.py <-- Plugin core logic implementation
|-- iosxr <-- Token
| |-- __init__.py <-- Token declaration
| `-- plugin.py <-- Plugin core logic implementation