xref: /cloud-hypervisor/docs/api.md (revision b440cb7d2330770cd415b63544a371d4caa2db3a)
1- [Cloud Hypervisor API](#cloud-hypervisor-api)
2  * [External API](#external-api)
3    + [REST API](#rest-api)
4      - [Location and availability](#location-and-availability)
5      - [Endpoints](#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    + [Command Line Interface](#command-line-interface)
15    + [REST API and CLI Architectural Relationship](#rest-api-and-cli-architectural-relationship)
16  * [Internal API](#internal-api)
17    + [Goals and Design](#goals-and-design)
18  * [End to End Example](#end-to-end-example)
19
20# Cloud Hypervisor API
21
22The Cloud Hypervisor API is made of 2 distinct interfaces:
23
241. **The external API**. This is the user facing API. Users and operators can
25   control and manage Cloud Hypervisor through either a REST API or a Command
26   Line Interface (CLI).
271. **The internal API**, based on [rust's Multi-Producer, Single-Consumer (MPSC)](https://doc.rust-lang.org/std/sync/mpsc/)
28   module. This API is used internally by the Cloud Hypervisor threads to
29   communicate between each others.
30
31The goal of this document is to describe the Cloud Hypervisor API as a whole,
32and to outline how the internal and external APIs are architecturally related.
33
34## External API
35
36### REST API
37
38The Cloud Hypervisor [REST](https://en.wikipedia.org/wiki/Representational_state_transfer)
39API triggers VM and VMM specific actions, and as such it is designed as a
40collection of RPC-style, static methods.
41
42The API is [OpenAPI 3.0](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md)
43compliant. Please consult the [Cloud Hypervisor API](https://raw.githubusercontent.com/cloud-hypervisor/cloud-hypervisor/master/vmm/src/api/openapi/cloud-hypervisor.yaml)
44document for more details about the API payloads and responses.
45
46### Location and availability
47
48The REST API is available as soon as the Cloud Hypervisor binary is started,
49through a local UNIX socket.
50By default, it is located at `/run/user/{user ID}/cloud-hypervisor.{Cloud Hypervisor PID}`.
51For example, if you launched Cloud Hypervisor as user ID 1000 and its PID is
52123456, the Cloud Hypervisor REST API will be available at `/run/user/1000/cloud-hypervisor.123456`.
53
54The REST API default URL can be overridden through the Cloud Hypervisor
55option `--api-socket`:
56
57```
58$ ./target/debug/cloud-hypervisor --api-socket /tmp/cloud-hypervisor.sock
59Cloud Hypervisor Guest
60    API server: /tmp/cloud-hypervisor.sock
61    vCPUs: 1
62    Memory: 512 MB
63    Kernel: None
64    Kernel cmdline:
65    Disk(s): None
66```
67
68### Endpoints
69
70The Cloud Hypervisor API exposes the following actions through its endpoints:
71
72#### Virtual Machine Manager (VMM) Actions
73
74Action                              | Endpoint        | Request Body | Response Body              | Prerequisites
75------------------------------------|-----------------|--------------|----------------------------|---------------------------
76Check for the REST API availability | `/vmm.ping`     | N/A          | `/schemas/VmmPingResponse` | N/A
77Shut the VMM down                   | `/vmm.shutdown` | N/A          | N/A                        | The VMM is running
78
79#### Virtual Machine (VM) Actions
80
81Action                             | Endpoint             | Request Body              | Response Body            | Prerequisites
82-----------------------------------|----------------------|---------------------------|--------------------------|---------------------------
83Create the VM                      | `/vm.create`         | `/schemas/VmConfig`       | N/A                      | The VM is not created yet
84Delete the VM                      | `/vm.delete`         | N/A                       | N/A                      | N/A
85Boot the VM                        | `/vm.boot`           | N/A                       | N/A                      | The VM is created but not booted
86Shut the VM down                   | `/vm.shutdown`       | N/A                       | N/A                      | The VM is booted
87Reboot the VM                      | `/vm.reboot`         | N/A                       | N/A                      | The VM is booted
88Trigger power button of the VM     | `/vm.power-button`   | N/A                       | N/A                      | The VM is booted
89Pause the VM                       | `/vm.pause`          | N/A                       | N/A                      | The VM is booted
90Resume the VM                      | `/vm.resume`         | N/A                       | N/A                      | The VM is paused
91Task a snapshot of the VM          | `/vm.snapshot`       | `/schemas/VmSnapshotConfig`| N/A                     | The VM is paused
92Perform a coredump of the VM       | `/vm.coredump`       | `/schemas/VmCoredumpData` | N/A                      | The VM is paused
93Restore the VM from a snapshot     | `/vm.restore`        | `/schemas/RestoreConfig`  | N/A                      | The VM is created but not booted
94Add/remove CPUs to/from the VM     | `/vm.resize`         | `/schemas/VmResize`       | N/A                      | The VM is booted
95Add/remove memory from the VM      | `/vm.resize`         | `/schemas/VmResize`       | N/A                      | The VM is booted
96Add/remove memory from a zone      | `/vm.resize-zone`    | `/schemas/VmResizeZone`   | N/A                      | The VM is booted
97Dump the VM information            | `/vm.info`           | N/A                       | `/schemas/VmInfo`        | The VM is created
98Add VFIO PCI device to the VM      | `/vm.add-device`     | `/schemas/VmAddDevice`    | `/schemas/PciDeviceInfo` | The VM is booted
99Add disk device to the VM          | `/vm.add-disk`       | `/schemas/DiskConfig`     | `/schemas/PciDeviceInfo` | The VM is booted
100Add fs device to the VM            | `/vm.add-fs`         | `/schemas/FsConfig`       | `/schemas/PciDeviceInfo` | The VM is booted
101Add pmem device to the VM          | `/vm.add-pmem`       | `/schemas/PmemConfig`     | `/schemas/PciDeviceInfo` | The VM is booted
102Add network device to the VM       | `/vm.add-net`        | `/schemas/NetConfig`      | `/schemas/PciDeviceInfo` | The VM is booted
103Add userspace PCI device to the VM | `/vm.add-user-device`| `/schemas/VmAddUserDevice`| `/schemas/PciDeviceInfo` | The VM is booted
104Add vdpa device to the VM          | `/vm.add-vdpa`       | `/schemas/VdpaConfig`     | `/schemas/PciDeviceInfo` | The VM is booted
105Add vsock device to the VM         | `/vm.add-vsock`      | `/schemas/VsockConfig`    | `/schemas/PciDeviceInfo` | The VM is booted
106Remove device from the VM          | `/vm.remove-device`  | `/schemas/VmRemoveDevice` | N/A                      | The VM is booted
107Dump the VM counters               | `/vm.counters`       | N/A                       | `/schemas/VmCounters`    | The VM is booted
108
109### REST API Examples
110
111For the following set of examples, we assume Cloud Hypervisor is started with
112the REST API available at `/tmp/cloud-hypervisor.sock`:
113
114```
115$ ./target/debug/cloud-hypervisor --api-socket /tmp/cloud-hypervisor.sock
116Cloud Hypervisor Guest
117    API server: /tmp/cloud-hypervisor.sock
118    vCPUs: 1
119    Memory: 512 MB
120    Kernel: None
121    Kernel cmdline:
122    Disk(s): None
123```
124
125#### Create a Virtual Machine
126
127We want to create a virtual machine with the following characteristics:
128
129* 4 vCPUs
130* 1 GB of RAM
131* 1 virtio based networking interface
132* Direct kernel boot from a custom 5.6.0-rc4 Linux kernel located at
133  `/opt/clh/kernel/vmlinux-virtio-fs-virtio-iommu`
134* Using a Ubuntu image as its root filesystem, located at
135  `/opt/clh/images/focal-server-cloudimg-amd64.raw`
136
137```shell
138#!/bin/bash
139
140curl --unix-socket /tmp/cloud-hypervisor.sock -i \
141     -X PUT 'http://localhost/api/v1/vm.create'  \
142     -H 'Accept: application/json'               \
143     -H 'Content-Type: application/json'         \
144     -d '{
145         "cpus":{"boot_vcpus": 4, "max_vcpus": 4},
146         "kernel":{"path":"/opt/clh/kernel/vmlinux-virtio-fs-virtio-iommu"},
147         "cmdline":{"args":"console=ttyS0 console=hvc0 root=/dev/vda1 rw"},
148         "disks":[{"path":"/opt/clh/images/focal-server-cloudimg-amd64.raw"}],
149         "rng":{"src":"/dev/urandom"},
150         "net":[{"ip":"192.168.10.10", "mask":"255.255.255.0", "mac":"12:34:56:78:90:01"}]
151         }'
152```
153
154#### Boot a Virtual Machine
155
156Once the VM is created, we can boot it:
157
158```shell
159#!/bin/bash
160
161curl --unix-socket /tmp/cloud-hypervisor.sock -i -X PUT 'http://localhost/api/v1/vm.boot'
162```
163
164#### Dump a Virtual Machine Information
165
166We can fetch information about any VM, as soon as it's created:
167
168```shell
169#!/bin/bash
170
171curl --unix-socket /tmp/cloud-hypervisor.sock -i \
172     -X GET 'http://localhost/api/v1/vm.info' \
173     -H 'Accept: application/json'
174```
175
176#### Reboot a Virtual Machine
177
178We can reboot a VM that's already booted:
179
180```shell
181#!/bin/bash
182
183curl --unix-socket /tmp/cloud-hypervisor.sock -i -X PUT 'http://localhost/api/v1/vm.reboot'
184```
185
186#### Shut a Virtual Machine Down
187
188Once booted, we can shut a VM down from the REST API:
189
190```shell
191#!/bin/bash
192
193curl --unix-socket /tmp/cloud-hypervisor.sock -i -X PUT 'http://localhost/api/v1/vm.shutdown'
194```
195
196### Command Line Interface
197
198The Cloud Hypervisor Command Line Interface (CLI) can only be used for launching
199the Cloud Hypervisor binary, i.e. it can not be used for controlling the VMM or
200the launched VM once they're up and running.
201
202If you want to inspect the VMM, or control the VM after launching Cloud
203Hypervisor from the CLI, you must use the [REST API](#rest-api).
204
205From the CLI, one can either:
206
2071. Create and boot a complete virtual machine by using the CLI options to build
208   the VM config. Run `cloud-hypervisor --help` for a complete list of CLI
209   options. As soon as the `cloud-hypervisor` binary is launched, the
210   [REST API](#rest-api) is available for controlling and managing the VM.
2111. Start the [REST API](#rest-api) server only, by not passing any VM
212   configuration options. The VM can then be asynchronously created and booted
213   by sending HTTP commands to the [REST API](#rest-api). Check the
214   [REST API examples](#rest-api-examples) section for more details.
215
216### REST API and CLI Architectural Relationship
217
218The REST API and the CLI both rely on a common, [internal API](#internal-api).
219
220The CLI options are parsed by the
221[clap crate](https://docs.rs/clap/2.33.0/clap/) and then translated into
222[internal API](#internal-api) commands.
223
224The REST API is processed by an HTTP thread using the
225[Firecracker's `micro_http`](https://github.com/firecracker-microvm/firecracker/tree/master/src/micro_http)
226crate. As with the CLI, the HTTP requests eventually get translated into
227[internal API](#internal-api) commands.
228
229As a summary, the REST API and the CLI are essentially frontends for the
230[internal API](#internal-api):
231
232```
233                                  +------------------+
234                        REST API  |                  |
235                       +--------->+    micro_http    +--------+
236                       |          |                  |        |
237                       |          +------------------+        |
238                       |                                      |      +------------------------+
239                       |                                      |      |                        |
240+------------+         |                                      |      |                        |
241|            |         |                                      |      | +--------------+       |
242|    User    +---------+                                      +------> | Internal API |       |
243|            |         |                                      |      | +--------------+       |
244+------------+         |                                      |      |                        |
245                       |                                      |      |                        |
246                       |                                      |      +------------------------+
247                       |            +----------+              |                 VMM
248                       |     CLI    |          |              |
249                       +----------->+   clap   +--------------+
250                                    |          |
251                                    +----------+
252
253
254```
255
256## Internal API
257
258The Cloud Hypervisor internal API, as its name suggests, is used internally
259by the different Cloud Hypervisor threads (VMM, HTTP, control loop, etc) to
260send commands and responses to each others.
261
262It is based on [rust's Multi-Producer, Single-Consumer (MPSC)](https://doc.rust-lang.org/std/sync/mpsc/),
263and the single consumer (a.k.a. the API receiver) is the Cloud Hypervisor
264control loop.
265
266API producers are the HTTP thread handling the [REST API](#rest-api) and the
267main thread that initially parses the [CLI](#command-line-interface).
268
269### Goals and Design
270
271The internal API is designed for controlling, managing and inspecting a Cloud
272Hypervisor VMM and its guest. It is a backend for handling external, user
273visible requests through either the [REST API](#rest-api) or the
274[CLI](#command-line-interface) interfaces.
275
276The API follows a command-response scheme that closely maps the [REST API](#rest-api).
277Any command must be replied to with a response.
278
279Commands are [MPSC](https://doc.rust-lang.org/std/sync/mpsc/) based messages and
280are received and processed by the VMM control loop.
281
282In order for the VMM control loop to respond to any internal API command, it
283must be able to send a response back to the MPSC sender. For that purpose, all
284internal API command payload carry the [Sender](https://doc.rust-lang.org/std/sync/mpsc/struct.Sender.html)
285end of an [MPSC](https://doc.rust-lang.org/std/sync/mpsc/) channel.
286
287The sender of any internal API command is therefore responsible for:
288
2891. Creating an [MPSC](https://doc.rust-lang.org/std/sync/mpsc/) response
290   channel.
2911. Passing the [Sender](https://doc.rust-lang.org/std/sync/mpsc/struct.Sender.html)
292   end of the response channel as part of the internal API command payload.
2931. Waiting for the internal API command's response on the [Receiver](https://doc.rust-lang.org/std/sync/mpsc/struct.Receiver.html)
294   end of the response channel.
295
296## End to End Example
297
298In order to further understand how the external and internal Cloud Hypervisor
299APIs work together, let's look at a complete VM creation flow, from the
300[REST API](#rest-api) call, to the reply the external user will receive:
301
3021. A user or operator sends an HTTP request to the Cloud Hypervisor
303   [REST API](#rest-api) in order to creates a virtual machine:
304   ```
305   shell
306   #!/bin/bash
307
308	curl --unix-socket /tmp/cloud-hypervisor.sock -i \
309		-X PUT 'http://localhost/api/v1/vm.create'  \
310		-H 'Accept: application/json'               \
311		-H 'Content-Type: application/json'         \
312		-d '{
313			"cpus":{"boot_vcpus": 4, "max_vcpus": 4},
314			"kernel":{"path":"/opt/clh/kernel/vmlinux-virtio-fs-virtio-iommu"},
315			"cmdline":{"args":"console=ttyS0 console=hvc0 root=/dev/vda1 rw"},
316			"disks":[{"path":"/opt/clh/images/focal-server-cloudimg-amd64.raw"}],
317			"rng":{"src":"/dev/urandom"},
318			"net":[{"ip":"192.168.10.10", "mask":"255.255.255.0", "mac":"12:34:56:78:90:01"}]
319			}'
320   ```
3211. The Cloud Hypervisor HTTP thread processes the request and de-serializes the
322   HTTP request JSON body into an internal `VmConfig` structure.
3231. The Cloud Hypervisor HTTP thread creates an
324   [MPSC](https://doc.rust-lang.org/std/sync/mpsc/) channel for the internal API
325   server to send its response back.
3261. The Cloud Hypervisor HTTP thread prepares an internal API command for creating a
327   virtual machine. The command's payload is made of the de-serialized
328   `VmConfig` structure and the response channel:
329   ```Rust
330   VmCreate(Arc<Mutex<VmConfig>>, Sender<ApiResponse>)
331   ```
3321. The Cloud Hypervisor HTTP thread sends the internal API command, and waits
333   for the response:
334   ```Rust
335   // Send the VM creation request.
336    api_sender
337        .send(ApiRequest::VmCreate(config, response_sender))
338        .map_err(ApiError::RequestSend)?;
339    api_evt.write(1).map_err(ApiError::EventFdWrite)?;
340
341    response_receiver.recv().map_err(ApiError::ResponseRecv)??;
342   ```
3431. The Cloud Hypervisor control loop receives the command, as it listens on the
344   internal API [MPSC](https://doc.rust-lang.org/std/sync/mpsc/) channel:
345   ```Rust
346   // Read from the API receiver channel
347   let api_request = api_receiver.recv().map_err(Error::ApiRequestRecv)?;
348   ```
3491. The Cloud Hypervisor control loop matches the received internal API against
350   the `VmCreate` payload, and extracts both the `VmConfig` structure and the
351   [Sender](https://doc.rust-lang.org/std/sync/mpsc/struct.Sender.html) from the
352   command payload. It stores the `VmConfig` structure and replies back to the
353   sender ((The HTTP thread):
354   ```Rust
355   match api_request {
356	   ApiRequest::VmCreate(config, sender) => {
357		   // We only store the passed VM config.
358		   // The VM will be created when being asked to boot it.
359		   let response = if self.vm_config.is_none() {
360			   self.vm_config = Some(config);
361			   Ok(ApiResponsePayload::Empty)
362		   } else {
363			   Err(ApiError::VmAlreadyCreated)
364		   };
365
366	       sender.send(response).map_err(Error::ApiResponseSend)?;
367	   }
368   ```
3691. The Cloud Hypervisor HTTP thread receives the internal API command response
370   as the return value from its `VmCreate` HTTP handler. Depending on the
371   control loop internal API response, it generates the appropriate HTTP
372   response:
373   ```Rust
374   // Call vm_create()
375   match vm_create(api_notifier, api_sender, Arc::new(Mutex::new(vm_config)))
376	   .map_err(HttpError::VmCreate)
377   {
378	   Ok(_) => Response::new(Version::Http11, StatusCode::NoContent),
379	   Err(e) => error_response(e, StatusCode::InternalServerError),
380   }
381   ```
3821. The Cloud Hypervisor HTTP thread sends the formed HTTP response back to the
383   user. This is abstracted by the
384   [micro_http](https://github.com/firecracker-microvm/firecracker/tree/master/src/micro_http)
385   crate.
386