blob: c62a29cd95b084dc36389f7416720abb82f24637 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Resource monitoring communication.
//!
//! # Summary
//!
//! The monitor MessageHub definitions fosters communication between the
//! resources watchdog and individual monitors. This is a two-way communication
//! path where the watchdog issues commands to the monitors and the monitors
//! return data as it becomes available.
//!
//! Since the watchdog and monitors are brought up independently of each other,
//! communication initiation is through broadcasting. A single command to
//! initiate monitoring is available to the watchdog. The watchdog broadcasts
//! this command, reaching all monitors, and then listens on the resulting
//! receptor for responses.
//!
//!
//! # Example
//!
//! ```no_run
//! #use crate::monitor;
//! #use crate::service::base::{Audience, MessengerType};
//!
//! async fn message_example() {
//! // Create monitor message hub.
//! let messenger_factory = service::message::create_hub();
//!
//! // Create a messenger for watchdog.
//! let (watchdog_client, _) = messenger_factory
//! .create(MessengerType::Unbound)
//! .await
//! .expect("should be able to create messenger");
//!
//! // Create a messenger for monitor.
//! let (_, mut monitor_receptor) = messenger_factory
//! .messenger_builder(MessengerType::Unbound)
//! .add_role(role::Signature::role(service::Role::Monitor(monitor::Role::Monitor)))
//! .build()
//! .await
//! .expect("should be able to create messenger");
//!
//! // Issue monitor command.
//! let mut data_receptor = watchdog_client
//! .message(request_payload.clone(),
//! Audience::Role(role::Signature::role(service::Role::Monitor(monitor::Role::Monitor,
//! )))).send();
//!
//! let data_payload = monitor::Payload::Data(
//! monitor::DataBuilder::new(monitor::State::Warning).build());
//!
//! // Receive command.
//! if let Ok((monitor::Payload::Monitor.into(), client)) =
//! monitor_receptor.next_payload().await {
//! client.reply(data_payload..into()).send().ack();
//! }
//! }
//! ```
use crate::clock::now;
use crate::payload_convert;
use fuchsia_zircon as zx;
pub mod base;
pub mod environment;
/// The commands and responses sent between the watchdog and monitors.
#[allow(dead_code)]
#[derive(Clone, Debug, PartialEq)]
pub enum Payload {
/// Broadcasted to inform monitors to start tracking resources.
///
/// A monitor should reply to this message with updates
/// about the resource it is monitoring, using the [`Data`] variant as the
/// payload for those updates.
Monitor,
/// Details captured by a monitor, sent as a response to Monitor.
Data(Data),
}
#[derive(PartialEq, Copy, Clone, Debug, Eq, Hash)]
pub enum Role {
Monitor,
}
/// `State` defines buckets for operating ranges that resources can be observed
/// in. These are coarsely defined levels whose cut-off points are dependent on
/// the resource context. The watchdog will use these signals to indicate what
/// state the service is in.
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum State {
/// Resource utilization is or has returned to normal.
Normal,
/// Resource utilization has risen or fallen to a warning state.
Warning,
/// Resource utilization has reached critical usage.
Critical,
}
/// `DataBuilder` is a helper for constructing [`Data`] to send through the
/// MessageHub.
pub struct DataBuilder {
/// Time which data was updated.
update_time: zx::Time,
/// The current state of the resource.
state: State,
/// Optional extra data describing the state.
details: Option<Details>,
}
impl DataBuilder {
/// By default, the builder creates Data with the current time and no
/// details
#[allow(dead_code)]
pub fn new(state: State) -> Self {
Self { update_time: now(), state, details: None }
}
#[allow(dead_code)]
pub fn set_details(mut self, details: Details) -> Self {
self.details = Some(details);
self
}
#[allow(dead_code)]
pub fn build(self) -> Data {
Data { update_time: self.update_time, state: self.state, details: self.details }
}
}
/// `Data` captures the resource context at any given time observed by a
/// monitor.
#[derive(Clone, Debug, PartialEq)]
pub struct Data {
/// Time which data was updated.
update_time: zx::Time,
/// The current state of the resource.
state: State,
/// Optional extra data describing the state.
details: Option<Details>,
}
/// `Details` defines the types of information that can be returned with
/// [`Data`] to provide additional details surrounding an observed [`State`].
/// This enum will become populated as monitors are implemented.
#[derive(Clone, Debug, PartialEq)]
pub enum Details {}
payload_convert!(Monitor, Payload);
#[cfg(test)]
mod tests {
use crate::message::base::{role, Audience, MessengerType};
use crate::monitor;
use crate::service;
#[fuchsia_async::run_until_stalled(test)]
async fn test_messaging() {
// Create monitor message hub.
let messenger_factory = service::message::create_hub();
// Create a messenger for watchdog.
let (watchdog_client, _) = messenger_factory
.create(MessengerType::Unbound)
.await
.expect("should be able to create messenger");
// Create a messenger for monitor.
let (_, mut monitor_receptor) = messenger_factory
.messenger_builder(MessengerType::Unbound)
.add_role(role::Signature::role(service::Role::Monitor(monitor::Role::Monitor)))
.build()
.await
.expect("should be able to create messenger");
let request_payload: service::Payload = monitor::Payload::Monitor.into();
// Issue monitor command.
let mut data_receptor = watchdog_client
.message(
request_payload.clone(),
Audience::Role(role::Signature::role(service::Role::Monitor(
monitor::Role::Monitor,
))),
)
.send();
let data_payload: service::Payload =
monitor::Payload::Data(monitor::DataBuilder::new(monitor::State::Warning).build())
.into();
// Receive command.
if let Ok((payload, client)) = monitor_receptor.next_payload().await {
assert_eq!(payload, request_payload);
client.reply(data_payload.clone().into()).send().ack();
} else {
panic!("expected payload");
}
// Ensure the payload was received by the messenger that broadcasted
// the Monitor command.
assert_eq!(data_receptor.next_payload().await.expect("should have data").0, data_payload);
}
}