Creating a Model Component for a New Image
For a quick refresher on what a model component is, see Model Components. You can also review the model components that were created in the ACME repository. In the model component that this tutorial builds, we are going to focus on create a model component that provides objects to plugins.
Generating a Model Component Skeleton
Create a directory for our model component within a repository and then run the firewheel mc generate
function with the appropriate parameters.
Since we’re building an Ubuntu Server
it makes sense to place this model component in the linux
repository:
$ mkdir -p /opt/firewheel/model_components/linux/ubuntu/cosmic
$ mv ubuntu-18.10-server-amd64.qcow2.xz /opt/firewheel/model_components/linux/ubuntu/cosmic
$ firewheel mc generate --name linux.ubuntu1810 \
--location /opt/firewheel/linux/ubuntu/cosmic \
--image ubuntu-18.10-server-amd64.qcow2.xz \
--arch x86_64 \
--model_component_depends linux.ubuntu \
--model_component_objects model_component_objects.py
We’ll walk through the parameters:
name
: Choose the name of the model component. In this case we uselinux.ubuntu1810
.location
: The absolute path the directory that should contain this model component.image
: The path (relative to the model component) to the image file that was created in Building a New Image from ISO. In this case it is expected that the disk image lives within thecosmic
directory.model_component_depends
: We want to be able to use all the functions that a base Linux VM provides, therefore we depend onlinux.base_objects
.model_component_objects
: This specifies the name of the file that holds the objects that we want to provide to a topology. It can be named anything you like, but we have found that usingmodel_component_objects.py
is a reasonable convention to follow.
As mentioned above, this model component assumes that the ubuntu-18.10-server-amd64.qcow2.xz
file is located within the cosmic
directory.
It is best to have the image within the model component because then everything associated with the component is in one place.
You should now see all the relevant model component files in /opt/firewheel/linux/ubuntu/cosmic
.
Creating the Ubuntu1810Server
object
The MANIFEST
file defined that the model component objects are located in a file called model_component_objects.py
.
Create that file to look as follows:
from firewheel.control.experiment_graph import require_class
from linux.ubuntu import UbuntuServer
@require_class(UbuntuServer)
class Ubuntu1810Server(object):
def __init__(self):
try:
self.vm
except AttributeError:
self.vm = {}
if 'architecture' not in self.vm:
self.vm['architecture'] = 'x86_64'
if 'vcpu' not in self.vm:
self.vm['vcpu'] = {
'model': 'qemu64',
'sockets': 1,
'cores': 1,
'threads': 1
}
if 'mem' not in self.vm:
self.vm['mem'] = 256
if 'drives' not in self.vm:
self.vm['drives'] = [
{
'db_path': 'ubuntu-18.10-server-amd64.qcow2.xz',
'file': 'ubuntu-18.10-server-amd64.qcow2'
}
]
if 'vga' not in self.vm:
self.vm['vga'] = 'std'
self.set_image('ubuntu1810server')
Let’s walk through the object.
First, we import two things, require_class
and UbuntuServer
.
The require_class
function is a decorator that we use to indicate inter-class dependencies.
So, in this case, we’re saying that the Ubuntu1810Server
class requires the UbuntuServer
class.
You can think of this as reverse inheritance.
In inheritance, the object being instantiated gets to run first and passes control to its parent through a call to super()
.
When using the require_class
decorator it gives control to the object that is required before handing it over to the object being instantiated.
Similar to inheritance, any function that is defined in UbuntuServer
is available to Ubuntu1810Server
.
This allows us to create “layers” that can be “stacked” in different ways to make complex objects.
It also allows us to define functions at an appropriate layer so that they can have the widest possible usage.
Therefore, UbuntuServer
has functions that are relevant to any image that uses Ubuntu Server as well as any model components that are higher in the stack.
An example of such a function is configure_ips()
.
The configure_ips()
function in the LinuxHost
object will ensure that the correct commands get run on a Linux VM to set its IP address.
This differs from the configure_ips()
function in the WindowsHost
object since the mechanism for setting an IP on Windows is different than on Linux.
The UbuntuServer
class requires UbuntuHost
which requires LinuxHost
Therefore, we can require the LinuxHost
class from many different objects and duplicating effort.
We recommend that you use the most specific class to enable all available functions.
For example, the UbuntuHost
, which UbuntuServer
requires, contains the install_debs()
function which installs Debian packages.
Next, we check to see if the vm
variable already exists on the object.
Remember, decorators are reversed from inheritance in the sense that the required classes are executed first.
Therefore, we have to be careful not to accidentally undo things previous “layers” did unless it is intended.
Once the vm
dictionary is present, we set parameters such as architecture
, vcpu
, mem
, drives
, and vga
.
Feel free to change any of these values to fit the needs of the image that is being created.
Note that db_path
needs to be the same as the paths
parameter that was set in the MANIFEST
file.
The file
parameter allows the name of the decompressed disk to be something different than the base name of the compressed disk, although it is doubtful that they’ll be different in most cases (other than the xz
suffix of course).
The set_image()
function takes an arbitrary string.
Feel free to set it to anything that you like, but it is good convention to make it consistent with the name of the class.
This value tells FIREWHEEL
that the VM decorated by this class does not need a default image because we’re explicitly setting its image type in the class.
At this point this class is usable within a topology.
All that a topology would need to do is import this class via from linux.ubuntu1810 import Ubuntu1810Server
and to decorate a vertex with the Ubuntu1810Server
class.
Don’t forget to add linux.ubuntu1810
as a model component dependency in the topology model component’s MANIFEST
file.