Commit 9aab9805 authored by Heinz N. Gies's avatar Heinz N. Gies

Merge branch 'dev'

parents 65f8529b 0f9eabb8
Pipeline #14122184 passed with stage
in 4 minutes and 5 seconds
......@@ -51,4 +51,4 @@ test:
script:
- cargo test
tags:
- gitlab-org-high-cpu
- docker
[root]
name = "vmadm"
version = "0.3.4"
version = "0.3.6"
dependencies = [
"aud 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"bzip2 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
......
[package]
name = "vmadm"
version = "0.3.5"
version = "0.3.6"
authors = ["Heinz N. Gies <heinz@project-fifo.net>"]
[dependencies]
......
VERSION=0.3.4
VERSION=0.3.6
COMPONENT_INTERNAL=rvmadm
COMPONENT=vmadm
......@@ -8,7 +8,9 @@ uuid="$2"
hostname="$3"
# include shared utility functions
. ${brand_root}/../shared/utils.sh
. "${brand_root}/../shared/utils.sh"
read_routes
# create
jail -c persist \
......
......@@ -12,6 +12,7 @@ hostname="$3"
distro=$(detect_distro "/jail")
read_routes
if [ "${distro}" = "redhat" ]
then
......
......@@ -95,3 +95,12 @@ expand_linked() {
ldd -a "${file}" 2> /dev/null | awk '/=>/{print $(NF-1)}'
done
}
read_routes() {
while read route gw
do
/sbin/route add "$route" -gateway "$gw"
echo "route: $route"
echo "gw: $gw"
done < "/config/routes"
}
......@@ -3,12 +3,11 @@
use std::error::Error;
use std::fs::File;
use std::io::Read;
#[cfg(target_os = "freebsd")]
use std::process::Command;
#[cfg(target_os = "freebsd")]
use errors::GenericError;
use errors::{ValidationError, ValidationErrors};
use errors::ValidationError;
use config::Config;
use serde_json;
......@@ -266,6 +265,8 @@ pub struct JailConfig {
/// Version of the package used for this jail
#[serde(skip_serializing_if = "Option::is_none")]
pub package_version: Option<String>,
#[serde(default = "empty_map")]
pub routes: Map<String, String>,
// TODO:
#[serde(default = "empty_map")]
pub customer_metadata: Map<String, String>,
......@@ -298,6 +299,7 @@ impl PartialEq for JailConfig {
self.indestructible_zoneroot == other.indestructible_zoneroot &&
self.owner_uuid == other.owner_uuid &&
self.package_name == other.package_name &&
self.routes == other.routes &&
self.package_version == other.package_version
}
}
......@@ -306,19 +308,20 @@ lazy_static! {
static ref HOSTNAME_RE: Regex = Regex::new("^[a-zA-Z0-9]([a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?$").unwrap();
static ref ALIAS_RE: Regex = Regex::new("^[a-zA-Z0-9]([a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?$").unwrap();
static ref INTERFACE_RE: Regex = Regex::new("^[a-zA-Z]{1,4}[0-9]{0,3}$").unwrap();
static ref IP_RE: Regex = Regex::new("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$").unwrap();
static ref IP_RE: Regex = Regex::new("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$").unwrap();
static ref NET_RE: Regex = Regex::new("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(/((3[0-2])|([12][0-9])|[0-9]))?$").unwrap();
static ref MAC_RE: Regex = Regex::new("^[a-fA-F0-9]{1,2}([:][a-fA-F0-9]{1,2}){5}$").unwrap();
}
impl JailConfig {
/// Reads a new config from a file
pub fn from_file(config: &Config, config_path: &str) -> Result<Self, Box<Error>> {
pub fn from_file(config_path: &str) -> Result<Self, Box<Error>> {
let config_file = File::open(config_path)?;
JailConfig::from_reader(config, config_file)
JailConfig::from_reader(config_file)
}
/// Reads the config from a reader
pub fn from_reader<R>(config: &Config, reader: R) -> Result<Self, Box<Error>>
pub fn from_reader<R>(reader: R) -> Result<Self, Box<Error>>
where
R: Read,
{
......@@ -330,10 +333,7 @@ impl JailConfig {
if conf.max_locked_memory.is_none() {
conf.max_locked_memory = Some(max_physical_memory);
}
match conf.errors(config) {
Some(errors) => Err(ValidationErrors::bx(errors)),
None => Ok(conf),
}
Ok(conf)
}
/// checks the config for errors
pub fn errors(&self, config: &Config) -> Option<Vec<ValidationError>> {
......@@ -393,6 +393,20 @@ impl JailConfig {
}
i = i + 1;
}
for (dest, gw) in self.routes.iter() {
if !NET_RE.is_match(dest.as_str()) {
errors.push(ValidationError::new(
format!("routes {} -> {}", dest, gw).as_str(),
"Invalid destination",
))
}
if !IP_RE.is_match(gw.as_str()) {
errors.push(ValidationError::new(
format!("routes {} -> {}", dest, gw).as_str(),
"Invalid gateway",
))
}
}
if errors.is_empty() {
None
} else {
......@@ -506,4 +520,3 @@ fn checkip(ipaddr: &str) -> bool {
.expect("failed to ping");
output.status.success()
}
......@@ -115,6 +115,20 @@ impl<'a> Jail<'a> {
resolver_file.write_all(b"\n")?;
}
}
if ! self.config.routes.is_empty() {
let mut routes = config.clone();
routes.push("routes");
debug!("preparing routes file";
"vm" => self.idx.uuid.hyphenated().to_string(),
"file" => routes.to_str());
let mut routes_file = File::create(routes)?;
for (dest, gw) in self.config.routes.iter() {
routes_file.write_all(dest.as_bytes())?;
routes_file.write_all(b"\t")?;
routes_file.write_all(gw.as_bytes())?;
routes_file.write_all(b"\n")?;
}
}
match self.config.customer_metadata.get("root_authorized_keys") {
None => (),
Some(keys) => {
......
......@@ -206,7 +206,7 @@ impl<'a> JDB<'a> {
config_path.push(entry.uuid.hyphenated().to_string());
config_path.set_extension("json");
match config_path.to_str() {
Some(path) => JailConfig::from_file(self.config, path),
Some(path) => JailConfig::from_file(path),
None => Err(GenericError::bx("could not generate vm config path")),
}
}
......
......@@ -73,7 +73,7 @@ mod config;
use config::Config;
mod errors;
use errors::GenericError;
use errors::{GenericError, ValidationErrors};
/// Custom Drain logic
struct RuntimeLevelFilter<D> {
......@@ -378,11 +378,11 @@ fn create(conf: &Config, matches: &clap::ArgMatches) -> Result<i32, Box<Error>>
let jail = match value_t!(matches, "file", String) {
Err(_) => {
debug!("Reading from STDIN");
jail_config::JailConfig::from_reader(conf, io::stdin())?
jail_config::JailConfig::from_reader(io::stdin())?
}
Ok(file) => {
debug!("Reading from file"; "file" => file.clone() );
jail_config::JailConfig::from_reader(conf, File::open(file)?)?
jail_config::JailConfig::from_reader(File::open(file)?)?
}
};
let mut dataset = conf.settings.pool.clone();
......@@ -404,7 +404,7 @@ fn create(conf: &Config, matches: &clap::ArgMatches) -> Result<i32, Box<Error>>
conf,
uuid: jail.uuid.clone(),
dataset,
config: jail,
config: jail.clone(),
entry: None,
snapshot: None,
root: None,
......@@ -573,13 +573,18 @@ fn create(conf: &Config, matches: &clap::ArgMatches) -> Result<i32, Box<Error>>
Adventure::new(init_up, init_down),
Adventure::new(brand_install_up, brand_install_down),
]);
match saga.tell(state) {
Ok(state) => {
println!("Created jail {}", state.uuid);
Ok(0)
}
Err(failure) => Err(failure.to_error()),
match jail.errors(conf) {
Some(errors) => Err(ValidationErrors::bx(errors)),
None =>
match saga.tell(state) {
Ok(state) => {
println!("Created jail {}", state.uuid);
Ok(0)
}
Err(failure) => Err(failure.to_error()),
}
}
}
fn delete(conf: &Config, matches: &clap::ArgMatches) -> Result<i32, Box<Error>> {
......
......@@ -6,6 +6,7 @@ use serde_json;
use uuid::Uuid;
use jdb::IdxEntry;
use zfs;
use std::collections::BTreeMap as Map;
macro_rules! update {
( $src:ident, $target:ident; $($field:ident),+) => (
......@@ -117,11 +118,15 @@ pub struct JailUpdate {
package_version: Option<String>,
#[serde(default = "empty_nics")]
add_nics: Vec<NIC>,
#[serde(default = "empty_macs")]
#[serde(default = "empty_svec")]
remove_nics: Vec<String>,
#[serde(default = "empty_nic_update")]
update_nics: Vec<NICUpdate>,
#[serde(default = "empty_svec")]
remove_routes: Vec<String>,
#[serde(default = "empty_map")]
set_routes: Map<String, String>,
}
impl JailUpdate {
......@@ -155,7 +160,8 @@ impl JailUpdate {
add_nics: vec![],
remove_nics: vec![],
update_nics: vec![],
remove_routes: vec![],
set_routes: Map::new()
}
}
pub fn apply(&self, config: JailConfig, index: &IdxEntry) -> Result<JailConfig, Box<Error>> {
......@@ -194,21 +200,27 @@ impl JailUpdate {
}).collect(),
_ => c.nics.iter().map(|nic| update.apply(nic.clone())).collect()
};
}
if self.quota.is_some() {
zfs::quota(index.root.as_str(), self.quota.unwrap())?;
}
for remove_route in self.remove_routes.iter() {
c.routes.remove(remove_route);
}
for (route, gw) in self.set_routes.iter() {
c.routes.insert(route.clone(), gw.clone());
}
return Ok(c);
}
}
fn empty_map() -> Map<String, String> {
Map::new()
}
fn empty_macs() -> Vec<String> {
fn empty_svec() -> Vec<String> {
Vec::new()
}
......@@ -300,6 +312,7 @@ mod tests {
resolvers: Vec::new(),
customer_metadata: Map::new(),
internal_metadata: Map::new(),
routes: Map::new(),
}
}
......@@ -449,6 +462,52 @@ mod tests {
assert_eq!(true, conf1.nics[1].primary);
}
#[test]
fn set_routes() {
let conf = conf();
let mut update = JailUpdate::empty();
let target = String::from("10.0.0.0/24");
let gw = String::from("10.0.1.0");
update.set_routes.insert(target.clone(), gw.clone());
let updated = update.apply(conf, &IdxEntry::empty()).unwrap();
assert!(!updated.routes.is_empty());
assert_eq!(&gw, updated.routes.get(&target).unwrap());
}
#[test]
fn reset_routes() {
let mut conf = conf();
let mut update = JailUpdate::empty();
let target = String::from("10.0.0.0/24");
let gw = String::from("10.0.1.0");
let gw2 = String::from("10.0.2.0");
conf.routes.insert(target.clone(), gw.clone());
update.set_routes.insert(target.clone(), gw2.clone());
let updated = update.apply(conf, &IdxEntry::empty()).unwrap();
assert!(!updated.routes.is_empty());
assert_eq!(&gw2, updated.routes.get(&target).unwrap());
}
#[test]
fn remove_routes() {
let mut conf = conf();
let mut update = JailUpdate::empty();
let target = String::from("10.0.0.0/24");
let gw = String::from("10.0.1.0");
conf.routes.insert(target.clone(), gw);
update.remove_routes = vec![target];
assert!(update.apply(conf, &IdxEntry::empty()).unwrap().routes.is_empty());
}
#[test]
fn remove_routes_not_found() {
let conf = conf();
let mut update = JailUpdate::empty();
let target = String::from("10.0.0.0/24");
update.remove_routes = vec![target];
assert!(update.apply(conf, &IdxEntry::empty()).unwrap().routes.is_empty());
}
// nic update tests
#[test]
......
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