diff --git a/examples/models/router/relations/basic.cfg b/examples/models/router/relations/basic.cfg index 526761ea..a8241929 100644 --- a/examples/models/router/relations/basic.cfg +++ b/examples/models/router/relations/basic.cfg @@ -12,10 +12,14 @@ relations: requires: - no-ssh-sockets + interfaces: + requires: + - addresses + - routes + # State $: - consists: + requires: - routes - - interfaces - addresses - ssh-sockets diff --git a/libsysinspect/src/intp/inspector.rs b/libsysinspect/src/intp/inspector.rs index f94a6f25..8b02d599 100644 --- a/libsysinspect/src/intp/inspector.rs +++ b/libsysinspect/src/intp/inspector.rs @@ -18,7 +18,7 @@ use crate::{ }; use colored::Colorize; use serde_yaml::Value; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; pub struct SysInspector { entities: HashMap, @@ -126,12 +126,20 @@ impl SysInspector { let mut out: Vec = Vec::default(); for s in &self.checkbook { if rids.contains(&s.id()) { - for rel in s.relations() { - out.extend(self.actions_by_entities(rel.get_entities(state.to_owned()), state.to_owned())?); + for r in s.relations() { + out.extend(self.actions_by_entities(r.required(&parse_state(state.clone()))?, state.clone())?); } } } + if out.is_empty() { + return Err(SysinspectError::ModelDSLError(format!( + "Checkbook contains no such relations as \"{}\" that would be aligned with the state \"{}\"", + rids.join(", "), + parse_state(state) + ))); + } + Ok(out) } @@ -139,6 +147,8 @@ impl SysInspector { pub fn actions_by_entities(&self, eids: Vec, state: Option) -> Result, SysinspectError> { let mut out: Vec = Vec::default(); let state = parse_state(state); + let mut dropped: HashSet = HashSet::default(); + dropped.extend(eids.clone()); for eid in eids { for action in self.actions.values() { @@ -149,10 +159,19 @@ impl SysInspector { // it also corresponds to other claims and conditions, and that then // needs to be passed to the reactor. out.push(action.to_owned().setup(self, &eid, state.to_owned())?); + dropped.remove(&eid); } } } + if !dropped.is_empty() { + return Err(SysinspectError::ModelDSLError(format!( + "Entities \"{}\" are not bound with the state \"{}\" or don't exist", + dropped.iter().map(|s| s.to_string()).collect::>().join(", "), + state + ))); + } + Ok(out) } diff --git a/libsysinspect/src/intp/relations.rs b/libsysinspect/src/intp/relations.rs index 4e2ba3ef..8443f351 100644 --- a/libsysinspect/src/intp/relations.rs +++ b/libsysinspect/src/intp/relations.rs @@ -39,15 +39,21 @@ impl Relation { &self.states } - /// Get related entities - pub fn get_entities(&self, state: Option) -> Vec { - let mut out: Vec = Vec::default(); - let state = state.unwrap_or_default(); - for (st, ent) in self.states() { - if st.eq(&state) || st.eq("$") { - out.extend(ent.values().flat_map(|eids| eids.to_owned()).collect::>()); + /// Get required entities (consists of). + /// There is no clear distinction between "consists of" and "required". + pub fn required(&self, state: &str) -> Result, SysinspectError> { + let mut out = Vec::default(); + if let Some(set) = self.states().get(state) { + if let Some(required) = set.get("requires") { + out.extend(required.iter().map(|s| s.to_string())); } + } else { + return Err(SysinspectError::ModelDSLError(format!( + "No required entities has been found in the \"{}\" relation as the \"{state}\" state", + self.id() + ))); } - out + + Ok(out) } } diff --git a/src/main.rs b/src/main.rs index a72f4af0..c001a145 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ use libsysinspect::{ inspector::SysInspectRunner, logger, reactor::handlers, + traits::get_minion_traits, SysinspectError, }; use log::LevelFilter; @@ -108,6 +109,7 @@ fn main() { sr.set_state(params.get_one::("state").cloned()); sr.set_entities(clidef::split_by(¶ms, "entities", None)); sr.set_checkbook_labels(clidef::split_by(¶ms, "labels", None)); + sr.set_traits(get_minion_traits(None)); sr.start(); } }