Commit 2c2544fc authored by Heinz N. Gies's avatar Heinz N. Gies

Routes

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