Commit b5063acb authored by Michal Privoznik's avatar Michal Privoznik
Browse files

Materials for the seventh lecture

parent 90c3a415
Loading
Loading
Loading
Loading

07/.gitignore

0 → 100644
+2 −0
Original line number Diff line number Diff line
stats/Cargo.lock
stats/target/

07/cpu_stats.py

0 → 100755
+65 −0
Original line number Diff line number Diff line
#!/usr/bin/env python

import libvirt
import time
import getopt
import sys

def print_help(progname):
    print(f"{progname} options:\n" +
          "\n" +
          "     -c | --connect=URI          connection URI\n" +
          "     -d | --domain=NAME          domain name\n" +
          "     -s | --sleep=SECONDS        how many seconds sleep between prints\n" +
          "\n")

def parse_argv():
    options = "hc:d:s:"
    long_options = [ "help", "connect=", "domain=", "sleep=" ]
    connect = None
    domain = None
    sleep = 2

    try:
        args, values = getopt.getopt(sys.argv[1:], options, long_options)
        for currArg, currVal in args:
            if currArg in ("-h", "--help"):
                print_help(sys.argv[0])
                sys.exit(0)
            elif currArg in ("-c", "--connect"):
                connect = currVal
            elif currArg in ("-d", "--domain"):
                domain = currVal
            elif currArg in ("-s", "--sleep"):
                try:
                    sleep = int(currVal)
                except ValueError as err:
                    print(f"Invalid number '{currVal}'")
                    sys.exit(1)

    except getopt.error as err:
        print(str(err))
        sys.exit(1)

    if domain is None:
        print("At least domain name must be specified")
        sys.exit(1)

    return connect, domain, sleep

def do_top(conn_uri, dom_name, sleep_time = 2):
    conn = libvirt.open(conn_uri)
    dom = conn.lookupByName(dom_name)
    while 1:
        old = dom.getCPUStats(total=1)[0]['cpu_time']
        time.sleep(sleep_time)
        new = dom.getCPUStats(total=1)[0]['cpu_time']
        #cpu_time is in nanoseconds
        print("CPU: %f" % ((new - old) / 1E9 * 100 / sleep_time))

def main():
    c, d, s = parse_argv()
    do_top(c, d, s)

if __name__ == "__main__":
    sys.exit(main())

07/hw04.pdf

0 → 100644
+124 KiB

File added.

No diff preview for this file type.

07/stats/Cargo.toml

0 → 100644
+9 −0
Original line number Diff line number Diff line
[package]
name = "stats"
version = "0.1.0"
edition = "2021"

[dependencies]
virt = { git="https://gitlab.com/libvirt/libvirt-rust.git", rev="a92766ca23373b88e22d450c4ad915251045d693" }
clap = { version = "4.4.4", features = ["derive"] }
libxml = "0.3.3"

07/stats/src/main.rs

0 → 100644
+90 −0
Original line number Diff line number Diff line
use clap::Parser;
use libxml::xpath::Context;
use std::thread::sleep;
use std::time::Duration;
use virt::connect::Connect;
use virt::domain::Domain;

#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
    /// Connection uri
    #[arg(short = 'c', long = "connect", value_name = "URI")]
    uri: Option<String>,

    /// The domain name
    #[arg(short, long, value_name = "NAME")]
    domain: String,

    /// The domain's interface, either target name or MAC address
    #[arg(short, long, value_name = "IFACE", required_unless_present("list"))]
    interface: Option<String>,

    /// How many seconds sleep between prints
    #[arg(short, long, value_name = "SECONDS")]
    sleep: Option<u64>,

    /// List domain interfaces and exit
    #[arg(short, long)]
    list: bool,
}

fn list_dom_ifaces(dom: &Domain) -> Result<(), virt::error::Error> {
    let domxml = dom.get_xml_desc(0)?;
    let parser = libxml::parser::Parser::default();
    let domxml_doc = parser.parse_string(domxml).unwrap();
    let ctxt = Context::new(&domxml_doc).unwrap();
    let result = ctxt.evaluate("/domain/devices/interface").unwrap();

    for node in &result.get_nodes_as_vec() {
        let typ = node.get_attribute("type");
        let target = node
            .findnodes("./target/@dev")
            .unwrap_or_default()
            .first()
            .map(|x| x.get_content());
        let mac = node
            .findnodes("./mac/@address")
            .unwrap_or_default()
            .first()
            .map(|x| x.get_content());

        println!(
            "type={}\ttarget={}\tmac={}",
            typ.unwrap_or_default(),
            target.unwrap_or_default(),
            mac.unwrap_or_default()
        );
    }

    Ok(())
}

fn main() -> Result<(), virt::error::Error> {
    let cli = Args::parse();
    let mut conn = Connect::open(cli.uri.as_deref())?;
    let dom = Domain::lookup_by_name(&conn, &cli.domain)?;

    if cli.list {
        list_dom_ifaces(&dom)?;
        conn.close()?;
        return Ok(());
    }

    let iface = cli.interface.unwrap();
    let mut stats_old = Domain::interface_stats(&dom, &iface)?;
    loop {
        let how_long: u64 = cli.sleep.unwrap_or(2) * 1000;
        let divisor: i64 = how_long as i64;

        sleep(Duration::from_millis(how_long));
        let stats_new = Domain::interface_stats(&dom, &iface)?;
        println!(
            "RX: {} KiB/s\t\tTX: {} KiB/s\r",
            (stats_new.rx_bytes - stats_old.rx_bytes) / divisor,
            (stats_new.tx_bytes - stats_old.tx_bytes) / divisor
        );

        stats_old = stats_new;
    }
}