Merge pull request #4305 from yibozhuang/stable-2.4

stable-2.4 | Backport fixes for direct-volume stats
This commit is contained in:
Yibo Zhuang 2022-05-26 13:52:19 -07:00 committed by GitHub
commit fc2c933a88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 109 additions and 41 deletions

View File

@ -40,13 +40,11 @@ use rustjail::specconv::CreateOpts;
use nix::errno::Errno;
use nix::mount::MsFlags;
use nix::sys::stat;
use nix::sys::{stat, statfs};
use nix::unistd::{self, Pid};
use rustjail::cgroups::Manager;
use rustjail::process::ProcessOperations;
use sysinfo::{DiskExt, System, SystemExt};
use crate::device::{
add_devices, get_virtio_blk_pci_device_name, update_device_cgroup, update_env_pci,
};
@ -71,7 +69,6 @@ use tracing::instrument;
use libc::{self, c_char, c_ushort, pid_t, winsize, TIOCSWINSZ};
use std::fs;
use std::os::unix::fs::MetadataExt;
use std::os::unix::prelude::PermissionsExt;
use std::process::{Command, Stdio};
use std::time::Duration;
@ -1452,20 +1449,12 @@ fn get_memory_info(block_size: bool, hotplug: bool) -> Result<(u64, bool)> {
fn get_volume_capacity_stats(path: &str) -> Result<VolumeUsage> {
let mut usage = VolumeUsage::new();
let s = System::new();
for disk in s.disks() {
if let Some(v) = disk.name().to_str() {
if v.to_string().eq(path) {
usage.available = disk.available_space();
usage.total = disk.total_space();
usage.used = usage.total - usage.available;
usage.unit = VolumeUsage_Unit::BYTES; // bytes
break;
}
} else {
return Err(anyhow!(nix::Error::EINVAL));
}
}
let stat = statfs::statfs(path)?;
let block_size = stat.block_size() as u64;
usage.total = stat.blocks() * block_size;
usage.available = stat.blocks_free() * block_size;
usage.used = usage.total - usage.available;
usage.unit = VolumeUsage_Unit::BYTES;
Ok(usage)
}
@ -1473,20 +1462,11 @@ fn get_volume_capacity_stats(path: &str) -> Result<VolumeUsage> {
fn get_volume_inode_stats(path: &str) -> Result<VolumeUsage> {
let mut usage = VolumeUsage::new();
let s = System::new();
for disk in s.disks() {
if let Some(v) = disk.name().to_str() {
if v.to_string().eq(path) {
let meta = fs::metadata(disk.mount_point())?;
let inode = meta.ino();
usage.used = inode;
usage.unit = VolumeUsage_Unit::INODES;
break;
}
} else {
return Err(anyhow!(nix::Error::EINVAL));
}
}
let stat = statfs::statfs(path)?;
usage.total = stat.files();
usage.available = stat.files_free();
usage.used = usage.total - usage.available;
usage.unit = VolumeUsage_Unit::INODES;
Ok(usage)
}
@ -1866,7 +1846,8 @@ fn load_kernel_module(module: &protocols::agent::KernelModule) -> Result<()> {
#[cfg(test)]
mod tests {
use super::*;
use crate::protocols::agent_ttrpc::AgentService as _;
use crate::{protocols::agent_ttrpc::AgentService as _, skip_if_not_root};
use nix::mount;
use oci::{Hook, Hooks};
use ttrpc::{r#async::TtrpcContext, MessageHeader};
@ -2197,4 +2178,66 @@ mod tests {
}
}
}
#[tokio::test]
async fn test_volume_capacity_stats() {
skip_if_not_root!();
// Verify error if path does not exist
assert!(get_volume_capacity_stats("/does-not-exist").is_err());
// Create a new tmpfs mount, and verify the initial values
let mount_dir = tempfile::tempdir().unwrap();
mount::mount(
Some("tmpfs"),
mount_dir.path().to_str().unwrap(),
Some("tmpfs"),
mount::MsFlags::empty(),
None::<&str>,
)
.unwrap();
let mut stats = get_volume_capacity_stats(mount_dir.path().to_str().unwrap()).unwrap();
assert_eq!(stats.used, 0);
assert_ne!(stats.available, 0);
let available = stats.available;
// Verify that writing a file will result in increased utilization
fs::write(mount_dir.path().join("file.dat"), "foobar").unwrap();
stats = get_volume_capacity_stats(mount_dir.path().to_str().unwrap()).unwrap();
assert_eq!(stats.used, 4 * 1024);
assert_eq!(stats.available, available - 4 * 1024);
}
#[tokio::test]
async fn test_get_volume_inode_stats() {
skip_if_not_root!();
// Verify error if path does not exist
assert!(get_volume_inode_stats("/does-not-exist").is_err());
// Create a new tmpfs mount, and verify the initial values
let mount_dir = tempfile::tempdir().unwrap();
mount::mount(
Some("tmpfs"),
mount_dir.path().to_str().unwrap(),
Some("tmpfs"),
mount::MsFlags::empty(),
None::<&str>,
)
.unwrap();
let mut stats = get_volume_inode_stats(mount_dir.path().to_str().unwrap()).unwrap();
assert_eq!(stats.used, 1);
assert_ne!(stats.available, 0);
let available = stats.available;
// Verify that creating a directory and writing a file will result in increased utilization
let dir = mount_dir.path().join("foobar");
fs::create_dir_all(&dir).unwrap();
fs::write(dir.as_path().join("file.dat"), "foobar").unwrap();
stats = get_volume_inode_stats(mount_dir.path().to_str().unwrap()).unwrap();
assert_eq!(stats.used, 3);
assert_eq!(stats.available, available - 2);
}
}

View File

@ -7,10 +7,11 @@ package main
import (
"encoding/json"
"fmt"
"net/url"
containerdshim "github.com/kata-containers/kata-containers/src/runtime/pkg/containerd-shim-v2"
"github.com/kata-containers/kata-containers/src/runtime/pkg/direct-volume"
volume "github.com/kata-containers/kata-containers/src/runtime/pkg/direct-volume"
"github.com/kata-containers/kata-containers/src/runtime/pkg/utils/shimclient"
"github.com/urfave/cli"
@ -89,12 +90,14 @@ var statsCommand = cli.Command{
Destination: &volumePath,
},
},
Action: func(c *cli.Context) (string, error) {
Action: func(c *cli.Context) error {
stats, err := Stats(volumePath)
if err != nil {
return "", cli.NewExitError(err.Error(), 1)
return cli.NewExitError(err.Error(), 1)
}
return string(stats), nil
fmt.Println(string(stats))
return nil
},
}
@ -127,8 +130,14 @@ func Stats(volumePath string) ([]byte, error) {
if err != nil {
return nil, err
}
urlSafeDevicePath := url.PathEscape(volumePath)
body, err := shimclient.DoGet(sandboxId, defaultTimeout, containerdshim.DirectVolumeStatUrl+"/"+urlSafeDevicePath)
volumeMountInfo, err := volume.VolumeMountInfo(volumePath)
if err != nil {
return nil, err
}
urlSafeDevicePath := url.PathEscape(volumeMountInfo.Device)
body, err := shimclient.DoGet(sandboxId, defaultTimeout,
fmt.Sprintf("%s?%s=%s", containerdshim.DirectVolumeStatUrl, containerdshim.DirectVolumePathKey, urlSafeDevicePath))
if err != nil {
return nil, err
}
@ -141,8 +150,13 @@ func Resize(volumePath string, size uint64) error {
if err != nil {
return err
}
volumeMountInfo, err := volume.VolumeMountInfo(volumePath)
if err != nil {
return err
}
resizeReq := containerdshim.ResizeRequest{
VolumePath: volumePath,
VolumePath: volumeMountInfo.Device,
Size: size,
}
encoded, err := json.Marshal(resizeReq)

View File

@ -32,6 +32,8 @@ import (
)
const (
DirectVolumePathKey = "path"
DirectVolumeStatUrl = "/direct-volume/stats"
DirectVolumeResizeUrl = "/direct-volume/resize"
)
@ -139,7 +141,16 @@ func decodeAgentMetrics(body string) []*dto.MetricFamily {
}
func (s *service) serveVolumeStats(w http.ResponseWriter, r *http.Request) {
volumePath, err := url.PathUnescape(strings.TrimPrefix(r.URL.Path, DirectVolumeStatUrl))
val := r.URL.Query().Get(DirectVolumePathKey)
if val == "" {
msg := fmt.Sprintf("Required parameter %s not found", DirectVolumePathKey)
shimMgtLog.Info(msg)
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(msg))
return
}
volumePath, err := url.PathUnescape(val)
if err != nil {
shimMgtLog.WithError(err).Error("failed to unescape the volume stat url path")
w.WriteHeader(http.StatusInternalServerError)