virtualize_new_api - yuhc/ava GitHub Wiki
This tutorial instructs how to virtualize a new API with AvA using the demo API
as the example (API name: demo).
We use cmake to configure our build. Add the following lines to ava/CMakeLists.txt
so that we can configure to build the new API virtualization:
set(AVA_GEN_DEMO_SPEC OFF CACHE BOOL "Enable demo specification")
message(STATUS "Build demo specification: ${AVA_GEN_DEMO_SPEC}")
Add the following line to ava-spec external project's CMAKE_CACHE_ARGS:
-DAVA_GEN_DEMO_SPEC:BOOL=${AVA_GEN_DEMO_SPEC}
At the end of the file, add:
message(STATUS "Compile demo specification: ${AVA_GEN_DEMO_SPEC}")
if(AVA_GEN_DEMO_SPEC)
include(samples/demo/CMakeLists.txt)
endif()
As it shows, we will put our new specification and corresponding CMakeLists file
in ava/cava/samples/demo.
We will write a more detailed tutorial on how to write API specifications (Writing a Specification). For now, ava/cava/lapis.md is the best documentation that we have for writing specifications.
Write your first specification!
Create a demo.c under ava/cava/samples/demo.
Define the API name, version, identifier and number at the top of the specification:
ava_name("DEMO");
ava_version("0.0.1");
ava_identifier(DEMO);
ava_number(1);The identifier is the prefix of the generated files and directory (demo_nw).
The API number is the ID used to differentiate the virtualized APIs in the system, which is optional.
Then we need to provide C (ava_cflags) or C++ flags (ava_cxxflags) for compiling the generated code.
It usually tells the compiler where to find the included headers (e.g., -I/usr/local/cuda-10.1/include)
as well as other compilation options.
We can specify which libraries should be linked via ava_libs (e.g., -lcuda).
The workspace is in ava/cava/samples/demo_nw.
In this example, we defined the libdemo header in ava/cava/headers/demo.h, so we use that header
search path in the cflags and include the header file:
ava_cflags(-I../headers);
#include <demo.h>Our specification compiler, cava, can be used to generate a preliminary specification from the specification we just edited. In
ava/cava, run./nwcc samples/demo/demo.c -Iheaders --dumpwhich will print the specification to stdout. This feature may help when you are virtualizing a lot of APIs. But the annotations are generated from templates and require human refinition.
We will use fprintf in the specification to print logs, which are considered as an utility:
ava_begin_utility;
#include <stdio.h>
ava_end_utility;
At last, we declare the APIs from demo.h to be virtualized:
int ava_test_api(int x);
ava_test_api only takes a simple scalar parameter which needs no special annotation.
So it is good to go.
In this example, we do not really write a demo library (i.e., libdemo). Instead, we define
ava_test_apiinside the specification. Check outava/cava/samples/demo/demo.cfor more details.
The CMakeLists.txt for the specification can be more complicated than other cmake files.
Fortunately, one can copy the existing specifications' CMakeLists.txt and make simple modifications.
For example, starting from ava/cava/samples/demo/CMakeLists.txt:
- Replace
demowith the new API's identifier. - Change the parameters given to cava in the
nwccstep. - Create symbolic links to
libguestlib.soin thelinkstep.
Add the following lines to ava/CMakeLists.txt so that we can configure to build
the new API server manager:
set(AVA_MANAGER_DEMO OFF CACHE BOOL "Build demo manager")
message(STATUS "Build demo manager: ${AVA_MANAGER_DEMO}")
Add the following line to ava-manager external project's CMAKE_CACHE_ARGS:
-DAVA_MANAGER_DEMO:BOOL=${AVA_MANAGER_DEMO}
Add the following lines to the end of the file:
if(AVA_MANAGER_DEMO)
add_subdirectory(demo)
endif()
A developer can write any AvA manager according their needs, as long as the manager satisfies the following rules:
- The manager receives a
WorkerAssignRequestrequest (defined inava/proto/manager_service.proto) from guestlib. The request is serialized and delimited: the first 4 bytes is the length (L) of the serialized request, and the next L bytes is the serialized request. - The manager spawns API servers with any kind of policies or behaviors. After that,
the manager packs the spawned API server's address (IP:port) into a
WorkerAssignReplyresponse, serializes and delimits the response in the same way as the request.
To simplify this process, we defined a ava_manager::ManagerServiceServerBase class
in ava/worker/include/manager_service.h.
In this example, The developer creates DemoManager class inheriting this base class
and starts a basic request handling service by:
DemoManager manager(kDefaultManagerPort, kDefaultWorkerPortBase, argv[1]);
manager.RunServer();The developer can overload HandleRequest and SpawnWorker methods in the derived
class to enforce more complex scheduling policies.
The AvA manager can have multiple components built into separate binaries. The master code must enforce the above rules, while there is no restriction of how the components communicate internally (e.g., they can talk with gRPC instead of Boost::asio).
ava/worker/demo/CMakeLists.txt shows a minimal CMakeLists file for building an
AvA manager.
At the root of the build directory, run:
cmake ../ava -DAVA_GEN_DEMO_SPEC=ON -DAVA_MANAGER_DEMO=ON
makeThe command will generate virtualization codes from the demo API specification
into ava/cava/demo_nw.
The codes are compiled in build/generated/demo_nw and copied into build/install/demo_nw.
The demo manager is compiled in build/worker/demo and copied into build/install/bin.