-
Notifications
You must be signed in to change notification settings - Fork 0
Plugin
Most core and custom functions of eiSoil are encapsulated in plugins. Each plugin may provide services, which encapsulate the actual functionality.
Advantages in using plugins are:
- Selection: A developer/administrator can pick certain plugins he/she wants to use for the test bed without to enable all plugins (easer on resource usage).
- Exchangability: Provide a full set of plugins for developers. But for some domains developers can choose to re-implement domain-specific plugins.
- Clarity: Provide a set of services and interfaces and hide the details behind the plugin.
- Encapsulation: Protect implementations from other developers.
A plugin consists of the following three things:
- A
MANIFEST.jsonfile, specifying the services and dependencies of the plugin - A
plugin.pyfile which contains asetup()method for initialization and registering services - The actual implementation of the plugin
Example for a MANIFEST.json:
{
"name" : "RPC Registry",
"author" : "Tom Rothe",
"author-email" : "tom.rothe@eict.de",
"version" : 1,
"implements" : ["rpcserver", "xmlrpc"],
"loads-after" : ["config"],
"requires" : []
}Example for a plugin.py:
# ...imports...
def setup():
# setup config using the config service
config = pm.getService("config")
config.install("myplugin.config_key", "defaultvalue", "Description")
# register a service
pm.registerService('myclass', ServiceClass)
pm.registerService('myinstance', SingleClass() )
pm.registerService('mypackage', my.python.package)Each plugin can offer, none, one or multiple services. A service can be everything from a string over a dict, an object or a class. The pluginmanager holds a registry of services. Each of the registered services can be acquired by other plugins.
To use another plugin's service in your plugin, add the name of the required service in the manifest file.
Use either loads-after if the setup method (or any import in the plugin.py module) requires the service.
Or use requires if the plugin does only need the service until after initialization.
Now, get a reference to the service and call methods which are published:
import eisoil.core.pluginmanager as pm
myservice = pm.getService('myservice')
myervice.do_something(123)Methods which can be used from a plugin's service are marked with the decorator @serviceinterface from eisoil.core.
Example (do_something is intended to be used from plugin users, do_more is not):
class MyService(object):
@serviceinterface
def do_something(self, param):
pass
def do_more(self, param):
passIf you are writing your own plugins, you may bind (registerService(...)) basically everything to a service name. This may be an object, class, dictionary or even a module (you get the drift).
So just create a new folder in plugins, create the manifest (be sure to put all services you implement in the implements section), plugin.py and the implementation.
Then all you need is to call registerService(...) in your setup method and annotate the things you want to be used with @serviceinterface.
Note
There is no need to add a __init__.py file to the root folder of the plugin. The plugin manager will assume the plugin's folder as the root folder.
Still, you need to add init-files for sub-folders to enable them as packages. If you create a file in the plugin's root, make sure it does not have the same name as the plugin.
Good practices
- Never import another plugin directly, always go via the
pluginmanagerandpm.GetService(). - Setup your config defaults in the setup method (see Configuration).
- If you have plugin-specific exceptions, create a package with all exceptions and register the package as a service (e.g. done in the GENI v3 RPC plugin).
- Seperate a plugin into multiple plugins if this improves re-usability (e.g. a GENI v3 Delegate and the Resource Manager should be two plugins).
Bootstrap
When you do stuff when the import is performed by Python, make sure you import those packages in the setup method (not on the top of the file plugin.py). Example:
mypack.py:
import eisoil.core.pluginmanager as pm
do_something(pm.getService('config').get('mypack.xxx')) # this is executed when importing this package
class MyPack():
# ....plugin.py:
# does NOT work when the user has no config database yet
import mypack
def setup():
# setup stuff
# this is the way to go
def setup():
import mypack
# setup stuffWhenever you create your own plugins, please create the new plugin in the plugins folder. When you want to use/remove an existing plugin (shipped with eiSoil) create/remove the symlink to the vendors folder in the plugins folder.
As a rule of thumb, if you change code in the vendors folder, there is something wrong.
A good start to create your first AM is to remove the dhcprm and dhcpgeni3 symlinks in the plugins folder and then start creating your own.
Please also consider the Plugins section on the Documentation page.