Skip to content

Commit

Permalink
track ddm state durations
Browse files Browse the repository at this point in the history
  • Loading branch information
rcgoodfellow committed Mar 21, 2024
1 parent de065a8 commit a8a25ce
Show file tree
Hide file tree
Showing 9 changed files with 300 additions and 64 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion ddm-admin-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ progenitor::generate_api!(
}),
post_hook = (|log: &slog::Logger, result: &Result<_, _>| {
slog::trace!(log, "client response"; "result" => ?result);
})
}),
replace = { Duration = std::time::Duration }
);

impl Copy for types::Ipv4Prefix {}
Expand Down
66 changes: 63 additions & 3 deletions ddm/src/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use crate::db::{Db, PeerInfo, TunnelRoute};
use crate::db::{Db, PeerInfo, PeerStatus, RouterKind, TunnelRoute};
use crate::exchange::PathVector;
use crate::sm::{AdminEvent, Event, PrefixSet, SmContext};
use dropshot::endpoint;
Expand All @@ -27,6 +27,7 @@ use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::mpsc::Sender;
use std::sync::Arc;
use std::sync::Mutex;
use std::time::{Duration, Instant};
use tokio::spawn;
use tokio::task::JoinHandle;
use uuid::Uuid;
Expand Down Expand Up @@ -103,12 +104,71 @@ pub fn handler(
Ok(())
}

/// Status of a DDM peer with state expressed as durations.
#[derive(
Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize, JsonSchema,
)]
#[serde(tag = "type", content = "value")]
pub enum ApiPeerStatus {
NoContact,
Init(Duration),
Solicit(Duration),
Exchange(Duration),
Expired(Duration),
}

// Translate internal peer status which is based on instants, to API
// representation which is based on durations.
impl From<PeerStatus> for ApiPeerStatus {
fn from(value: PeerStatus) -> Self {
match value {
PeerStatus::NoContact => Self::NoContact,
PeerStatus::Init(t) => Self::Init(Instant::now().duration_since(t)),
PeerStatus::Solicit(t) => {
Self::Solicit(Instant::now().duration_since(t))
}
PeerStatus::Exchange(t) => {
Self::Exchange(Instant::now().duration_since(t))
}
PeerStatus::Expired(t) => {
Self::Expired(Instant::now().duration_since(t))
}
}
}
}

/// Information about a DDM peer.
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, JsonSchema)]
pub struct ApiPeerInfo {
pub status: ApiPeerStatus,
pub addr: Ipv6Addr,
pub host: String,
pub kind: RouterKind,
}

impl From<PeerInfo> for ApiPeerInfo {
fn from(value: PeerInfo) -> Self {
Self {
status: value.status.into(),
addr: value.addr,
host: value.host,
kind: value.kind,
}
}
}

#[endpoint { method = GET, path = "/peers" }]
async fn get_peers(
ctx: RequestContext<Arc<Mutex<HandlerContext>>>,
) -> Result<HttpResponseOk<HashMap<u32, PeerInfo>>, HttpError> {
) -> Result<HttpResponseOk<HashMap<u32, ApiPeerInfo>>, HttpError> {
let ctx = ctx.context().lock().unwrap();
Ok(HttpResponseOk(ctx.db.peers()))
let peers = ctx
.db
.peers()
.into_iter()
.map(|(k, v)| (k, v.into()))
.collect();
Ok(HttpResponseOk(peers))
}

#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
Expand Down
60 changes: 49 additions & 11 deletions ddm/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ use mg_common::net::{IpPrefix, Ipv6Prefix, TunnelOrigin};
use schemars::{JsonSchema, JsonSchema_repr};
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use slog::{error, Logger};
use slog::{debug, error, Logger};
use std::collections::{HashMap, HashSet};
use std::net::Ipv6Addr;
use std::sync::{Arc, Mutex};
use std::time::Instant;

/// The handle used to open a persistent key-value tree for originated
/// prefixes.
Expand Down Expand Up @@ -227,10 +228,34 @@ impl Db {

/// Set peer info at the given index. Returns true if peer information was
/// changed.
pub fn set_peer(&self, index: u32, info: PeerInfo) -> bool {
match self.data.lock().unwrap().peers.insert(index, info.clone()) {
Some(previous) => previous == info,
None => true,
pub fn set_peer_info(
&self,
index: u32,
addr: Ipv6Addr,
host: String,
kind: RouterKind,
) -> bool {
let mut data = self.data.lock().unwrap();
if let Some(peer) = data.peers.get_mut(&index) {
if peer.addr == addr && peer.host == host && peer.kind == kind {
false
} else {
peer.addr = addr;
peer.host = host;
peer.kind = kind;
true
}
} else {
data.peers.insert(
index,
PeerInfo {
addr,
host,
kind,
status: PeerStatus::Init(Instant::now()),
},
);
true
}
}

Expand Down Expand Up @@ -267,6 +292,19 @@ impl Db {
self.data.lock().unwrap().peers.remove(&index);
}

pub fn peer_status_transition(&self, index: u32, status: PeerStatus) {
if let Some(info) = self.data.lock().unwrap().peers.get_mut(&index) {
info.status = status;
} else {
// This is expected to happen during initialization as we don't
// add a peer to the db until an advertisement is received.
debug!(
self.log,
"status update: peer with index {} does not exist", index
);
}
}

pub fn routes_by_vector(
&self,
dst: Ipv6Prefix,
Expand All @@ -283,16 +321,16 @@ impl Db {
}
}

#[derive(
Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize, JsonSchema,
)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PeerStatus {
NoContact,
Active,
Expired,
Init(Instant),
Solicit(Instant),
Exchange(Instant),
Expired(Instant),
}

#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub struct PeerInfo {
pub status: PeerStatus,
pub addr: Ipv6Addr,
Expand Down
14 changes: 4 additions & 10 deletions ddm/src/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
//! and 1 for a transit routers. The fourth byte is a hostname length followed
//! directly by a hostname of up to 255 bytes in length.

use crate::db::{Db, PeerInfo, PeerStatus, RouterKind};
use crate::db::{Db, RouterKind};
use crate::sm::{Config, Event, NeighborEvent, SessionStats};
use crate::util::u8_slice_assume_init_ref;
use crate::{dbg, err, inf, trc, wrn};
Expand Down Expand Up @@ -504,15 +504,9 @@ fn handle_advertisement(
}
};
drop(guard);
let updated = ctx.db.set_peer(
ctx.config.if_index,
PeerInfo {
status: PeerStatus::Active,
addr: *sender,
host: hostname,
kind,
},
);
let updated =
ctx.db
.set_peer_info(ctx.config.if_index, *sender, hostname, kind);
if updated {
stats.peer_address.lock().unwrap().replace(*sender);
emit_nbr_update(ctx, sender, version);
Expand Down
17 changes: 14 additions & 3 deletions ddm/src/sm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use crate::db::{Db, RouterKind};
use crate::db::{Db, PeerStatus, RouterKind};
use crate::discovery::Version;
use crate::exchange::{PathVector, TunnelUpdate, UnderlayUpdate, Update};
use crate::{dbg, discovery, err, exchange, inf, wrn};
Expand All @@ -16,7 +16,7 @@ use std::sync::mpsc::{Receiver, Sender};
use std::sync::{Arc, Mutex};
use std::thread::sleep;
use std::thread::spawn;
use std::time::Duration;
use std::time::{Duration, Instant};
use thiserror::Error;

#[derive(Debug)]
Expand Down Expand Up @@ -228,6 +228,10 @@ impl State for Init {
&mut self,
event: Receiver<Event>,
) -> (Box<dyn State>, Receiver<Event>) {
self.ctx.db.peer_status_transition(
self.ctx.config.if_index,
PeerStatus::Init(Instant::now()),
);
loop {
let info = match get_ipaddr_info(&self.ctx.config.aobj_name) {
Ok(info) => info,
Expand Down Expand Up @@ -303,6 +307,10 @@ impl State for Solicit {
&mut self,
event: Receiver<Event>,
) -> (Box<dyn State>, Receiver<Event>) {
self.ctx.db.peer_status_transition(
self.ctx.config.if_index,
PeerStatus::Solicit(Instant::now()),
);
loop {
let e = match event.recv() {
Ok(e) => e,
Expand Down Expand Up @@ -529,6 +537,10 @@ impl State for Exchange {
&mut self,
event: Receiver<Event>,
) -> (Box<dyn State>, Receiver<Event>) {
self.ctx.db.peer_status_transition(
self.ctx.config.if_index,
PeerStatus::Exchange(Instant::now()),
);
let exchange_thread = loop {
match exchange::handler(
self.ctx.clone(),
Expand Down Expand Up @@ -759,7 +771,6 @@ impl State for Exchange {
);
}
}
// TODO tunnel
Event::Peer(PeerEvent::Push(update)) => {
inf!(
self.log,
Expand Down
1 change: 1 addition & 0 deletions ddmadm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ tabwriter.workspace = true
colored.workspace = true
anyhow.workspace = true
anstyle.workspace = true
humantime.workspace = true
33 changes: 31 additions & 2 deletions ddmadm/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
use anyhow::Result;
use clap::Parser;
use colored::*;
use ddm_admin_client::types::ApiPeerStatus;
use ddm_admin_client::{types, Client};
use humantime::Duration;
use mg_common::cli::oxide_cli_style;
use mg_common::net::{IpPrefix, Ipv4Prefix, Ipv6Prefix};
use slog::{Drain, Logger};
Expand Down Expand Up @@ -120,7 +122,7 @@ async fn run() -> Result<()> {
for (index, info) in &msg.into_inner() {
writeln!(
&mut tw,
"{}\t{}\t{}\t{}\t{:?}",
"{}\t{}\t{}\t{}\t{}",
index,
info.host,
info.addr,
Expand All @@ -129,7 +131,34 @@ async fn run() -> Result<()> {
1 => "Transit",
_ => "?",
},
info.status,
match info.status {
ApiPeerStatus::NoContact => "no contact".into(),
ApiPeerStatus::Init(t) => format!(
"Init {}",
// Don't care about precision beyond milliseconds
Duration::from(std::time::Duration::from_millis(
t.as_millis() as u64
))
),
ApiPeerStatus::Solicit(t) => format!(
"Solicit {}",
Duration::from(std::time::Duration::from_millis(
t.as_millis() as u64
))
),
ApiPeerStatus::Exchange(t) => format!(
"Exchange {}",
Duration::from(std::time::Duration::from_millis(
t.as_millis() as u64
))
),
ApiPeerStatus::Expired(t) => format!(
"Expired {}",
Duration::from(std::time::Duration::from_millis(
t.as_millis() as u64
))
),
}
)?;
}
tw.flush()?;
Expand Down
Loading

0 comments on commit a8a25ce

Please sign in to comment.