Merge pull request #970 from jodh-intel/2.0-dev-agent-ctl-add-missing-apis

tools: Make agent-ctl support more APIs
This commit is contained in:
Peng Tao 2020-10-17 10:12:40 +08:00 committed by GitHub
commit e5262b1c29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 747 additions and 13 deletions

View File

@ -257,6 +257,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "hex"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
[[package]] [[package]]
name = "humantime" name = "humantime"
version = "2.0.1" version = "2.0.1"
@ -274,7 +280,9 @@ name = "kata-agent-ctl"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"byteorder",
"clap", "clap",
"hex",
"humantime", "humantime",
"lazy_static", "lazy_static",
"libc", "libc",
@ -435,7 +443,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "059a34f111a9dee2ce1ac2826a68b24601c4298cfeb1a587c3cb493d5ab46f52" checksum = "059a34f111a9dee2ce1ac2826a68b24601c4298cfeb1a587c3cb493d5ab46f52"
dependencies = [ dependencies = [
"libc", "libc",
"nix 0.17.0", "nix 0.18.0",
] ]
[[package]] [[package]]

View File

@ -17,6 +17,8 @@ oci = { path = "../../src/agent/oci" }
clap = "2.33.0" clap = "2.33.0"
lazy_static = "1.4.0" lazy_static = "1.4.0"
anyhow = "1.0.31" anyhow = "1.0.31"
hex = "0.4.2"
byteorder = "1.3.4"
logging = { path = "../../pkg/logging" } logging = { path = "../../pkg/logging" }
slog = "2.5.2" slog = "2.5.2"

View File

@ -3,6 +3,11 @@
* [Overview](#overview) * [Overview](#overview)
* [Audience and environment](#audience-and-environment) * [Audience and environment](#audience-and-environment)
* [Full details](#full-details) * [Full details](#full-details)
* [Code summary](#code-summary)
* [Running the tool](#running-the-tool)
* [Prerequisites](#prerequisites)
* [Connect to a real Kata Container](#connect-to-a-real-kata-container)
* [Run the tool and the agent in the same environment](#run-the-tool-and-the-agent-in-the-same-environment)
## Overview ## Overview
@ -37,3 +42,80 @@ To see some examples, run:
```sh ```sh
$ cargo run -- examples $ cargo run -- examples
``` ```
## Code summary
The table below summarises where to look to learn more about both this tool,
the agent protocol and the client and server implementations.
| Description | File | Example RPC or function | Example summary |
|-|-|-|-|
| Protocol buffers definition of the Kata Containers Agent API protocol | [`agent.proto`](../../src/agent/protocols/protos/agent.proto) | `CreateContainer` | API to create a Kata container. |
| Agent Control (client) API calls | [`src/client.rs`](src/client.rs) | `agent_cmd_container_create()` | Agent Control tool function that calls the `CreateContainer` API. |
| Agent (server) API implementations | [`rpc.rs`](../../src/agent/src/rpc.rs) | `create_container()` | Server function that implements the `CreateContainers` API. |
## Running the tool
### Prerequisites
It is necessary to create an OCI bundle to use the tool. The simplest method
is:
```sh
$ bundle_dir="bundle"
$ rootfs_dir="$bundle_dir/rootfs"
$ image="busybox"
$ mkdir -p "$rootfs_dir" && (cd "$bundle_dir" && runc spec)
$ sudo docker export $(sudo docker create "$image") | tar -C "$rootfs_dir" -xvf -
```
### Connect to a real Kata Container
1. Start a Kata Container
1. Establish the VSOCK guest CID number for the virtual machine:
Assuming you are running a single QEMU based Kata Container, you can look
at the program arguments to find the (randomly-generated) `guest-cid=` option
value:
```sh
$ guest_cid=$(ps -ef | grep qemu-system-x86_64 | egrep -o "guest-cid=[^,][^,]*" | cut -d= -f2)
```
1. Run the tool to connect to the agent:
```sh
$ cargo run -- -l debug connect --bundle-dir "${bundle_dir}" --server-address "vsock://${guest_cid}:1024" -c Check -c GetGuestDetails
```
This examples makes two API calls:
- It runs `Check` to see if the agent's RPC server is serving.
- It then runs `GetGuestDetails` to establish some details of the
environment the agent is running in.
### Run the tool and the agent in the same environment
> **Warnings:**
>
> - This method is **only** for testing and development!
> - Only continue if you are using a non-critical system
> (such as a freshly installed VM environment).
1. Start the agent, specifying a local socket for it to communicate on:
```sh
$ sudo KATA_AGENT_SERVER_ADDR=unix:///tmp/foo.socket target/x86_64-unknown-linux-musl/release/kata-agent
```
1. Run the tool in the same environment:
```sh
$ cargo run -- -l debug connect --server-address "unix://@/tmp/foo.socket" --bundle-dir "$bundle_dir" -c Check -c GetGuestDetails
```
> **Note:**
>
> The `@` in the server address is required - it denotes an abstract
> socket which the agent requires (see `unix(7)`).

View File

@ -8,6 +8,7 @@
use crate::types::{Config, Options}; use crate::types::{Config, Options};
use crate::utils; use crate::utils;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use byteorder::ByteOrder;
use nix::sys::socket::{connect, socket, AddressFamily, SockAddr, SockFlag, SockType, UnixAddr}; use nix::sys::socket::{connect, socket, AddressFamily, SockAddr, SockFlag, SockType, UnixAddr};
use protocols::agent::*; use protocols::agent::*;
use protocols::agent_ttrpc::*; use protocols::agent_ttrpc::*;
@ -75,6 +76,11 @@ const DEFAULT_PS_FORMAT: &str = "json";
const ERR_API_FAILED: &str = "API failed"; const ERR_API_FAILED: &str = "API failed";
static AGENT_CMDS: &'static [AgentCmd] = &[ static AGENT_CMDS: &'static [AgentCmd] = &[
AgentCmd {
name: "AddARPNeighbors",
st: ServiceType::Agent,
fp: agent_cmd_sandbox_add_arp_neighbors,
},
AgentCmd { AgentCmd {
name: "Check", name: "Check",
st: ServiceType::Health, st: ServiceType::Health,
@ -85,6 +91,16 @@ static AGENT_CMDS: &'static [AgentCmd] = &[
st: ServiceType::Health, st: ServiceType::Health,
fp: agent_cmd_health_version, fp: agent_cmd_health_version,
}, },
AgentCmd {
name: "CloseStdin",
st: ServiceType::Agent,
fp: agent_cmd_container_close_stdin,
},
AgentCmd {
name: "CopyFile",
st: ServiceType::Agent,
fp: agent_cmd_sandbox_copy_file,
},
AgentCmd { AgentCmd {
name: "CreateContainer", name: "CreateContainer",
st: ServiceType::Agent, st: ServiceType::Agent,
@ -106,9 +122,19 @@ static AGENT_CMDS: &'static [AgentCmd] = &[
fp: agent_cmd_container_exec, fp: agent_cmd_container_exec,
}, },
AgentCmd { AgentCmd {
name: "GuestDetails", name: "GetGuestDetails",
st: ServiceType::Agent, st: ServiceType::Agent,
fp: agent_cmd_sandbox_guest_details, fp: agent_cmd_sandbox_get_guest_details,
},
AgentCmd {
name: "GetMetrics",
st: ServiceType::Agent,
fp: agent_cmd_sandbox_get_metrics,
},
AgentCmd {
name: "GetOOMEvent",
st: ServiceType::Agent,
fp: agent_cmd_sandbox_get_oom_event,
}, },
AgentCmd { AgentCmd {
name: "ListInterfaces", name: "ListInterfaces",
@ -125,11 +151,36 @@ static AGENT_CMDS: &'static [AgentCmd] = &[
st: ServiceType::Agent, st: ServiceType::Agent,
fp: agent_cmd_container_list_processes, fp: agent_cmd_container_list_processes,
}, },
AgentCmd {
name: "MemHotplugByProbe",
st: ServiceType::Agent,
fp: agent_cmd_sandbox_mem_hotplug_by_probe,
},
AgentCmd {
name: "OnlineCPUMem",
st: ServiceType::Agent,
fp: agent_cmd_sandbox_online_cpu_mem,
},
AgentCmd { AgentCmd {
name: "PauseContainer", name: "PauseContainer",
st: ServiceType::Agent, st: ServiceType::Agent,
fp: agent_cmd_container_pause, fp: agent_cmd_container_pause,
}, },
AgentCmd {
name: "ReadStderr",
st: ServiceType::Agent,
fp: agent_cmd_container_read_stderr,
},
AgentCmd {
name: "ReadStdout",
st: ServiceType::Agent,
fp: agent_cmd_container_read_stdout,
},
AgentCmd {
name: "ReseedRandomDev",
st: ServiceType::Agent,
fp: agent_cmd_sandbox_reseed_random_dev,
},
AgentCmd { AgentCmd {
name: "RemoveContainer", name: "RemoveContainer",
st: ServiceType::Agent, st: ServiceType::Agent,
@ -140,6 +191,11 @@ static AGENT_CMDS: &'static [AgentCmd] = &[
st: ServiceType::Agent, st: ServiceType::Agent,
fp: agent_cmd_container_resume, fp: agent_cmd_container_resume,
}, },
AgentCmd {
name: "SetGuestDateTime",
st: ServiceType::Agent,
fp: agent_cmd_sandbox_set_guest_date_time,
},
AgentCmd { AgentCmd {
name: "SignalProcess", name: "SignalProcess",
st: ServiceType::Agent, st: ServiceType::Agent,
@ -165,6 +221,16 @@ static AGENT_CMDS: &'static [AgentCmd] = &[
st: ServiceType::Agent, st: ServiceType::Agent,
fp: agent_cmd_sandbox_tracing_stop, fp: agent_cmd_sandbox_tracing_stop,
}, },
AgentCmd {
name: "TtyWinResize",
st: ServiceType::Agent,
fp: agent_cmd_container_tty_win_resize,
},
AgentCmd {
name: "UpdateContainer",
st: ServiceType::Agent,
fp: agent_cmd_sandbox_update_container,
},
AgentCmd { AgentCmd {
name: "UpdateInterface", name: "UpdateInterface",
st: ServiceType::Agent, st: ServiceType::Agent,
@ -180,6 +246,11 @@ static AGENT_CMDS: &'static [AgentCmd] = &[
st: ServiceType::Agent, st: ServiceType::Agent,
fp: agent_cmd_container_wait_process, fp: agent_cmd_container_wait_process,
}, },
AgentCmd {
name: "WriteStdin",
st: ServiceType::Agent,
fp: agent_cmd_container_write_stdin,
},
]; ];
static BUILTIN_CMDS: &'static [BuiltinCmd] = &[ static BUILTIN_CMDS: &'static [BuiltinCmd] = &[
@ -684,6 +755,8 @@ fn agent_cmd_health_check(
// value unused // value unused
req.set_service("".to_string()); req.set_service("".to_string());
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = health let reply = health
.check(&req, cfg.timeout_nano) .check(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?; .map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
@ -707,6 +780,8 @@ fn agent_cmd_health_version(
// value unused // value unused
req.set_service("".to_string()); req.set_service("".to_string());
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = health let reply = health
.version(&req, cfg.timeout_nano) .version(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?; .map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
@ -729,6 +804,8 @@ fn agent_cmd_sandbox_create(
let sid = utils::get_option("sid", options, args); let sid = utils::get_option("sid", options, args);
req.set_sandbox_id(sid); req.set_sandbox_id(sid);
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client let reply = client
.create_sandbox(&req, cfg.timeout_nano) .create_sandbox(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?; .map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
@ -748,6 +825,8 @@ fn agent_cmd_sandbox_destroy(
) -> Result<()> { ) -> Result<()> {
let req = DestroySandboxRequest::default(); let req = DestroySandboxRequest::default();
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client let reply = client
.destroy_sandbox(&req, cfg.timeout_nano) .destroy_sandbox(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?; .map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
@ -778,6 +857,8 @@ fn agent_cmd_container_create(
req.set_exec_id(exec_id); req.set_exec_id(exec_id);
req.set_OCI(grpc_spec); req.set_OCI(grpc_spec);
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client let reply = client
.create_container(&req, cfg.timeout_nano) .create_container(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?; .map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
@ -801,6 +882,8 @@ fn agent_cmd_container_remove(
req.set_container_id(cid); req.set_container_id(cid);
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client let reply = client
.remove_container(&req, cfg.timeout_nano) .remove_container(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?; .map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
@ -838,6 +921,8 @@ fn agent_cmd_container_exec(
req.set_exec_id(exec_id); req.set_exec_id(exec_id);
req.set_process(process); req.set_process(process);
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client let reply = client
.exec_process(&req, cfg.timeout_nano) .exec_process(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?; .map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
@ -861,6 +946,8 @@ fn agent_cmd_container_stats(
req.set_container_id(cid); req.set_container_id(cid);
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client let reply = client
.stats_container(&req, cfg.timeout_nano) .stats_container(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?; .map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
@ -884,6 +971,8 @@ fn agent_cmd_container_pause(
req.set_container_id(cid); req.set_container_id(cid);
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client let reply = client
.pause_container(&req, cfg.timeout_nano) .pause_container(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?; .map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
@ -907,6 +996,8 @@ fn agent_cmd_container_resume(
req.set_container_id(cid); req.set_container_id(cid);
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client let reply = client
.resume_container(&req, cfg.timeout_nano) .resume_container(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?; .map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
@ -930,6 +1021,8 @@ fn agent_cmd_container_start(
req.set_container_id(cid); req.set_container_id(cid);
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client let reply = client
.start_container(&req, cfg.timeout_nano) .start_container(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?; .map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
@ -940,7 +1033,7 @@ fn agent_cmd_container_start(
Ok(()) Ok(())
} }
fn agent_cmd_sandbox_guest_details( fn agent_cmd_sandbox_get_guest_details(
cfg: &Config, cfg: &Config,
client: &AgentServiceClient, client: &AgentServiceClient,
_health: &HealthClient, _health: &HealthClient,
@ -951,6 +1044,8 @@ fn agent_cmd_sandbox_guest_details(
req.set_mem_block_size(true); req.set_mem_block_size(true);
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client let reply = client
.get_guest_details(&req, cfg.timeout_nano) .get_guest_details(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?; .map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
@ -981,6 +1076,8 @@ fn agent_cmd_container_list_processes(
req.set_container_id(cid); req.set_container_id(cid);
req.set_format(list_format); req.set_format(list_format);
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client let reply = client
.list_processes(&req, cfg.timeout_nano) .list_processes(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?; .map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
@ -1006,6 +1103,8 @@ fn agent_cmd_container_wait_process(
req.set_container_id(cid); req.set_container_id(cid);
req.set_exec_id(exec_id); req.set_exec_id(exec_id);
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client let reply = client
.wait_process(&req, cfg.timeout_nano) .wait_process(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?; .map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
@ -1041,6 +1140,8 @@ fn agent_cmd_container_signal_process(
req.set_exec_id(exec_id); req.set_exec_id(exec_id);
req.set_signal(signum as u32); req.set_signal(signum as u32);
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client let reply = client
.signal_process(&req, cfg.timeout_nano) .signal_process(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?; .map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
@ -1060,6 +1161,8 @@ fn agent_cmd_sandbox_tracing_start(
) -> Result<()> { ) -> Result<()> {
let req = StartTracingRequest::default(); let req = StartTracingRequest::default();
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client let reply = client
.start_tracing(&req, cfg.timeout_nano) .start_tracing(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?; .map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
@ -1079,6 +1182,8 @@ fn agent_cmd_sandbox_tracing_stop(
) -> Result<()> { ) -> Result<()> {
let req = StopTracingRequest::default(); let req = StopTracingRequest::default();
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client let reply = client
.stop_tracing(&req, cfg.timeout_nano) .stop_tracing(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?; .map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
@ -1098,6 +1203,7 @@ fn agent_cmd_sandbox_update_interface(
) -> Result<()> { ) -> Result<()> {
let req = UpdateInterfaceRequest::default(); let req = UpdateInterfaceRequest::default();
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client let reply = client
.update_interface(&req, cfg.timeout_nano) .update_interface(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?; .map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
@ -1105,9 +1211,6 @@ fn agent_cmd_sandbox_update_interface(
// FIXME: Implement 'UpdateInterface' fully. // FIXME: Implement 'UpdateInterface' fully.
eprintln!("FIXME: 'UpdateInterface' not fully implemented"); eprintln!("FIXME: 'UpdateInterface' not fully implemented");
// let if = ...;
// req.set_interface(if);
info!(sl!(), "response received"; info!(sl!(), "response received";
"response" => format!("{:?}", reply)); "response" => format!("{:?}", reply));
@ -1123,6 +1226,8 @@ fn agent_cmd_sandbox_update_routes(
) -> Result<()> { ) -> Result<()> {
let req = UpdateRoutesRequest::default(); let req = UpdateRoutesRequest::default();
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client let reply = client
.update_routes(&req, cfg.timeout_nano) .update_routes(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?; .map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
@ -1130,9 +1235,6 @@ fn agent_cmd_sandbox_update_routes(
// FIXME: Implement 'UpdateRoutes' fully. // FIXME: Implement 'UpdateRoutes' fully.
eprintln!("FIXME: 'UpdateRoutes' not fully implemented"); eprintln!("FIXME: 'UpdateRoutes' not fully implemented");
// let routes = ...;
// req.set_routes(routes);
info!(sl!(), "response received"; info!(sl!(), "response received";
"response" => format!("{:?}", reply)); "response" => format!("{:?}", reply));
@ -1148,6 +1250,8 @@ fn agent_cmd_sandbox_list_interfaces(
) -> Result<()> { ) -> Result<()> {
let req = ListInterfacesRequest::default(); let req = ListInterfacesRequest::default();
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client let reply = client
.list_interfaces(&req, cfg.timeout_nano) .list_interfaces(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?; .map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
@ -1167,6 +1271,8 @@ fn agent_cmd_sandbox_list_routes(
) -> Result<()> { ) -> Result<()> {
let req = ListRoutesRequest::default(); let req = ListRoutesRequest::default();
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client let reply = client
.list_routes(&req, cfg.timeout_nano) .list_routes(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?; .map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
@ -1177,9 +1283,531 @@ fn agent_cmd_sandbox_list_routes(
Ok(()) Ok(())
} }
fn agent_cmd_container_tty_win_resize(
cfg: &Config,
client: &AgentServiceClient,
_health: &HealthClient,
options: &mut Options,
args: &str,
) -> Result<()> {
let mut req = TtyWinResizeRequest::default();
let cid = utils::get_option("cid", options, args);
let exec_id = utils::get_option("exec_id", options, args);
req.set_container_id(cid);
req.set_exec_id(exec_id);
let rows_str = utils::get_option("row", options, args);
if rows_str != "" {
let rows = rows_str
.parse::<u32>()
.map_err(|e| anyhow!(e).context("invalid row size"))?;
req.set_row(rows);
}
let cols_str = utils::get_option("column", options, args);
if cols_str != "" {
let cols = cols_str
.parse::<u32>()
.map_err(|e| anyhow!(e).context("invalid column size"))?;
req.set_column(cols);
}
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client
.tty_win_resize(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
info!(sl!(), "response received";
"response" => format!("{:?}", reply));
Ok(())
}
fn agent_cmd_container_close_stdin(
cfg: &Config,
client: &AgentServiceClient,
_health: &HealthClient,
options: &mut Options,
args: &str,
) -> Result<()> {
let mut req = CloseStdinRequest::default();
let cid = utils::get_option("cid", options, args);
let exec_id = utils::get_option("exec_id", options, args);
req.set_container_id(cid);
req.set_exec_id(exec_id);
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client
.close_stdin(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
info!(sl!(), "response received";
"response" => format!("{:?}", reply));
Ok(())
}
fn agent_cmd_container_read_stdout(
cfg: &Config,
client: &AgentServiceClient,
_health: &HealthClient,
options: &mut Options,
args: &str,
) -> Result<()> {
let mut req = ReadStreamRequest::default();
let cid = utils::get_option("cid", options, args);
let exec_id = utils::get_option("exec_id", options, args);
req.set_container_id(cid);
req.set_exec_id(exec_id);
let length_str = utils::get_option("len", options, args);
if length_str != "" {
let length = length_str
.parse::<u32>()
.map_err(|e| anyhow!(e).context("invalid length"))?;
req.set_len(length);
}
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client
.read_stdout(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
info!(sl!(), "response received";
"response" => format!("{:?}", reply));
Ok(())
}
fn agent_cmd_container_read_stderr(
cfg: &Config,
client: &AgentServiceClient,
_health: &HealthClient,
options: &mut Options,
args: &str,
) -> Result<()> {
let mut req = ReadStreamRequest::default();
let cid = utils::get_option("cid", options, args);
let exec_id = utils::get_option("exec_id", options, args);
req.set_container_id(cid);
req.set_exec_id(exec_id);
let length_str = utils::get_option("len", options, args);
if length_str != "" {
let length = length_str
.parse::<u32>()
.map_err(|e| anyhow!(e).context("invalid length"))?;
req.set_len(length);
}
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client
.read_stderr(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
info!(sl!(), "response received";
"response" => format!("{:?}", reply));
Ok(())
}
fn agent_cmd_container_write_stdin(
cfg: &Config,
client: &AgentServiceClient,
_health: &HealthClient,
options: &mut Options,
args: &str,
) -> Result<()> {
let mut req = WriteStreamRequest::default();
let cid = utils::get_option("cid", options, args);
let exec_id = utils::get_option("exec_id", options, args);
let str_data = utils::get_option("data", options, args);
let data = utils::str_to_bytes(&str_data)?;
req.set_container_id(cid);
req.set_exec_id(exec_id);
req.set_data(data.to_vec());
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client
.write_stdin(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
info!(sl!(), "response received";
"response" => format!("{:?}", reply));
Ok(())
}
fn agent_cmd_sandbox_get_metrics(
cfg: &Config,
client: &AgentServiceClient,
_health: &HealthClient,
_options: &mut Options,
_args: &str,
) -> Result<()> {
let req = GetMetricsRequest::default();
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client
.get_metrics(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
info!(sl!(), "response received";
"response" => format!("{:?}", reply));
Ok(())
}
fn agent_cmd_sandbox_get_oom_event(
cfg: &Config,
client: &AgentServiceClient,
_health: &HealthClient,
_options: &mut Options,
_args: &str,
) -> Result<()> {
let req = GetOOMEventRequest::default();
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client
.get_oom_event(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
info!(sl!(), "response received";
"response" => format!("{:?}", reply));
Ok(())
}
fn agent_cmd_sandbox_copy_file(
cfg: &Config,
client: &AgentServiceClient,
_health: &HealthClient,
options: &mut Options,
args: &str,
) -> Result<()> {
let mut req = CopyFileRequest::default();
let path = utils::get_option("path", options, args);
if path != "" {
req.set_path(path);
}
let file_size_str = utils::get_option("file_size", options, args);
if file_size_str != "" {
let file_size = file_size_str
.parse::<i64>()
.map_err(|e| anyhow!(e).context("invalid file_size"))?;
req.set_file_size(file_size);
}
let file_mode_str = utils::get_option("file_mode", options, args);
if file_mode_str != "" {
let file_mode = file_mode_str
.parse::<u32>()
.map_err(|e| anyhow!(e).context("invalid file_mode"))?;
req.set_file_mode(file_mode);
}
let dir_mode_str = utils::get_option("dir_mode", options, args);
if dir_mode_str != "" {
let dir_mode = dir_mode_str
.parse::<u32>()
.map_err(|e| anyhow!(e).context("invalid dir_mode"))?;
req.set_dir_mode(dir_mode);
}
let uid_str = utils::get_option("uid", options, args);
if uid_str != "" {
let uid = uid_str
.parse::<i32>()
.map_err(|e| anyhow!(e).context("invalid uid"))?;
req.set_uid(uid);
}
let gid_str = utils::get_option("gid", options, args);
if gid_str != "" {
let gid = gid_str
.parse::<i32>()
.map_err(|e| anyhow!(e).context("invalid gid"))?;
req.set_gid(gid);
}
let offset_str = utils::get_option("offset", options, args);
if offset_str != "" {
let offset = offset_str
.parse::<i64>()
.map_err(|e| anyhow!(e).context("invalid offset"))?;
req.set_offset(offset);
}
let data_str = utils::get_option("data", options, args);
if data_str != "" {
let data = utils::str_to_bytes(&data_str)?;
req.set_data(data.to_vec());
}
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client
.copy_file(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
info!(sl!(), "response received";
"response" => format!("{:?}", reply));
Ok(())
}
fn agent_cmd_sandbox_reseed_random_dev(
cfg: &Config,
client: &AgentServiceClient,
_health: &HealthClient,
options: &mut Options,
args: &str,
) -> Result<()> {
let mut req = ReseedRandomDevRequest::default();
let str_data = utils::get_option("data", options, args);
let data = utils::str_to_bytes(&str_data)?;
req.set_data(data.to_vec());
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client
.reseed_random_dev(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
info!(sl!(), "response received";
"response" => format!("{:?}", reply));
Ok(())
}
fn agent_cmd_sandbox_online_cpu_mem(
cfg: &Config,
client: &AgentServiceClient,
_health: &HealthClient,
options: &mut Options,
args: &str,
) -> Result<()> {
let mut req = OnlineCPUMemRequest::default();
let wait_str = utils::get_option("wait", options, args);
if wait_str != "" {
let wait = wait_str
.parse::<bool>()
.map_err(|e| anyhow!(e).context("invalid wait bool"))?;
req.set_wait(wait);
}
let nb_cpus_str = utils::get_option("nb_cpus", options, args);
if nb_cpus_str != "" {
let nb_cpus = nb_cpus_str
.parse::<u32>()
.map_err(|e| anyhow!(e).context("invalid nb_cpus value"))?;
req.set_nb_cpus(nb_cpus);
}
let cpu_only_str = utils::get_option("cpu_only", options, args);
if cpu_only_str != "" {
let cpu_only = cpu_only_str
.parse::<bool>()
.map_err(|e| anyhow!(e).context("invalid cpu_only bool"))?;
req.set_cpu_only(cpu_only);
}
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client
.online_cpu_mem(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
info!(sl!(), "response received";
"response" => format!("{:?}", reply));
Ok(())
}
fn agent_cmd_sandbox_set_guest_date_time(
cfg: &Config,
client: &AgentServiceClient,
_health: &HealthClient,
options: &mut Options,
args: &str,
) -> Result<()> {
let mut req = SetGuestDateTimeRequest::default();
let secs_str = utils::get_option("sec", options, args);
if secs_str != "" {
let secs = secs_str
.parse::<i64>()
.map_err(|e| anyhow!(e).context("invalid seconds"))?;
req.set_Sec(secs);
}
let usecs_str = utils::get_option("usec", options, args);
if usecs_str != "" {
let usecs = usecs_str
.parse::<i64>()
.map_err(|e| anyhow!(e).context("invalid useconds"))?;
req.set_Usec(usecs);
}
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client
.set_guest_date_time(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
info!(sl!(), "response received";
"response" => format!("{:?}", reply));
Ok(())
}
fn agent_cmd_sandbox_add_arp_neighbors(
cfg: &Config,
client: &AgentServiceClient,
_health: &HealthClient,
_options: &mut Options,
_args: &str,
) -> Result<()> {
let req = AddARPNeighborsRequest::default();
// FIXME: Implement fully.
eprintln!("FIXME: 'AddARPNeighbors' not fully implemented");
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client
.add_arp_neighbors(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
info!(sl!(), "response received";
"response" => format!("{:?}", reply));
Ok(())
}
fn agent_cmd_sandbox_update_container(
cfg: &Config,
client: &AgentServiceClient,
_health: &HealthClient,
options: &mut Options,
args: &str,
) -> Result<()> {
let mut req = UpdateContainerRequest::default();
let cid = utils::get_option("cid", options, args);
req.set_container_id(cid);
// FIXME: Implement fully
eprintln!("FIXME: 'UpdateContainer' not fully implemented");
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client
.update_container(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
info!(sl!(), "response received";
"response" => format!("{:?}", reply));
Ok(())
}
fn agent_cmd_sandbox_mem_hotplug_by_probe(
cfg: &Config,
client: &AgentServiceClient,
_health: &HealthClient,
options: &mut Options,
args: &str,
) -> Result<()> {
let mut req = MemHotplugByProbeRequest::default();
// Expected to be a comma separated list of hex addresses
let addr_list = utils::get_option("memHotplugProbeAddr", options, args);
if addr_list != "" {
let addrs: Vec<u64> = addr_list
// Convert into a list of string values.
.split(",")
// Convert each string element into a u8 array of bytes, ignoring
// those elements that fail the conversion.
.filter_map(|s| hex::decode(s.trim_start_matches("0x")).ok())
// "Stretch" the u8 byte slice into one of length 8
// (to allow each 8 byte chunk to be converted into a u64).
.map(|mut v| -> Vec<u8> {
v.resize(8, 0x0);
v
})
// Convert the slice of u8 bytes into a u64
.map(|b| byteorder::LittleEndian::read_u64(&b))
.collect();
req.set_memHotplugProbeAddr(addrs);
}
debug!(sl!(), "sending request"; "request" => format!("{:?}", req));
let reply = client
.mem_hotplug_by_probe(&req, cfg.timeout_nano)
.map_err(|e| anyhow!("{:?}", e).context(ERR_API_FAILED))?;
info!(sl!(), "response received";
"response" => format!("{:?}", reply));
Ok(())
}
#[inline] #[inline]
fn builtin_cmd_repeat(_cfg: &Config, _options: &mut Options, _args: &str) -> (Result<()>, bool) { fn builtin_cmd_repeat(_cfg: &Config, _options: &mut Options, _args: &str) -> (Result<()>, bool) {
// XXX: NOP implementation. Due to the way repeat has to work, providing // XXX: NOP implementation. Due to the way repeat has to work, providing a
// handler like this is "too late" to be useful. However, a handler // handler like this is "too late" to be useful. However, a handler
// is required as "repeat" is a valid command. // is required as "repeat" is a valid command.
// //

View File

@ -65,7 +65,7 @@ fn make_examples_text(program_name: &str) -> String {
- Query the agent environment: - Query the agent environment:
$ {program} connect --server-address "{vsock_server_address}" --cmd GuestDetails $ {program} connect --server-address "{vsock_server_address}" --cmd GetGuestDetails
- List all available (built-in and Kata Agent API) commands: - List all available (built-in and Kata Agent API) commands:
@ -85,7 +85,7 @@ fn make_examples_text(program_name: &str) -> String {
- Query guest details forever: - Query guest details forever:
$ {program} connect --server-address "{vsock_server_address}" --repeat -1 --cmd GuestDetails $ {program} connect --server-address "{vsock_server_address}" --repeat -1 --cmd GetGuestDetails
- Send a 'SIGUSR1' signal to a container process: - Send a 'SIGUSR1' signal to a container process:

View File

@ -408,3 +408,17 @@ pub fn get_grpc_spec(options: &mut Options, cid: &str) -> Result<grpcSpec> {
Ok(oci_to_grpc(&bundle_dir, cid, &oci_spec)?) Ok(oci_to_grpc(&bundle_dir, cid, &oci_spec)?)
} }
pub fn str_to_bytes(s: &str) -> Result<Vec<u8>> {
let prefix = "hex:";
if s.starts_with(prefix) {
let hex_str = s.trim_start_matches(prefix);
let decoded = hex::decode(hex_str).map_err(|e| anyhow!(e))?;
Ok(decoded)
} else {
Ok(s.as_bytes().to_vec())
}
}