-
Notifications
You must be signed in to change notification settings - Fork 4
Extending Migration Framework
This page contains a collection of short manuals on how to extend the Migration-Framework
The base class for all hypervisors is the class Hypervisor
.
So, in order to create a new hypervisor you have to include the header and derive the new hypervisor class from the base class:
//File: src/my_hypervisor.hpp
#ifndef MY_HYPERVISOR_HPP
#define MY_HYPERVISOR_HPP
#include "hypervisor.hpp"
class My_hypervisor :
public Hypervisor
{
//...
};
#endif
The Hypervisor class defines some pure virtual methods which the custom hypervisor should override. If the hypervisor cannot implement all methods, it should implement it by just throwing an exception:
//File: src/my_hypervisor.hpp
#ifndef MY_HYPERVISOR_HPP
#define MY_HYPERVISOR_HPP
#include "hypervisor.hpp"
class My_hypervisor :
public Hypervisor
{
void start(const fast::msg::migfra::Start &task, fast::msg::migfra::Time_measurement &time_measurement) override;
};
#endif
(note the override
behind the method signature, which tells the compile that this function should be virtual and should override a virtual method from the base class)
//File:
void My_hypervisor::start(const fast::msg::migfra::Start &task, fast::msg::migfra::Time_measurement &time_measurement)
{
(void) task; (void) time_measurement;
throw std::runtime_error("My_hypervisor has no support for starting.");
}
The (void)
casts are just there to suppress unused parameter warnings.
See, Dummy_hypervisor
for a very simple hypervisor that does not do anything and Libvirt_hypervisor
as the most complete example.
If the new hypervisor is ready and implemented it is time to integrate it into the Migration-Framework.
Therefor, the new hypervisor has to be included in the Task_handler
, which manages the initilization and implements the main loop.
So, go to src/task_handler.cpp
and add #include "my_hypervisor.hpp"
at the top next to the other hypervisor includes.
Then go to the method void Task_handler::load(const YAML::Node &node)
which implements parsing of the config file and initialization of the hypervisor.
Here, you have to search for a big if construct starting with if (type == "libvirt") {
and you have to add something like this:
} else if (type == "my") {
hypervisor = std::make_shared<My_hypervisor>();
}
See the libvirt part here for a more complex initilization of a hypervisor, which also parses hypervisor options from the config file.
The config file will be generated from src/migfra.conf.in
and can be found in build/migfra.conf
after building.
In order to add a new Task, firstly a struct representing the task has to be defined in fast-lib:"include/fast-lib/message/migfra/task.hpp".
This task has to be derived from the base class Task
.
include/fast-lib/message/migfra/task.hpp
struct My_task : public Task
{
// Constructors:
My_task();
// Serialization:
YAML::Node emit() const override;
void load(const YAML:Node &node) override;
// Data:
std::string vm_name;
};
Be sure to implement the class in fast-lib:"src/message/migfra/task.cpp". You may use the Optional container to denote optional data fields. The emit and load function have to be overridden to implement serialization to YAML.
After that the task has to be added to the types array in Task_container::type()
fast-lib:"src/message/migfra/task.cpp" in order to define the type of task and result.
Also, in the following if construct the task has to be added similarly to this:
//...
else if (std::dynamic_pointer_cast<My_task>(tasks.front()))
return types[8];
//...
For formats containing lists of tasks also Task_container::emit()
has to be altered.
After that a load_my_task()
function has to be defined, which defines from which node the task is loaded.
To make those changes easy, do everything similarly to the other existing tasks.
Lastly, in migration-framework:"src/task.cpp" you have to change the function execute()
.
Here, the Base Class is cast into the derived classes and for the successful cast, the corresponding hypervisor function is called.
So, here we can call the actual job the task should perform.
auto my_task = std::dynamic_pointer_cast<My_task>(task);
//...
} else if (my_task) {
vm_name = resume_task->vm_name;
// call hypervisor here
}
More options for tasks can be added by defining them in fast-lib:"include/fast-lib/message/migfra/task.hpp" and implementing the serialization in fast-lib:"src/message/migfra/task.cpp".
The important part is to add them to emit and load for serialization.
For optional options you can use the Optional container class for which you have to define a tag in the constructor and serialization is done using emit and load.
The merge_node macro may be used to merge two YAML nodes.
You can also define other composed classes which support serialization for more complex structures (for example see the Swap_with class).
After adding options in fast-lib a version tag for the commit can be added in github and in the CMakeLists.txt of Migration-Framework change the GIT_TAG
version to your new commit.
Then the new options are ready to use.