-
Notifications
You must be signed in to change notification settings - Fork 14
Developing plugins for Expedient
Home > Developing plugins for Expedient
This manual is intended for AM developers that want to integrate the data returned by the Aggregate with the Expedient GUI.
The plugin system is a way to ease development of new Aggregate Managers and its interaction with the GUI (Expedient).
- Aggregate Manager (AM): manages resources of a given kind
- Plugin: communicates with the AM to retrieve resources according to some logic
Therefore, a developer may write a plugin to see the AM resources within the Expedient topology (the slice detail page).
The communication between Expedient, the plugins and their corresponding AM is as follows:
------------------------------------------------------ <- [3] translates node and link structures
| Expedient | to show topology
------------------------------------------------------
| | | <----------- [1] calls to each AM plugin method
v v v (get_ui_data) to retrieve data
--------------- --------------- ------------------
| plugin <vt> | | plugin <of> | | plugin <yours> |
--------------- --------------- ------------------
^ ^ ^
| | | <----------- [2] each AM returns nodes and links
------------ ------------ ------------- through its internal implementation
| VT AM | | OF AM | | your AM |
------------ ------------ -------------
The plugin system was first released along with OCF 0.5.
Folder must be placed under /opt/ofelia/expedient/src/python/plugins/
The name of this folder will be used to designate the plugin.
Plugins follow a minimal folder structure that allows the PluginLoader to retrieve the data
|-- <plugin_name> Root folder
| |-- settings.conf Configuration file
| `-- urls.py URLs file (as in any Django package)
The settings file contains a number of settings to be read from Expedient that you will have to configure to your own needs. You may also add new settings and use these within your plugin.
An explanation of each setting follows:
[urls]
BASIC_AUTH_URLS: URLs that need basic authorization to be accessed
[paths]
CSS_DIRS: relative path to folder where CSS files are stored within your plugin
IMG_DIRS: relative path to folder where images are stored within your plugin
JS_DIRS: relative path to folder where JS files are stored within your plugin
TEMPLATE_DIRS: relative path to folder where HTML files are stored within your plugin
TEMPLATE_RESOURCES: relative path to the file that allows to add resources to the slice.
This will be shown as a collapsible container in the slice details page
[general]
AGGREGATE_PLUGINS: (list of) 3-tuple(s) where:
1st: model for the resource aggregate. This defines the fields in the
"Add aggregate" form in the initial view of Expedient
2nd: name of the plugin
3rd: location of the URLs file (not a relative path; use
<plugin_name>.urls for example)
INSTALLED_APPS: (list of) Django app(s) to be activated. You should set -at least- your app
name
GET_UI_DATA_LOCATION: defines the file where the method "get_ui_data(slice)" is defined
RESOURCE_TYPE: kind of resource, currently identified by {"network", "computation"}. This
is useful to distinguish diferent resources and group these accordingly in
the slice details page (e.g. virtualization servers and sensors will be
under the "computation" category, while switches will fall under "network")
Each plugin has to implement the method "get_ui_data(slice)" and "link" the file where it is contained through the settings.conf file.
This method processes the data and return a dictionary with (at least) the keys "nodes" and "links". The values for those keys are a list of Node's and a list of Link's, respectively.
def get_ui_data(slice):
return {"nodes": [Node(...), ...], "links": [Link(...), ...]}
Node and Link are extendable structures that contain a basic skeleton that must be filled:
Node
name: node name
value: node (unique) identifier
description: html-formatted data to be shown in a tooltip when hovering the node in the topology
type: type of resource (e.g. "OpenFlow switch", "Sensor device"). Any text you like
image: path to the icon used to show the resource node in the topology. You can get the path in
this way: reverse('img_media_<plugin_name>', args=("<file_name>",))
This path is the one defined in the IMG_DIRS setting for your plugin
Link
source: source node ID
target: target node ID
value: string with a specific format depending on:
* "simple" nodes connected directly to other (e.g. switches, sensors):
"rsc_id_<x>-rsc_id_<y>" where:
- {x,y} = node IDs used in Django
* "complex" nodes (e.g. servers) connected through a given interface to a "simple" node:
"rsc_id_<x>-<y>:<z>" where:
- x = "simple" target port ID assigned to Django resources (e.g. 355)
- y = interface name (e.g. "eth1")
- z = "simple" target port number (e.g. 65534)
You may pass more arguments to each node and link if you want some extra data to be used in your plugin templates:
Node(
name = "Bodhisattva",
value = "251",
description = "<strong>Sensor: Bodhisattva</strong>
<strong>Connections</strong><ul><li>Sensor 249</li><li>Sensor 252</li><li>Sensor 253</li></ul>",
type = "Sensor resource",
image = reverse('img_media_sample_resource', args=("sensor-icon.png",)),
connections = [249, 252, 253]
)
Link(
source = "251",
target = "252",
value = "rsc_id_251-rsc_id_252"
)
Fill this as in every URLs file for a Django package: urls to create/edit/delete both aggregate and resources, etc.
You can add any other file and logic to your plugin. Just keep in mind to fill the gaps as stated to enable the communication. Everything else is up to you :)
- The templates for every plugin are tried in order from the paths defined in the Django setting
TEMPLATE_DIRS
, so if two templates with the same name exist in different plugins only the one in the first path will be loaded. To avoid this and to establish a common style, prepend your plugin name to every template (e.g. plugin "sample_resource" with template "add_resources.html" would be renamed to "sample_resource_add_resources.html") - The monitoring of each aggregate manager' status is done by retrieving its address from the aggregate model (defined at the time of its creation). This requires at least the
url
field from the xmlrpcServerProxy model to be present
- Improve plugin setting loading (plugin settings should be accessible at any time)
- Integrate with [Theme Manager](Theme manager) to allow different templates
- Establish a generic format for links between nodes - without differentiating between "simple" (OF switches) and "complex" (servers) nodes (see [Link explanation](Developing plugins for Expedient#method-get_ui_dataslice))
-
Install dependencies
apt-get install python-lxml
-
Move the code from
/opt/ofelia/expedient/doc/plugins/samples/
as following: -
Move plugin (
plugin/sample_resource
) under/opt/ofelia/expedient/src/python/plugins/
-
Move AM (
aggregate/sr_manager
) under/opt/ofelia/
-
Synchronize database:
cd /opt/ofelia/expedient/src/python/expedient/clearinghouse python manage.py syncdb
Output should be similar to:
Creating tables ... Creating table sample_resource_sampleresource_connections Creating table sample_resource_sampleresource Creating table sample_resource_sampleresourceaggregate Creating table sample_resource_xmlrpcserverproxy Installing custom SQL ... Installing indexes ... No fixtures found.
-
Restart Apache
-
Configure the SR AM if you need it (defaults are fine, though) in the file
/opt/ofelia/sr_manager/src/settings.py
. -
Go to the SR AM folder and start the server:
cd /opt/ofelia/sr_manager/src/ python server.py
Note: if by any chance you do use Python2.7 and find that your server crashed along with an error like
TypeError: shutdown() takes exactly 0 arguments (1 given)
, the werkzeug library needs to be fixed (see AMsoil wiki).Go into
/usr/lib/python2.7/SocketServer.py
and add the following below line 461:except TypeError: # << add this pass # << add this
Resulting method will be as follows:
def shutdown_request(self, request): """Called to shutdown and close an individual request.""" try: #explicitly shutdown. socket.close() merely releases #the socket and waits for GC to perform the actual close. request.shutdown(socket.SHUT_WR) except socket.error: pass #some platforms may raise ENOTCONN here except TypeError: # << add this pass # << add this self.close_request(request)
-
Create an aggregate manager with type
SampleResource
in Expedient. E.g.:Name: SR AM Description: SampleResource Aggregate Manager Geographic Location: Barcelona Sync resources?: x User: user # Default value Password: password # Default value Server URL: https://<IP_OR_DOMAIN>:9445 # Default port
-
Add this AM to a project and then to some slice within. You should find now five "sensors" conforming a pentagon-shaped topology
-
Delete any AM of the
SampleResource
type (via the Expedient welcome page, in the AM list). This shall delete every AM and also any SampleResource associated to it -
Stop the SR AM server
-
If you do not want to completely remove data for this plugin/application jump this step.
Otherwise type the following:
cd /opt/ofelia/expedient/src/python/expedient/clearinghouse python manage.py sqlclear sample_resource
and execute the code returned inside your MySQL engine to drop the corresponding tables.
cd /opt/ofelia/expedient/src/python/expedient/clearinghouse python manage.py dbshell mysql> (paste previous SQL code here)
-
Move the code back into their corresponding folders, under
/opt/ofelia/expedient/doc/plugins/samples/
-
Restart Apache
Note: if you do not follow the previous steps before moving the sample plugin code you may find that the AM list in the Expedient welcome page is not loading. If so, place the code back in the plugin folder and follow these steps.
You might still have the old plugins (openflow
, vt_plugin
) under /opt/ofelia/expedient/src/python
. Backup these into another directory or delete them, as you wish. Then reload Apache and try again.
There is an Aggregate Manager that couldn't be removed from the MySQL database. Enter your MySQL engine and type the following (or look for it yourself) in order to delete the stale Aggregate Manager:
use expedient;
select id from aggregate_aggregate where leaf_name = 'SampleResourceAggregate';
delete from aggregate_aggregate where id = <previous_id>; # Type here the ID obtained previously
This may happen because you installed the version from the ofelia.stable
branch and your Expedient database contains some stalled tables that were later modified. You should delete your old tables structure and synchronize again. For that:
- Follow step #3 only from the disable section
- Follow step #4 and onwards from the enable section
- Overview
- Experimenting
-
Administering
- Installing
- Upgrading
-
Configuration
- Components
- Infrastructure
- Troubleshooting
- Theme manager
-
Contributing
- Developing
-
Reporting
- Issue tracker and Roadmap