Floodlight uses a home grown module system in order to decide what will be run. The goals of this system are as follows.
- Define what modules are going to be loaded by modifying a configuration file
- Swap out implementations of modules without modifying modules that depend upon them
- Create a well defined platform and API to extend Floodlight
- Enforce code modularity
The module system consists of the a few main parts. The module loader, modules, services, a configuration file, and a file that contains the list of modules available in a jar.
A module is defined as a class that implements the IFloodlightModule interface.
A module may export one ore more services. A service is defined as an interface that extends the IFloodlightService interface.
Currently, this interface is blank. It is used to enforce type safety in our loading system.
The configuration file specifies which modules to explicitly load. The format is standard Java properties. It uses a key value pair. For the module list we use the key "floodlight.modules". The value is a comma separated list on modules either on one line or using the \ delimiter. Here is the default configuration for Floodlight.
Notice that there are many modules that are loaded in this configuration are not listed here. This is because the module system automatically loads the dependencies. If a module does not provide a service it must be explicitly specified here.
To find the modules in the classpath we use Java's ServiceLoader. This requires us to list all of the classes in a file. The format of the file is the fully qualified name of the module on it's own line. The file is located in src/main/resources/META-INFO/services/net.floodlightcontroller.module.IFloodlightModule. This is a sample file.
All modules (classes that implement IFloodlightModule) in the classpath are found and we create 3 maps.
- The Service Map - Maps a service (i.e. IStorageSourceService) to the module(s) that provide it.
- The Module Service Map - Maps a module (i.e. MemoryStorageSource) to all the service(s) it provides (i.e. IStorageSourceService)
- The Module Name Map - Maps a string name to the module class
The Depth First Search (DFS) algorithm is used to find the minimal set of modules that need to be loaded. All of the modules specified in the configuration file are added to the queue. As each module is dequeued it is added to the list of modules to be started. getModuleDependencies() is also called on the module. If it's module dependencies have not been added to the list of modules to be started they are done here. A FloodlightModuleException can be thrown here for two reasons. First is if a module or it's dependency has been specified in the configuration file but was not found. The second is if two modules provide the same service and which one to use was not specified in the configuration file.
The set of modules to be loaded are iterated over and init() is called on each of them. Here the module is supposed to do two things.
- Wire together it's dependencies (i.e. IStorageSourceService) using the getServiceImpl() call on the FloodlightModuleContext.
- Perform internal initialization of it's own data structures.
The order in which init() is called is not deterministic.
After init() has been called on each module startUp() is then called. In this call modules are allowed to perform calls that depend upon other modules. An example would be creating database tables using the IStorageSourceService module or starting worker threads using IFloodlightProviderService's executor service.
If you want to run Floodlight in it's default configuration, the easiest way is to just run it via this command line.
It is also possible to run Floodlight using multiple jars. This is useful if you want to offer another package for distribution. The command to run it is slightly different.
-cp tells Java what jar files to use in the classpath. The net.floodlightcontroller.core.Mains specifies where the main method to start the application is.
With both of the methods you can also specify a different configuration file. This is used using the -cf option.
The -cf must come after all the Java options are specified. This makes Java pass the argument to the program instead of the JVM. The ordering sequence of which configuration file to use is
- The file specified by using the -cf option
- The config/floodlight.properties file if it exists
- The floodlightdefault.properties file built into the jar (under src/main/resources)
The properties file can also specify per module configuration options. The format of the parameters is <fully qualified module name>.<config option name> = <value>. We use the fully qualified module name so any module can create configuration options specific to it's own implementation.
For example if we want to specify the REST API port in the configuration file we add this to the property file we are using.
In RestApiServer.java's init() method we parse the option.
Note that the null check is required. If no configuration option is provided the FloodlightModuleLoader will not add it to the context.
Options can also be specified via the command line as Java properties. These will override anything specified in a Floodlight properties file.
Two these to note here. First is that any Java properties must be specified before -jar floodlight.jar. Anything after this will be passed to the Java executable as a command line argument. The second is that there are no spaces with the -D option.
- In order to deal with circular dependencies the order in which init() and startUp() are called are not deterministic. Therefore you can not assume any other module's init or startUp methods have been called before yours.
- Your configuration file can not be called floodlightdefault.properties. This is the default file included in the jar.
- Each module must have a 0 argument (and preferably empty) constructor. Anything normally done in the constructor should be done instead in the init() call.
- Multiple modules may not have service overlap, but functional overlap. For example both LearningSwitch and Forwarding are a way of forwarding packets as a response to PacketIns. Since they do not provide a common service we do not detect and overlap.