Dependency Management
Dependencies come in two basic forms, object/resource dependencies, and experiment state dependencies. Object and resource dependencies occur when a model component depends on importing model component objects or using VM resources or images contained in other model components. Experiment state dependencies occur when one model component must only execute before another finishes (e.g. when the first must instantiate an experiment property before the second can modify it) or requires another to run after it finishes. Dependency management is how FIREWHEEL determines which model components get loaded into it’s Python Virtual Environment (FWPY) and order in which those components are loaded and executed when an experiment is launched. This dependency system is akin to most package managers which will ensure that installing one package will recursively identify and install all new dependent packages. Therefore, when developing new model components, it’s only important to identify the direct dependency relationship for that component; the remainder of the necessary dependencies will be automatically resolved.
Model component’s dependency relationships are ordered via a directed acyclic graph (DAG) to ensure that a repeatable experiment environment is created. There are two methods for establishing relationships between model components.
1. A depends relationship indicates that a second MC must be installed and executed BEFORE another MC. For example, if
mc1
depends onmc2
, then the ordering in the dependency graph would bemc2-->mc1
.2. A precedes relationship indicates that a second MC must be installed and executed AFTER another MC. For example, if
mc1
precedesmc2
, then the ordering in the dependency graph would bemc1-->mc2
.
By explicitly specifying depends and precedes relationships, users can minimize the effort for building a repeatable experiment graph.
There are three conventions by which users can identify dependencies to FIREWHEEL:
Ordering of the model components listed on a
firewheel experiment
command line e.g.firewheel experiment mc1 mc2 mc3
.Using information in the model components’
attribute
block of the MANIFEST.Using information in the model components’
model_components
block of the MANIFEST.
This design enables multiple ways for achieving the same dependency graph combining each of these methods. Therefore, each of the three methods are discussed in-detail below to ensure full understanding of how to best use each. Additionally, some examples are provided to further clarify usage.
Model Component Ordering on the Command Line
To launch a FIREWHEEL experiment, you run FIREWHEEL from within a command shell using its experiment Helper i.e. firewheel experiment
.
Immediately following the experiment
Helper keyword, you then list the model components you want FIREWHEEL to run.
The order in which you list an experiment’s model components on the command line is guaranteed to be the order in which they are executed.
That is, when building the dependency graph, there are assumed directional edges between the MCs listed on the command line.
For example, take the command:
firewheel experiment tests.router_tree:5 tests.ping_all minimega.launch
In this case there are dependency relationships between the model components where tests.router_tree:5
must be executed before tests.ping_all
which must be executed before minimega.launch
.
Using command line dependencies is the most visible way to state the relationships between components.
However, when many model components are involved, it could become overwhelmingly complicated.
For example, if we were to list all dependencies required by the previous experiment, the command would look like:
firewheel experiment misc.blank_graph base_objects linux.base_objects generic_vm_objects tests.router_tree:5 minimega.emulated_entities minimega.testbed_available linux.ubuntu linux.ubuntu1604 vyos vyos.helium118 tests.ping_all minimega.create_mac_addresses minimega.resolve_vm_images minimega.configure_ips ping_default_gateway minimega.schedules_ready vm_resource.schedule vm_resource.validate minimega.parse_experiment_graph minimega.launch
Therefore, we recommend minimizing the use of command line ordering in favor of the two methods described below.
Attributes: Depends, Provides, and Precedes
Model component attributes can be thought of as commodities which can be provided by the model component and depended on or precedes other MCs.
The attributes field in a model component’s MANIFEST
is used to declare the names/labels of the attributes (if any) that a model component:
provides
for use by other model components
depends
on some other MC providing and must exist prior to the MC executing
precedes
(i.e. it depends on these attributes occurring AFTER this MC runs)
Attribute labels can be any descriptive string that describes a property that is provided by a given model component. These labels enable model components to provide, depend, and precede generic attributes about the experiment.
For example, a model component that creates a website will depend on a web server existing first. This website does not need to be run on a specific web server software (NGINX, Apache, etc.). Therefore, it can depend on a generic attribute like web_server. This attribute can then be provided by any model component which creates a web server. If however, a user would like to use a specific web server software, they can either have that MC provide a more specific label (e.g. nginx_web_server) OR explicitly depend on the model component that creates the NGINX server.
Attributes can also be used to precede the execution of a MC that provides a specific attribute.
For example, if a user creates a new topology, they may want to customize the hostnames after the topology is created.
The MC which changes these hostnames is agnostic to which topology it modifies.
Therefore, a user can have their topology precedes
the other MC and can be guaranteed that it will be executed after the topology.
Are Attribute Labels Considered Reserved Words?
Attribute labels are not reserved words – at least not in the sense of reserved words in programming languages.
In practice, however, only one model component per experiment can provide any given attribute, in one sense they can become like reserved words.
For instance, the misc.blank_graph
model component in the base
repository (included by default with FIREWHEEL), declares that it provides
graph in its MANIFEST
file.
Since the base
repository is available in every FIRWHEEL installation, and the graph attribute label is already declared as provided by it, and since arguably every experiment needs to depend on graph to define its network topology, then essentially graph acts like a reserved word among attribute labels.
(Unless, of course, you define another way of providing an instance of FIREWHEEL’s ExperimentGraph
to your model component.)
To discover attributes that are already provided/available you can use the FIREWHEEL mc list Helper:
firewheel mc list -g provides
Can multiple Model Components provide the same Attribute?
It is important to note that multiple Model Components can provide the same attribute. However, FIREWHEEL will be unable to automatically determine which to use during experiment creation. Therefore, there are two options:
You can add the preferred Model Component to the command line.
A default Model Component can be set using the attribute_defaults configuration option. Note that even if a default is set, it can be overwritten by applying a different one on the command line.
Special Attribute Labels
There are two special attribute labels. They’re special because they’re (likely) necessary for every experiment to consume and produce. They are graph and topology.
graph
The misc.blank_graph
model component, located in FIREWHEEL’s base
repository, provides
the graph attribute.
This attribute represents the FIREWHEEL Graph object that every experiment must add vertices and edges to when constructing their network topologies.
In other words, the object instance of the ExperimentGraph
class that misc.blank_graph
provides each experiment, and as modified by an experiment’s model components, is the definition of the experiment.
Everything about an experiment is stored in its instance of the experiment graph.
Since FIREWHEEL provides a blank graph to any model component that lists graph as a dependency, then only the very first model component that will modify the graph for an experiment should declare a dependency on graph.
topology
The topology attribute is also special in FIREWHEEL.
It must be provided by a model component in an experiment in order to complete the experiment.
This is because other model component’s in FIREWHEEL’s base
repository depends
on it, namely the minimega.launch
MC, which launches the experiment via minimega.
Model Components: Depends and Precedes
The model_components field in a MANIFEST
is also used to declare which model components (if any) a model component depends
on or precedes
.
The main difference, between the attributes
object and the model_components
object is that with with model_components
, the values use the exact name
of a model component (as declared in its MANIFEST
file’s name
field).
Whereas attributes
are generalized, model_components
are specific.
This is useful when a user wants specificity when selecting a type of MC that provides a general attribute (e.g. choosing an Apache web server over an NGINX one).
Additionally, using the model_components
depends
field is required when a user needs to import and use an Object, located in the named dependencies’ model_components_objects.py
file, in the dependent MCs plugin
.
Model components can also be explicitly required to run after the current model component by using the precedes
field.
One practical example is when you know that your experiment should be launched with minimega (using the minimega.launch
MC).
Users could either add minimega.launch
as an argument to firewheel experiment
or in their topology MANIFEST file, they can precedes
the minimega.launch
MC.
Examples
Typical Experiment
Let’s consider an example experiment. Suppose you’ve created an experiment consisting of a my_basic_topology
model component that defines a simple star LAN topology, which contains a switch that’s connected to six hosts.
host2 host3
\ /
host1 -- LAN_switch -- host4
/ \
host6 host5
Now, your my_basic_topology
model component will need to list the graph attribute label (which the misc.blank_graph
model component provides as an attributes
dependency in its MANIFEST
.
Additionally, let’s also say it provides basic_topology to other model components.
So its MANIFEST
would include:
attributes:
depends:
- graph
provides:
- topology
precedes: []
Next, the my_basic_topology
model component needs to decorate the switch vertex in the graph with the Switch
object (provided by the base_objects
component.
Therefore, it will also need to depend on base_objects
in order to be able to import and use the Switch()
class in its plugin
python module.
Lastly, you want to decorate the host vertices with Ubuntu1604Server
object (found in the linux.ubuntu1604
MC).
Therefore, the MANIFEST
would also include:
model_components:
depends:
- base_objects
- linux.ubuntu1604
precedes: []
Next you have a model component named tests.ping_all
, which will add a VM resource to each host and check for connectivity by sending ICMP packets.
Now, the tests.ping_all
MC would depend on the topology attribute label in it’s MANIFEST
because it needs hosts to exist to install the VM resource.
Its MANIFEST
would include:
attributes:
depends:
- topology
provides: []
precedes: []
Then you want this to be an emulated experiment so it should be launched with minimega after the tests.ping_all
VM resources are scheduled.
You would run your experiment as follows:
$ firewheel experiment my_basic_topology tests.ping_all minimega.launch
The dependency graph for this experiment would generally [1] look like the following:
misc.blank_graph base_objects linux.ubuntu1604
+ + +
| | |
| | |
| v |
+-------> my_basic_topology <-------+
+
|
|
v
tests.ping_all
+
|
|
v
minimega.launch
- The exact (and deterministic) order of execution as determined by our algorithm would be:
misc.blank_graph
base_objects
linux.ubuntu1604
my_basic_topology
tests.ping_all
minimega.launch
Using Precedes
Suppose we have the same my_basic_topology
from the previous section.
In this experiment we do not want to use tests.ping_all
,but want to immediately launch it.
In this case, we have two options.
We can simply launch it from the command line by using:
$ firewheel experiment my_basic_topology minimega.launch
Alternatively, we can update the my_basic_topology
MANIFEST
file to include:
model_components:
depends:
- base_objects
- linux.ubuntu1604
precedes:
- minimega.launch
Both options will produce the same result. It is important to realize that an MC (or attribute) that is preceded is only guaranteed to be executed after the MC that precedes it. It is NOT guaranteed to be executed as the “last” model component. To do this, you should use command line ordering.