Keep child state (active/pending) in status and show in CLI and metrics. (#656)

This commit is contained in:
Tim Bruijnzeels
2021-09-13 13:32:22 +02:00
parent 5ff136b6fb
commit 4eae590e99
7 changed files with 92 additions and 18 deletions
+1
View File
@@ -160,6 +160,7 @@
#
# krill_cas_children{ca="ca"} number of children for CA
# krill_ca_child_success{ca="ca", child="child"} status of last child to CA connection (0=issue, 1=success)
# krill_ca_child_state{ca="ca", child="child"} child state (see 'suspend_child_after_inactive_hours' config) (0=suspended, 1=active)
# krill_ca_child_last_connection{ca="ca", child="child"} unix timestamp in seconds of last child to CA connection
# krill_ca_child_last_success{ca="ca", child="child"} unix timestamp in seconds of last successful child to CA connection
# krill_ca_child_agent_total{ca="ca", user_agent="ua string"} total children per user agent based on their last connection
+45 -14
View File
@@ -1637,10 +1637,10 @@ impl ChildrenConnectionStats {
ChildrenConnectionStats { children }
}
pub fn inactive_children(&self, threshold_hours: i64) -> Vec<ChildHandle> {
pub fn suspension_candidates(&self, threshold_hours: i64) -> Vec<ChildHandle> {
self.children
.iter()
.filter(|child| child.inactive(threshold_hours))
.filter(|child| child.suspension_candidate(threshold_hours))
.map(|child| child.handle.clone())
.collect()
}
@@ -1649,22 +1649,23 @@ impl ChildrenConnectionStats {
impl fmt::Display for ChildrenConnectionStats {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if !self.children.is_empty() {
writeln!(f, "handle, user_agent, last_exchange, result")?;
writeln!(f, "handle,user_agent,last_exchange,result,state")?;
for child in &self.children {
match &child.last_exchange {
None => {
writeln!(f, "{},n/a,never,n/a", child.handle)?;
writeln!(f, "{},n/a,never,n/a,{}", child.handle, child.state)?;
}
Some(exchange) => {
let agent = exchange.user_agent.as_deref().unwrap_or("");
writeln!(
f,
"{},{},{},{}",
"{},{},{},{},{}",
child.handle,
agent,
exchange.timestamp.to_rfc3339(),
exchange.result
exchange.result,
child.state
)?;
}
}
@@ -1678,19 +1679,29 @@ impl fmt::Display for ChildrenConnectionStats {
pub struct ChildConnectionStats {
handle: ChildHandle,
last_exchange: Option<ChildExchange>,
state: ChildState,
}
impl ChildConnectionStats {
pub fn new(handle: ChildHandle, last_exchange: Option<ChildExchange>) -> Self {
ChildConnectionStats { handle, last_exchange }
pub fn new(handle: ChildHandle, last_exchange: Option<ChildExchange>, state: ChildState) -> Self {
ChildConnectionStats {
handle,
last_exchange,
state,
}
}
/// The child is considered 'inactive' if there was at least one exchange, and the
/// last exchange is longer ago than the specified threshold hours.
pub fn inactive(&self, threshold_hours: i64) -> bool {
match &self.last_exchange {
None => false, // if there has been no exchange at all, the child is not yet active, rather than inactive
Some(exchange) => exchange.timestamp < (Timestamp::now_minus_hours(threshold_hours)),
/// The child is considered a candidate for suspension if there was at least one exchange,
/// and the last exchange is longer ago than the specified threshold hours, and the child
/// is not already suspended.
pub fn suspension_candidate(&self, threshold_hours: i64) -> bool {
if self.state == ChildState::Suspended {
false
} else {
match &self.last_exchange {
None => false, // if there has been no exchange at all, the child is not yet active, rather than inactive
Some(exchange) => exchange.timestamp < (Timestamp::now_minus_hours(threshold_hours)),
}
}
}
}
@@ -1701,6 +1712,7 @@ impl ChildConnectionStats {
pub struct ChildStatus {
last_exchange: Option<ChildExchange>,
last_success: Option<Timestamp>,
suspended: Option<Timestamp>,
}
impl ChildStatus {
@@ -1712,6 +1724,7 @@ impl ChildStatus {
user_agent,
});
self.last_success = Some(timestamp);
self.suspended = None;
}
pub fn set_failure(&mut self, user_agent: Option<String>, error_response: ErrorResponse) {
@@ -1720,6 +1733,11 @@ impl ChildStatus {
result: ExchangeResult::Failure(error_response),
user_agent,
});
self.suspended = None;
}
pub fn set_suspended(&mut self) {
self.suspended = Some(Timestamp::now())
}
pub fn last_exchange(&self) -> Option<&ChildExchange> {
@@ -1729,6 +1747,18 @@ impl ChildStatus {
pub fn last_success(&self) -> Option<Timestamp> {
self.last_success
}
pub fn suspended(&self) -> Option<Timestamp> {
self.suspended
}
pub fn child_state(&self) -> ChildState {
if self.suspended.is_none() {
ChildState::Active
} else {
ChildState::Suspended
}
}
}
impl Default for ChildStatus {
@@ -1736,6 +1766,7 @@ impl Default for ChildStatus {
ChildStatus {
last_exchange: None,
last_success: None,
suspended: None,
}
}
}
+14 -1
View File
@@ -765,11 +765,24 @@ impl CaManager {
if let Some(threshold_hours) = self.config.suspend_child_after_inactive_hours {
if let Ok(ca_status) = self.get_ca_status(&ca_handle).await {
let connections = ca_status.get_children_connection_stats();
for child in connections.inactive_children(threshold_hours) {
for child in connections.suspension_candidates(threshold_hours) {
info!(
"Child '{}' under CA '{}' was inactive for more than {} hours. Will suspend it.",
child, ca_handle, threshold_hours
);
if let Err(e) = self
.status_store
.lock()
.await
.set_child_suspended(&ca_handle, &child)
.await
{
error!(
"Could not update status to 'suspended' for inactive child, error: {}",
e
);
}
let req = UpdateChildRequest::suspend();
if let Err(e) = self.ca_child_update(&ca_handle, child, req, actor).await {
error!("Could not suspend inactive child, error: {}", e);
+11 -1
View File
@@ -30,7 +30,10 @@ impl CaStatus {
.children
.clone()
.into_iter()
.map(|(handle, status)| ChildConnectionStats::new(handle, status.into()))
.map(|(handle, status)| {
let state = status.child_state();
ChildConnectionStats::new(handle, status.into(), state)
})
.collect();
ChildrenConnectionStats::new(children)
}
@@ -167,6 +170,13 @@ impl StatusStore {
.await
}
/// Marks a child as suspended. Note that it will be implicitly unsuspended whenever a new success or
/// or failure is recorded for the child.
pub async fn set_child_suspended(&self, ca: &Handle, child: &ChildHandle) -> KrillResult<()> {
self.update_ca_child_status(ca, child, |status| status.set_suspended())
.await
}
pub async fn remove_child(&self, ca: &Handle, child: &ChildHandle) -> KrillResult<()> {
self.update_ca_status(ca, |status| {
status.children.remove(child);
+19 -2
View File
@@ -536,6 +536,7 @@ pub async fn metrics(req: Request) -> RoutingResult {
// krill_cas_children{ca="parent"} 11 // nr of children
// krill_ca_child_success{ca="parent", child="child"} 1
// krill_ca_child_state{ca="parent", child="child"} 1
// krill_ca_child_last_connection{ca="parent", child="child"} 1630921599
// krill_ca_child_last_success{ca="parent", child="child"} 1630921599
// krill_ca_child_agent_total{ca="parent", ua="krill/0.9.2"} 11
@@ -572,8 +573,24 @@ pub async fn metrics(req: Request) -> RoutingResult {
res.push('\n');
res.push_str(
"# HELP krill_ca_child_last_connection unix timestamp in seconds of last child to CA connection\n",
);
"# HELP krill_ca_child_state child state (see 'suspend_child_after_inactive_hours' config) (0=suspended, 1=active)\n",
);
res.push_str("# TYPE krill_ca_child_state gauge\n");
for (ca, status) in ca_status_map.iter() {
// skip the ones for which we have no status yet, i.e it was really only just added
// and no attempt to connect has yet been made.
for (child, status) in status.children().iter() {
let value = if status.suspended().is_none() { 0 } else { 1 };
res.push_str(&format!(
"krill_ca_child_state{{ca=\"{}\", child=\"{}\"}} {}\n",
ca, child, value
));
}
}
res.push('\n');
res.push_str("# HELP krill_ca_child_last_connection unix timestamp in seconds of last child to CA connection\n");
res.push_str("# TYPE krill_ca_child_last_connection gauge\n");
for (ca, status) in ca_status_map.iter() {
// skip the ones for which we have no status yet, i.e it was really only just added
@@ -160,6 +160,7 @@ service_uri = "https://localhost:3001/"
#
# krill_cas_children{ca="ca"} number of children for CA
# krill_ca_child_success{ca="ca", child="child"} status of last child to CA connection (0=issue, 1=success)
# krill_ca_child_state{ca="ca", child="child"} child state (see 'suspend_child_after_inactive_hours' config) (0=suspended, 1=active)
# krill_ca_child_last_connection{ca="ca", child="child"} unix timestamp in seconds of last child to CA connection
# krill_ca_child_last_success{ca="ca", child="child"} unix timestamp in seconds of last successful child to CA connection
# krill_ca_child_agent_total{ca="ca", user_agent="ua string"} total children per user agent based on their last connection
+1
View File
@@ -160,6 +160,7 @@ service_uri = "https://localhost:3001/"
#
# krill_cas_children{ca="ca"} number of children for CA
# krill_ca_child_success{ca="ca", child="child"} status of last child to CA connection (0=issue, 1=success)
# krill_ca_child_state{ca="ca", child="child"} child state (see 'suspend_child_after_inactive_hours' config) (0=suspended, 1=active)
# krill_ca_child_last_connection{ca="ca", child="child"} unix timestamp in seconds of last child to CA connection
# krill_ca_child_last_success{ca="ca", child="child"} unix timestamp in seconds of last successful child to CA connection
# krill_ca_child_agent_total{ca="ca", user_agent="ua string"} total children per user agent based on their last connection