view src/xml.rs @ 19:d37203a877d9

add xml parse/writer
author AnaTofuZ <k198584@ie.u-ryukyu.ac.jp>
date Tue, 03 Nov 2020 11:10:24 +0900
parents
children da4858f4658d
line wrap: on
line source

use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
use quick_xml::{Reader, Writer};
use rand::Rng;
use std::fs::File;
use std::io::{BufReader, BufWriter, Error, ErrorKind};
use std::path::Path;
use uuid::Uuid;

const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                        abcdefghijklmnopqrstuvwxyz\
                        0123456789)(*&^%$#@!~";

const PASSWORD_LEN: usize = 30;

const DOMAIN_XMLNS_QEMU: (&str, &str) =
    ("xmlns:qemu", "http://libvirt.org/schemas/domain/qemu/1.0");

const IE_VIRSH_TEMPLATE_VM_NAME: &[u8; 17] = b"ie-virsh-template";
const VNC_XML_TAG: &[u8; 8] = b"graphics";

const ROOT_START_TAG: &[u8; 6] = b"domain";

const QEMU_COMMAND_LINE_TAG: &[u8; 16] = b"qemu:commandline";
const QEMU_ARG_TAG: &[u8; 8] = b"qemu:arg";

pub fn generate() -> Result<(), Error> {
    let is_debug = true;

    let file = "./template.xml";
    let mut reader = Reader::from_reader(BufReader::new(get_xml_from_file(file)?));
    let mut writer = Writer::new(BufWriter::new(File::create("dump.xml").unwrap()));
    let mut buf = Vec::new();
    loop {
        match reader.read_event(&mut buf) {
            Ok(Event::Start(ref e)) if e.name() == b"uuid" => {
                writer
                    .write_event(Event::Start(e.clone()))
                    .expect("faild write event");
                reader
                    .read_event(&mut Vec::new())
                    .expect("faild read event");
                let vm_uuid = Uuid::new_v4().to_string();
                let elem = BytesText::from_plain_str(&vm_uuid);
                writer.write_event(Event::Text(elem)).unwrap();
            }

            Ok(Event::Start(ref e)) if (e.name() == ROOT_START_TAG && is_debug) => {
                let mut elem = e.clone();
                elem.push_attribute(DOMAIN_XMLNS_QEMU);
                writer.write_event(Event::Start(elem)).unwrap();

                let qemu_command_line_start = BytesStart::borrowed_name(QEMU_COMMAND_LINE_TAG);
                writer
                    .write_event(Event::Start(qemu_command_line_start))
                    .unwrap();

                for value in ["-S", "-gdb", "tcp"].iter() {
                    let mut qemu_elem = BytesStart::borrowed_name(QEMU_ARG_TAG);
                    let v: &str = &value;
                    qemu_elem.push_attribute(("value", v));
                    writer.write_event(Event::Empty(qemu_elem)).unwrap();
                }

                let qemu_command_line_end = BytesEnd::borrowed(QEMU_COMMAND_LINE_TAG);
                writer
                    .write_event(Event::End(qemu_command_line_end))
                    .unwrap();
            }

            Ok(Event::Empty(ref e)) if e.name() == VNC_XML_TAG => {
                let mut elem = e.clone();
                let pw: &str = &generate_pw();
                elem.push_attribute(("passwd", pw));
                writer.write_event(Event::Empty(elem)).ok();
            }

            Ok(Event::Empty(ref e)) if (e.name() == b"source") => {
                let mut elem = e.clone();
                let is_qcow_file = elem
                    .attributes()
                    .find(|attr| attr.as_ref().unwrap().key == b"file");
                if is_qcow_file.is_some() {
                    elem.clear_attributes();
                    elem.push_attribute(("file", "qcow"));
                }
                writer.write_event(Event::Empty(elem)).ok();
            }

            Ok(Event::Text(ref e)) if e.escaped() == IE_VIRSH_TEMPLATE_VM_NAME => {
                let elem = BytesText::from_plain_str("anatofuz-vm");
                writer.write_event(Event::Text(elem)).unwrap();
            }
            Ok(Event::Eof) => break,
            // you can use either `e` or `&e` if you don't want to move the event
            Ok(e) => assert!(writer.write_event(&e).is_ok()),
            Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
        }
        buf.clear();
    }
    Ok(())
}

fn get_xml_from_file(source: &str) -> Result<File, Error> {
    let local_path = Path::new(source);

    if local_path.is_file() {
        let file = File::open(local_path)?;
        return Ok(file);
    }

    Err(Error::new(ErrorKind::Other, "failed ..."))
}

fn generate_pw() -> String {
    let mut rng = rand::thread_rng();

    (0..PASSWORD_LEN)
        .map(|_| {
            let idx = rng.gen_range(0, CHARSET.len());
            CHARSET[idx] as char
        })
        .collect()
}