Commit 19230deb authored by Heinz N. Gies's avatar Heinz N. Gies

Create and destroy zfs datasets upon create and destroy

parent 67521ac8
{
"cpu" : 100,
"hostname": "test",
"uuid" : "fe0b9b05-1f3e-4b11-b0ae-8494bb6ecd53",
"ram" : 1024,
"disk" : 100,
"alias" : "test"
}
use std::error::Error;
use std::fs;
use std::fs::File;
use std::path::Path;
use std::path::PathBuf;
use std::str;
use std::cmp::PartialEq;
use uuid::Uuid;
......@@ -11,6 +11,7 @@ use serde_json;
use errors::{NotFoundError, ConflictError};
use config::Config;
#[derive(Debug, Serialize, Deserialize)]
pub struct JailConfig {
#[serde(default = "new_uuid")]
......@@ -23,6 +24,7 @@ pub struct JailConfig {
#[serde(default = "bfalse")]
autostart: bool,
}
fn new_uuid() -> String {
Uuid::new_v4().hyphenated().to_string()
}
......@@ -32,10 +34,10 @@ fn bfalse() -> bool {
}
#[derive(Debug, Serialize, Deserialize)]
struct IdxEntry {
pub struct IdxEntry {
version: u32,
uuid: String,
root: String,
pub root: String,
state: String,
jail_type: String,
}
......@@ -75,8 +77,9 @@ impl<'a> JDB<'a> {
/// ```
pub fn open(config: &'a Config) -> Result<Self, Box<Error>> {
let idx_file = Path::new(config.settings.conf_dir.as_str());
match File::open(idx_file.join("index")) {
let mut idx_file = PathBuf::from(config.settings.conf_dir.as_str());
idx_file.push("index");
match File::open(idx_file) {
Ok(file) => {
let index: Index = serde_json::from_reader(file)?;
Ok(JDB {
......@@ -104,25 +107,34 @@ impl<'a> JDB<'a> {
/// Inserts a config into the database, writes the config file
/// and adds it to the index.
pub fn insert(self: &'a mut JDB<'a>, config: JailConfig) -> Result<JailConfig, Box<Error>> {
pub fn insert(self: &'a mut JDB<'a>, config: JailConfig) -> Result<IdxEntry, Box<Error>> {
match self.find(&config.uuid) {
None => {
let path = Path::new(self.config.settings.conf_dir.as_str());
path.join(config.uuid.clone()).set_extension("json");
let file = File::create(&path)?;
let mut root = String::from("/jails/");
let mut path = PathBuf::from(self.config.settings.conf_dir.as_str());
path.push(config.uuid.clone());
path.set_extension("json");
let file = File::create(path)?;
let mut root = String::from(self.config.settings.pool.as_str());
root.push('/');
root.push_str(&config.uuid.clone());
let e = IdxEntry {
version: 0,
uuid: config.uuid.clone(),
state: String::from("installing"),
jail_type: String::from("base"),
root: root,
root: root.clone(),
};
self.index.entries.push(e);
self.save()?;
serde_json::to_writer(file, &config)?;
Ok(config)
// This is ugly but I don't know any better.
Ok(IdxEntry {
version: 0,
uuid: config.uuid.clone(),
state: String::from("installing"),
jail_type: String::from("base"),
root: root.clone(),
})
}
Some(_) => Err(ConflictError::bx(config.uuid.as_str())),
}
......@@ -135,8 +147,9 @@ impl<'a> JDB<'a> {
None => Err(NotFoundError::bx(uuid)),
Some(index) => {
// remove the config file first
let path = Path::new(self.config.settings.conf_dir.as_str());
path.join(uuid).set_extension("json");
let mut path = PathBuf::from(self.config.settings.conf_dir.as_str());
path.join(uuid);
path.set_extension("json");
fs::remove_file(&path)?;
self.index.entries.remove(index);
self.save()?;
......@@ -163,20 +176,30 @@ impl<'a> JDB<'a> {
/// Reads the config file for a given entry
fn config(self: &'a JDB<'a>, entry: &IdxEntry) -> Result<JailConfig, Box<Error>> {
let config_path = Path::new(self.config.settings.conf_dir.as_str());
config_path.join(entry.uuid.clone()).set_extension("json");
let mut config_path = PathBuf::from(self.config.settings.conf_dir.as_str());
config_path.push(entry.uuid.clone());
config_path.set_extension("json");
let config_file = File::open(config_path)?;
let conf: JailConfig = serde_json::from_reader(config_file)?;
Ok(conf)
}
/// Saves the database
fn save(self: &'a JDB<'a>) -> Result<usize, Box<Error>> {
let path = Path::new(self.config.settings.conf_dir.as_str());
path.join("index");
let mut path = PathBuf::from(self.config.settings.conf_dir.as_str());
path.push("index");
let file = File::create(path)?;
serde_json::to_writer(file, &self.index)?;
Ok(self.index.entries.len())
}
pub fn get(self: &'a JDB<'a>, uuid: &str) -> Option<&IdxEntry> {
match self.find(uuid) {
None => None,
Some(index) => {
Some(&self.index.entries[index])
}
}
}
/// Finds an entry for a given uuid
fn find(self: &'a JDB<'a>, uuid: &str) -> Option<usize> {
self.index.entries.iter().position(|x| *x.uuid == *uuid)
......
......@@ -36,7 +36,7 @@ mod config;
use config::Config;
pub mod errors;
use errors::GenericError;
use errors::{GenericError, NotFoundError};
fn main() {
use clap::App;
......@@ -77,6 +77,7 @@ fn main() {
Ok(exit_code) => std::process::exit(exit_code),
Err(e) => {
crit!(config.logger, "error: {}", e);
println!("error: {}", e);
std::process::exit(1)
}
}
......@@ -106,13 +107,22 @@ fn list(conf: &Config, _matches: &clap::ArgMatches) -> Result<i32, Box<Error>> {
fn create(conf: &Config, _matches: &clap::ArgMatches) -> Result<i32, Box<Error>> {
let mut db = JDB::open(conf)?;
let conf: jdb::JailConfig = serde_json::from_reader(io::stdin())?;
db.insert(conf)?;
let entry = db.insert(conf)?;
zfs::create(entry.root.as_str())?;
Ok(0)
}
fn destroy(conf: &Config, matches: &clap::ArgMatches) -> Result<i32, Box<Error>> {
let mut db = JDB::open(conf)?;
let uuid = value_t!(matches, "uuid", String).unwrap();
match db.get(uuid.as_str()) {
Some(entry) =>
match zfs::destroy(entry.root.as_str()){
Ok(_) => debug!(conf.logger, "zfs dataset deleted: {}", entry.root),
Err(e) => warn!(conf.logger, "failed to delete dataset: {}", e)
},
None => return Err(NotFoundError::bx("Could not find VM")),
};
db.remove(uuid.as_str())?;
Ok(0)
}
\ No newline at end of file
......@@ -30,6 +30,50 @@ pub fn list(pool: &str) -> Result<Vec<ZFSEntry>, Box<Error>> {
}
Ok(res)
}
/// reads the zfs datasets in a pool
pub fn get(pool: &str, dataset: &str) -> Result<ZFSEntry, Box<Error>> {
let mut path = String::from(pool);
path.push('/');
path.push_str(dataset);
let output = Command::new("zfs")
.args(&["list", "-p", "-H", path.as_str()])
.output()
.expect("zfs list failed");
if output.status.success() {
let reply = String::from_utf8_lossy(&output.stdout).to_string();
deconstruct_entry(reply.as_str())
} else {
Err(GenericError::bx("Failed to get dataset"))
}
}
/// create a zfs datasets in a pool
pub fn create(dataset: &str) -> Result<i32, Box<Error>> {
let output = Command::new("zfs")
.args(&["create", dataset])
.output()
.expect("zfs create failed");
if output.status.success() {
Ok(0)
} else {
Err(GenericError::bx("Failed create dataset"))
}
}
/// destroy the zfs datasets in a pool
pub fn destroy(dataset: &str) -> Result<i32, Box<Error>> {
let output = Command::new("zfs")
.args(&["destroy", dataset])
.output()
.expect("zfs create failed");
if output.status.success() {
Ok(0)
} else {
Err(GenericError::bx("Failed destroy dataset"))
}
}
/// deconstructs a line from zfs list into an `ZFSEntry`.
fn deconstruct_entry(line: &str) -> Result<ZFSEntry, Box<Error>> {
let mut parts = line.split('\t');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment