Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

track ddm state durations #180

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
129 changes: 126 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, 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,133 @@ pub fn handler(
Ok(())
}

/// Status of a DDM peer.
#[derive(
Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema,
)]
#[serde(rename = "PeerStatus")]
pub enum PeerStatusV1 {
NoContact,
Active,
Expired,
}

// Translate internal peer status which is based on instants, to API
// representation which is based on durations.
impl From<crate::db::PeerStatus> for PeerStatusV1 {
fn from(value: crate::db::PeerStatus) -> Self {
match value {
crate::db::PeerStatus::NoContact => Self::NoContact,
crate::db::PeerStatus::Init(_)
| crate::db::PeerStatus::Solicit(_)
| crate::db::PeerStatus::Exchange(_) => Self::Active,
crate::db::PeerStatus::Expired(_) => Self::Expired,
}
}
}

/// Information about a DDM peer.
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, JsonSchema)]
#[serde(rename = "PeerInfo")]
pub struct PeerInfoV1 {
pub status: PeerStatusV1,
pub addr: Ipv6Addr,
pub host: String,
pub kind: RouterKind,
}

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

/// Status of a DDM peer. Includes how long the current status has been active.
#[derive(
Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize, JsonSchema,
)]
#[serde(tag = "type", content = "value")]
pub enum PeerStatusV2 {
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<crate::db::PeerStatus> for PeerStatusV2 {
fn from(value: crate::db::PeerStatus) -> Self {
match value {
crate::db::PeerStatus::NoContact => Self::NoContact,
crate::db::PeerStatus::Init(t) => {
Self::Init(Instant::now().duration_since(t))
}
crate::db::PeerStatus::Solicit(t) => {
Self::Solicit(Instant::now().duration_since(t))
}
crate::db::PeerStatus::Exchange(t) => {
Self::Exchange(Instant::now().duration_since(t))
}
crate::db::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 PeerInfoV2 {
pub status: PeerStatusV2,
pub addr: Ipv6Addr,
pub host: String,
pub kind: RouterKind,
}

impl From<crate::db::PeerInfo> for PeerInfoV2 {
fn from(value: crate::db::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, PeerInfoV1>>, 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))
}

#[endpoint { method = GET, path = "/peers_v2" }]
async fn get_peers_v2(
ctx: RequestContext<Arc<Mutex<HandlerContext>>>,
) -> Result<HttpResponseOk<HashMap<u32, PeerInfoV2>>, HttpError> {
let ctx = ctx.context().lock().unwrap();
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 Expand Up @@ -422,6 +544,7 @@ pub fn api_description(
) -> Result<ApiDescription<Arc<Mutex<HandlerContext>>>, String> {
let mut api = ApiDescription::new();
api.register(get_peers)?;
api.register(get_peers_v2)?;
api.register(expire_peer)?;
api.register(advertise_prefixes)?;
api.register(advertise_tunnel_endpoints)?;
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
Loading