xref: /cloud-hypervisor/docs/api.md (revision fa7a000dbe9637eb256af18ae8c3c4a8d5bf9c8f)
1- [Cloud Hypervisor API](#cloud-hypervisor-api)
2  - [External API](#external-api)
3    - [REST API](#rest-api)
4      - [REST API Location and availability](#rest-api-location-and-availability)
5      - [REST API Endpoints](#rest-api-endpoints)
6        - [Virtual Machine Manager (VMM) Actions](#virtual-machine-manager-vmm-actions)
7        - [Virtual Machine (VM) Actions](#virtual-machine-vm-actions)
8      - [REST API Examples](#rest-api-examples)
9        - [Create a Virtual Machine](#create-a-virtual-machine)
10        - [Boot a Virtual Machine](#boot-a-virtual-machine)
11        - [Dump a Virtual Machine Information](#dump-a-virtual-machine-information)
12        - [Reboot a Virtual Machine](#reboot-a-virtual-machine)
13        - [Shut a Virtual Machine Down](#shut-a-virtual-machine-down)
14    - [D-Bus API](#d-bus-api)
15      - [D-Bus API Location and availability](#d-bus-api-location-and-availability)
16      - [D-Bus API Interface](#d-bus-api-interface)
17    - [Command Line Interface](#command-line-interface)
18    - [REST API, D-Bus API and CLI Architectural Relationship](#rest-api-and-cli-architectural-relationship)
19  - [Internal API](#internal-api)
20    - [Goals and Design](#goals-and-design)
21  - [End to End Example](#end-to-end-example)
22
23# Cloud Hypervisor API
24
25The Cloud Hypervisor API is made of 2 distinct interfaces:
26
271. **The External API** This is the user facing API. Users and operators
28   can control and manage the Cloud Hypervisor through various options
29   including a REST API, a Command Line Interface (CLI) or a D-Bus based API,
30   which is not compiled into Cloud Hypervisor by default.
31
321. **The internal API**, based on [rust's Multi-Producer, Single-Consumer (MPSC)](https://doc.rust-lang.org/std/sync/mpsc/)
33   module. This API is used internally by the Cloud Hypervisor threads to
34   communicate between each others.
35
36The goal of this document is to describe the Cloud Hypervisor API as a whole,
37and to outline how the internal and external APIs are architecturally related.
38
39## External API
40
41### REST API
42
43The Cloud Hypervisor [REST](https://en.wikipedia.org/wiki/Representational_state_transfer)
44API triggers VM and VMM specific actions, and as such it is designed as a
45collection of RPC-style, static methods.
46
47The API is [OpenAPI 3.0](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md)
48compliant. Please consult the [Cloud Hypervisor OpenAPI Document](https://raw.githubusercontent.com/cloud-hypervisor/cloud-hypervisor/master/vmm/src/api/openapi/cloud-hypervisor.yaml)
49for more details about the API payloads and responses.
50
51#### REST API Location and availability
52
53The REST API is available as soon as the Cloud Hypervisor binary is started,
54through a local UNIX socket.
55By default, it is located at `/run/user/{user ID}/cloud-hypervisor.{Cloud Hypervisor PID}`.
56For example, if you launched Cloud Hypervisor as user ID 1000 and its PID is
57123456, the Cloud Hypervisor REST API will be available at `/run/user/1000/cloud-hypervisor.123456`.
58
59The REST API default URL can be overridden through the Cloud Hypervisor
60option `--api-socket`:
61
62```
63$ ./target/debug/cloud-hypervisor --api-socket /tmp/cloud-hypervisor.sock
64Cloud Hypervisor Guest
65    API server: /tmp/cloud-hypervisor.sock
66    vCPUs: 1
67    Memory: 512 MB
68    Kernel: None
69    Kernel cmdline:
70    Disk(s): None
71```
72
73#### REST API Endpoints
74
75The Cloud Hypervisor API exposes the following actions through its endpoints:
76
77##### Virtual Machine Manager (VMM) Actions
78
79| Action                              | Endpoint        | Request Body | Response Body              | Prerequisites      |
80| ----------------------------------- | --------------- | ------------ | -------------------------- | ------------------ |
81| Check for the REST API availability | `/vmm.ping`     | N/A          | `/schemas/VmmPingResponse` | N/A                |
82| Shut the VMM down                   | `/vmm.shutdown` | N/A          | N/A                        | The VMM is running |
83
84##### Virtual Machine (VM) Actions
85
86| Action                             | Endpoint                | Request Body                    | Response Body            | Prerequisites                                          |
87| ---------------------------------- | ----------------------- | ------------------------------- | ------------------------ | ------------------------------------------------------ |
88| Create the VM                      | `/vm.create`            | `/schemas/VmConfig`             | N/A                      | The VM is not created yet                              |
89| Delete the VM                      | `/vm.delete`            | N/A                             | N/A                      | N/A                                                    |
90| Boot the VM                        | `/vm.boot`              | N/A                             | N/A                      | The VM is created but not booted                       |
91| Shut the VM down                   | `/vm.shutdown`          | N/A                             | N/A                      | The VM is booted                                       |
92| Reboot the VM                      | `/vm.reboot`            | N/A                             | N/A                      | The VM is booted                                       |
93| Trigger power button of the VM     | `/vm.power-button`      | N/A                             | N/A                      | The VM is booted                                       |
94| Pause the VM                       | `/vm.pause`             | N/A                             | N/A                      | The VM is booted                                       |
95| Resume the VM                      | `/vm.resume`            | N/A                             | N/A                      | The VM is paused                                       |
96| Task a snapshot of the VM          | `/vm.snapshot`          | `/schemas/VmSnapshotConfig`     | N/A                      | The VM is paused                                       |
97| Perform a coredump of the VM*      | `/vm.coredump`          | `/schemas/VmCoredumpData`       | N/A                      | The VM is paused                                       |
98| Restore the VM from a snapshot     | `/vm.restore`           | `/schemas/RestoreConfig`        | N/A                      | The VM is created but not booted                       |
99| Add/remove CPUs to/from the VM     | `/vm.resize`            | `/schemas/VmResize`             | N/A                      | The VM is booted                                       |
100| Add/remove memory from the VM      | `/vm.resize`            | `/schemas/VmResize`             | N/A                      | The VM is booted                                       |
101| Add/remove memory from a zone      | `/vm.resize-zone`       | `/schemas/VmResizeZone`         | N/A                      | The VM is booted                                       |
102| Dump the VM information            | `/vm.info`              | N/A                             | `/schemas/VmInfo`        | The VM is created                                      |
103| Add VFIO PCI device to the VM      | `/vm.add-device`        | `/schemas/VmAddDevice`          | `/schemas/PciDeviceInfo` | The VM is booted                                       |
104| Add disk device to the VM          | `/vm.add-disk`          | `/schemas/DiskConfig`           | `/schemas/PciDeviceInfo` | The VM is booted                                       |
105| Add fs device to the VM            | `/vm.add-fs`            | `/schemas/FsConfig`             | `/schemas/PciDeviceInfo` | The VM is booted                                       |
106| Add pmem device to the VM          | `/vm.add-pmem`          | `/schemas/PmemConfig`           | `/schemas/PciDeviceInfo` | The VM is booted                                       |
107| Add network device to the VM       | `/vm.add-net`           | `/schemas/NetConfig`            | `/schemas/PciDeviceInfo` | The VM is booted                                       |
108| Add userspace PCI device to the VM | `/vm.add-user-device`   | `/schemas/VmAddUserDevice`      | `/schemas/PciDeviceInfo` | The VM is booted                                       |
109| Add vdpa device to the VM          | `/vm.add-vdpa`          | `/schemas/VdpaConfig`           | `/schemas/PciDeviceInfo` | The VM is booted                                       |
110| Add vsock device to the VM         | `/vm.add-vsock`         | `/schemas/VsockConfig`          | `/schemas/PciDeviceInfo` | The VM is booted                                       |
111| Remove device from the VM          | `/vm.remove-device`     | `/schemas/VmRemoveDevice`       | N/A                      | The VM is booted                                       |
112| Dump the VM counters               | `/vm.counters`          | N/A                             | `/schemas/VmCounters`    | The VM is booted                                       |
113| Inject an NMI                      | `/vm.nmi`               | N/A                             | N/A                      | The VM is booted                                       |
114| Prepare to receive a migration     | `/vm.receive-migration` | `/schemas/ReceiveMigrationData` | N/A                      | N/A                                                    |
115| Start to send migration to target  | `/vm.send-migration`    | `/schemas/SendMigrationData`    | N/A                      | The VM is booted and (shared mem or hugepages enabled) |
116
117* The `vmcoredump` action is available exclusively for the `x86_64`
118architecture and can be executed only when the `guest_debug` feature is
119enabled. Without this feature, the corresponding [REST API](#rest-api) or
120[D-Bus API](#d-bus-api) endpoints are not available.
121
122#### REST API Examples
123
124For the following set of examples, we assume Cloud Hypervisor is started with
125the REST API available at `/tmp/cloud-hypervisor.sock`:
126
127```
128$ ./target/debug/cloud-hypervisor --api-socket /tmp/cloud-hypervisor.sock
129Cloud Hypervisor Guest
130    API server: /tmp/cloud-hypervisor.sock
131    vCPUs: 1
132    Memory: 512 MB
133    Kernel: None
134    Kernel cmdline:
135    Disk(s): None
136```
137
138##### Create a Virtual Machine
139
140We want to create a virtual machine with the following characteristics:
141
142* 4 vCPUs
143* 1 GB of RAM
144* 1 virtio based networking interface
145* Direct kernel boot from a custom 5.6.0-rc4 Linux kernel located at
146  `/opt/clh/kernel/vmlinux-virtio-fs-virtio-iommu`
147* Using a Ubuntu image as its root filesystem, located at
148  `/opt/clh/images/focal-server-cloudimg-amd64.raw`
149
150```shell
151#!/usr/bin/env bash
152
153curl --unix-socket /tmp/cloud-hypervisor.sock -i \
154     -X PUT 'http://localhost/api/v1/vm.create'  \
155     -H 'Accept: application/json'               \
156     -H 'Content-Type: application/json'         \
157     -d '{
158         "cpus":{"boot_vcpus": 4, "max_vcpus": 4},
159         "payload":{"kernel":"/opt/clh/kernel/vmlinux-virtio-fs-virtio-iommu", "cmdline":"console=ttyS0 console=hvc0 root=/dev/vda1 rw"},
160         "disks":[{"path":"/opt/clh/images/focal-server-cloudimg-amd64.raw"}],
161         "rng":{"src":"/dev/urandom"},
162         "net":[{"ip":"192.168.10.10", "mask":"255.255.255.0", "mac":"12:34:56:78:90:01"}]
163         }'
164```
165
166##### Boot a Virtual Machine
167
168Once the VM is created, we can boot it:
169
170```shell
171#!/usr/bin/env bash
172
173curl --unix-socket /tmp/cloud-hypervisor.sock -i -X PUT 'http://localhost/api/v1/vm.boot'
174```
175
176##### Dump a Virtual Machine Information
177
178We can fetch information about any VM, as soon as it's created:
179
180```shell
181#!/usr/bin/env bash
182
183curl --unix-socket /tmp/cloud-hypervisor.sock -i \
184     -X GET 'http://localhost/api/v1/vm.info' \
185     -H 'Accept: application/json'
186```
187
188##### Reboot a Virtual Machine
189
190We can reboot a VM that's already booted:
191
192```shell
193#!/usr/bin/env bash
194
195curl --unix-socket /tmp/cloud-hypervisor.sock -i -X PUT 'http://localhost/api/v1/vm.reboot'
196```
197
198##### Shut a Virtual Machine Down
199
200Once booted, we can shut a VM down from the REST API:
201
202```shell
203#!/usr/bin/env bash
204
205curl --unix-socket /tmp/cloud-hypervisor.sock -i -X PUT 'http://localhost/api/v1/vm.shutdown'
206```
207
208### D-Bus API
209
210Cloud Hypervisor offers a D-Bus API as an alternative to its REST API. This
211D-Bus API fully reflects the functionality of the REST API, exposing the
212same group of endpoints. It can be a drop-in replacement since it also
213consumes/produces JSON.
214
215In addition, the D-Bus API also exposes events from `event-monitor` in the
216form of a D-Bus signal to which users can subscribe. For more information,
217see [D-Bus API Interface](#d-bus-api-interface).
218
219#### D-Bus API Location and availability
220
221This feature is not compiled into Cloud Hypervisor by default. Users who
222wish to use the D-Bus API, must explicitly enable it with the `dbus_api`
223feature flag when compiling Cloud Hypervisor.
224
225```sh
226$ ./scripts/dev_cli.sh build --release --libc musl -- --features dbus_api
227```
228
229Once this feature is enabled, it can be configured with the following
230CLI options:
231
232```
233  --dbus-service-name
234                    well known name of the service
235  --dbus-object-path
236                    object path to serve the dbus interface
237  --dbus-system-bus use the system bus instead of a session bus
238```
239
240Example invocation:
241
242```sh
243$ ./cloud-hypervisor --dbus-service-name "org.cloudhypervisor.DBusApi" \
244                     --dbus-object-path "/org/cloudhypervisor/DBusApi"
245```
246
247This will start serving a service with the name `org.cloudhypervisor.DBusApi1`
248which in turn can be used to control and manage Cloud Hypervisor.
249
250#### D-Bus API Interface
251
252Please refer to the [REST API](#rest-api) documentation for everything that
253is in common with the REST API. As previously mentioned, the D-Bus API can
254be used as a drop-in replacement for the [REST API](#rest-api).
255
256The D-Bus interface also exposes a signal, named `Event`, which is emitted
257whenever a new event is published from the `event-monitor` crate. Here is its
258definition in XML format:
259
260```xml
261<node>
262  <interface name="org.cloudhypervisor.DBusApi1">
263    <signal name="Event">
264      <arg name="event" type="s"/>
265    </signal>
266  </interface>
267</node>
268```
269
270### Command Line Interface
271
272The Cloud Hypervisor Command Line Interface (CLI) can only be used for launching
273the Cloud Hypervisor binary, i.e. it can not be used for controlling the VMM or
274the launched VM once they're up and running.
275
276If you want to inspect the VMM, or control the VM after launching Cloud
277Hypervisor from the CLI, you must use either the [REST API](#rest-api)
278or the [D-Bus API](#d-bus-api).
279
280From the CLI, one can:
281
2821. Create and boot a complete virtual machine by using the CLI options to build
283   the VM config. Run `cloud-hypervisor --help` for a complete list of CLI
284   options. As soon as the `cloud-hypervisor` binary is launched, contrary
285   to the [D-Bus API](#d-bus-api), the [REST API](#rest-api) is available
286   for controlling and managing the VM. The [D-Bus API](#d-bus-api) doesn't start
287   automatically and needs to be explicitly configured in order to be run.
2881. Start either the REST API, D-Bus API or both simultaneously without passing
289   any VM configuration options. The VM can then be asynchronously created and
290   booted by calling API methods of choice. It should be noted that one external
291   API does not exclude another; it is possible to have both the REST and D-Bus
292   APIs running simultaneously.
293
294### REST API, D-Bus API and CLI Architectural Relationship
295
296The REST API, D-Bus API and the CLI all rely on a common, [internal API](#internal-api).
297
298The CLI options are parsed by the
299[clap crate](https://docs.rs/clap/4.3.11/clap/) and then translated into
300[internal API](#internal-api) commands.
301
302The REST API is processed by an HTTP thread using the
303[Firecracker's `micro_http`](https://github.com/firecracker-microvm/micro-http)
304crate. As with the CLI, the HTTP requests eventually get translated into
305[internal API](#internal-api) commands.
306
307The D-Bus API is implemented using the [zbus](https://github.com/dbus2/zbus)
308crate and runs in its own thread. Whenever it needs to call the [internal API](#internal-api),
309the [blocking](https://github.com/smol-rs/blocking) crate is used perform the call in zbus' async context.
310
311As a summary, the REST API, the D-Bus API and the CLI are essentially frontends for the
312[internal API](#internal-api):
313
314```
315                                  +------------------+
316                        REST API  |                  |
317                       +--------->+    micro_http    +--------+
318                       |          |                  |        |
319                       |          +------------------+        |
320                       |                                      |      +------------------------+
321                       |                                      |      |                        |
322+------------+         |            +----------+              |      |                        |
323|            |         |  D-Bus API |          |              |      | +--------------+       |
324|    User    +---------+----------->+   zbus   +--------------+------> | Internal API |       |
325|            |         |            |          |              |      | +--------------+       |
326+------------+         |            +----------+              |      |                        |
327                       |                                      |      |                        |
328                       |                                      |      +------------------------+
329                       |            +----------+              |                 VMM
330                       |     CLI    |          |              |
331                       +----------->+   clap   +--------------+
332                                    |          |
333                                    +----------+
334
335
336```
337
338## Internal API
339
340The Cloud Hypervisor internal API, as its name suggests, is used internally
341by the different Cloud Hypervisor threads (VMM, HTTP, D-Bus, control loop,
342etc) to send commands and responses to each others.
343
344It is based on [rust's Multi-Producer, Single-Consumer (MPSC)](https://doc.rust-lang.org/std/sync/mpsc/),
345and the single consumer (a.k.a. the API receiver) is the Cloud Hypervisor
346control loop.
347
348API producers are the HTTP thread handling the [REST API](#rest-api), the
349D-Bus thread handling the [D-Bus API](#d-bus-api) and the main thread that
350initially parses the [CLI](#command-line-interface).
351
352### Goals and Design
353
354The internal API is designed for controlling, managing and inspecting a Cloud
355Hypervisor VMM and its guest. It is a backend for handling external, user
356visible requests through the [REST API](#rest-api), the [D-Bus API](#d-bus-api)
357or the [CLI](#command-line-interface) interfaces.
358
359The API follows a command-response scheme that closely maps the [REST API](#rest-api).
360Any command must be replied to with a response.
361
362Commands are [MPSC](https://doc.rust-lang.org/std/sync/mpsc/) based messages and
363are received and processed by the VMM control loop.
364
365In order for the VMM control loop to respond to any internal API command, it
366must be able to send a response back to the MPSC sender. For that purpose, all
367internal API command payload carry the [Sender](https://doc.rust-lang.org/std/sync/mpsc/struct.Sender.html)
368end of an [MPSC](https://doc.rust-lang.org/std/sync/mpsc/) channel.
369
370The sender of any internal API command is therefore responsible for:
371
3721. Creating an [MPSC](https://doc.rust-lang.org/std/sync/mpsc/) response
373   channel.
3741. Passing the [Sender](https://doc.rust-lang.org/std/sync/mpsc/struct.Sender.html)
375   end of the response channel as part of the internal API command payload.
3761. Waiting for the internal API command's response on the [Receiver](https://doc.rust-lang.org/std/sync/mpsc/struct.Receiver.html)
377   end of the response channel.
378
379## End to End Example
380
381In order to further understand how the external and internal Cloud Hypervisor
382APIs work together, let's look at a complete VM creation flow, from the
383[REST API](#rest-api) call, to the reply the external user will receive:
384
3851. A user or operator sends an HTTP request to the Cloud Hypervisor
386   [REST API](#rest-api) in order to creates a virtual machine:
387   ```
388   shell
389   #!/usr/bin/env bash
390
391	curl --unix-socket /tmp/cloud-hypervisor.sock -i \
392		-X PUT 'http://localhost/api/v1/vm.create'  \
393		-H 'Accept: application/json'               \
394		-H 'Content-Type: application/json'         \
395		-d '{
396			"cpus":{"boot_vcpus": 4, "max_vcpus": 4},
397			"payload":{"kernel":"/opt/clh/kernel/vmlinux-virtio-fs-virtio-iommu", "cmdline":"console=ttyS0 console=hvc0 root=/dev/vda1 rw"},
398			"disks":[{"path":"/opt/clh/images/focal-server-cloudimg-amd64.raw"}],
399			"rng":{"src":"/dev/urandom"},
400			"net":[{"ip":"192.168.10.10", "mask":"255.255.255.0", "mac":"12:34:56:78:90:01"}]
401			}'
402   ```
4031. The Cloud Hypervisor HTTP thread processes the request and de-serializes the
404   HTTP request JSON body into an internal `VmConfig` structure.
4051. The Cloud Hypervisor HTTP thread creates an
406   [MPSC](https://doc.rust-lang.org/std/sync/mpsc/) channel for the internal API
407   server to send its response back.
4081. The Cloud Hypervisor HTTP thread prepares an internal API command for creating a
409   virtual machine. The command's payload is made of the de-serialized
410   `VmConfig` structure and the response channel:
411   ```Rust
412   VmCreate(Arc<Mutex<VmConfig>>, Sender<ApiResponse>)
413   ```
4141. The Cloud Hypervisor HTTP thread sends the internal API command, and waits
415   for the response:
416   ```Rust
417   // Send the VM creation request.
418    api_sender
419        .send(ApiRequest::VmCreate(config, response_sender))
420        .map_err(ApiError::RequestSend)?;
421    api_evt.write(1).map_err(ApiError::EventFdWrite)?;
422
423    response_receiver.recv().map_err(ApiError::ResponseRecv)??;
424   ```
4251. The Cloud Hypervisor control loop receives the command, as it listens on the
426   internal API [MPSC](https://doc.rust-lang.org/std/sync/mpsc/) channel:
427   ```Rust
428   // Read from the API receiver channel
429   let api_request = api_receiver.recv().map_err(Error::ApiRequestRecv)?;
430   ```
4311. The Cloud Hypervisor control loop matches the received internal API against
432   the `VmCreate` payload, and extracts both the `VmConfig` structure and the
433   [Sender](https://doc.rust-lang.org/std/sync/mpsc/struct.Sender.html) from the
434   command payload. It stores the `VmConfig` structure and replies back to the
435   sender ((The HTTP thread):
436   ```Rust
437   match api_request {
438	   ApiRequest::VmCreate(config, sender) => {
439		   // We only store the passed VM config.
440		   // The VM will be created when being asked to boot it.
441		   let response = if self.vm_config.is_none() {
442			   self.vm_config = Some(config);
443			   Ok(ApiResponsePayload::Empty)
444		   } else {
445			   Err(ApiError::VmAlreadyCreated)
446		   };
447
448	       sender.send(response).map_err(Error::ApiResponseSend)?;
449	   }
450   ```
4511. The Cloud Hypervisor HTTP thread receives the internal API command response
452   as the return value from its `VmCreate` HTTP handler. Depending on the
453   control loop internal API response, it generates the appropriate HTTP
454   response:
455   ```Rust
456   // Call vm_create()
457   match vm_create(api_notifier, api_sender, Arc::new(Mutex::new(vm_config)))
458	   .map_err(HttpError::VmCreate)
459   {
460	   Ok(_) => Response::new(Version::Http11, StatusCode::NoContent),
461	   Err(e) => error_response(e, StatusCode::InternalServerError),
462   }
463   ```
4641. The Cloud Hypervisor HTTP thread sends the formed HTTP response back to the
465   user. This is abstracted by the
466   [micro_http](https://github.com/firecracker-microvm/micro-http)
467   crate.
468