Skip to content

Commit

Permalink
isis: implement log of SPF runs
Browse files Browse the repository at this point in the history
Signed-off-by: Renato Westphal <[email protected]>
  • Loading branch information
rwestphal committed Dec 22, 2024
1 parent 9f0ab7f commit 7332bd4
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 19 deletions.
7 changes: 6 additions & 1 deletion holo-isis/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use crate::interface::CircuitIdAllocator;
use crate::lsdb::{LspEntry, LspLogEntry};
use crate::northbound::configuration::InstanceCfg;
use crate::packet::{LevelNumber, LevelType, Levels};
use crate::spf::SpfScheduler;
use crate::spf::{SpfLogEntry, SpfScheduler};
use crate::tasks::messages::input::{
AdjHoldTimerMsg, DisElectionMsg, LspDeleteMsg, LspOriginateMsg,
LspPurgeMsg, LspRefreshMsg, NetRxPduMsg, SendCsnpMsg, SendPsnpMsg,
Expand Down Expand Up @@ -83,6 +83,9 @@ pub struct InstanceState {
// Log of LSP updates.
pub lsp_log: VecDeque<LspLogEntry>,
pub lsp_log_next_id: u32,
// Log of SPF runs.
pub spf_log: VecDeque<SpfLogEntry>,
pub spf_log_next_id: u32,
}

#[derive(Debug, Default)]
Expand Down Expand Up @@ -378,6 +381,8 @@ impl InstanceState {
discontinuity_time: Utc::now(),
lsp_log: Default::default(),
lsp_log_next_id: 0,
spf_log: Default::default(),
spf_log_next_id: 0,
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions holo-isis/src/lsdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,9 @@ pub(crate) fn install<'a>(

// Schedule SPF run if necessary.
if content_change {
let spf_sched = instance.state.spf_sched.get_mut(level);
spf_sched.trigger_lsps.push(lsp_log_id);
spf_sched.schedule_time.get_or_insert_with(Instant::now);
instance
.tx
.protocol_input
Expand Down
41 changes: 24 additions & 17 deletions holo-isis/src/northbound/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ use crate::adjacency::Adjacency;
use crate::collections::Lsdb;
use crate::instance::Instance;
use crate::interface::Interface;
use crate::lsdb::{LspEntry, LspLogEntry};
use crate::lsdb::{LspEntry, LspLogEntry, LspLogId};
use crate::packet::tlv::{
ExtIpv4Reach, ExtIsReach, Ipv4Reach, Ipv6Reach, IsReach, UnknownTlv,
};
use crate::packet::{LanId, LevelNumber};
use crate::spf::SpfLogEntry;

pub static CALLBACKS: Lazy<Callbacks<Instance>> = Lazy::new(load_callbacks);

Expand All @@ -38,6 +39,8 @@ pub static CALLBACKS: Lazy<Callbacks<Instance>> = Lazy::new(load_callbacks);
pub enum ListEntry<'a> {
#[default]
None,
SpfLog(&'a SpfLogEntry),
SpfTriggerLsp(&'a LspLogId),
LspLog(&'a LspLogEntry),
Lsdb(LevelNumber, &'a Lsdb),
LspEntry(&'a LspEntry),
Expand Down Expand Up @@ -78,31 +81,35 @@ fn load_callbacks() -> Callbacks<Instance> {
})
})
.path(isis::spf_log::event::PATH)
.get_iterate(|_instance, _args| {
// TODO: implement me!
None
.get_iterate(|instance, _args| {
let Some(instance_state) = &instance.state else { return None };
let iter = instance_state.spf_log.iter().map(ListEntry::SpfLog);
Some(Box::new(iter) as _).ignore_in_testing()
})
.get_object(|_instance, _args| {
.get_object(|_instance, args| {
use isis::spf_log::event::Event;
let log = args.list_entry.as_spf_log().unwrap();
Box::new(Event {
id: todo!(),
spf_type: None,
level: None,
schedule_timestamp: None,
start_timestamp: None,
end_timestamp: None,
id: log.id,
spf_type: Some(log.spf_type.to_yang()),
level: Some(log.level as u8),
schedule_timestamp: Some(Cow::Borrowed(&log.schedule_time)),
start_timestamp: Some(Cow::Borrowed(&log.start_time)),
end_timestamp: Some(Cow::Borrowed(&log.end_time)),
})
})
.path(isis::spf_log::event::trigger_lsp::PATH)
.get_iterate(|_instance, _args| {
// TODO: implement me!
None
.get_iterate(|_instance, args| {
let log = args.parent_list_entry.as_spf_log().unwrap();
let iter = log.trigger_lsps.iter().map(ListEntry::SpfTriggerLsp);
Some(Box::new(iter))
})
.get_object(|_instance, _args| {
.get_object(|_instance, args| {
use isis::spf_log::event::trigger_lsp::TriggerLsp;
let lsp = args.list_entry.as_spf_trigger_lsp().unwrap();
Box::new(TriggerLsp {
lsp: todo!(),
sequence: None,
lsp: lsp.lsp_id.to_yang(),
sequence: Some(lsp.seqno),
})
})
.path(isis::lsp_log::event::PATH)
Expand Down
10 changes: 10 additions & 0 deletions holo-isis/src/northbound/yang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use crate::lsdb::LspLogReason;
use crate::northbound::configuration::MetricType;
use crate::packet::consts::LspFlags;
use crate::packet::{AreaAddr, LanId, LevelType, LspId, SystemId};
use crate::spf::SpfType;

// ===== ToYang implementations =====

Expand Down Expand Up @@ -143,6 +144,15 @@ impl ToYang for LspLogReason {
}
}

impl ToYang for SpfType {
fn to_yang(&self) -> Cow<'static, str> {
match self {
SpfType::Full => "full".into(),
SpfType::RouteOnly => "route-only".into(),
}
}
}

// ===== TryFromYang implementations =====

impl TryFromYang for LevelType {
Expand Down
81 changes: 80 additions & 1 deletion holo-isis/src/spf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,23 @@
use std::time::{Duration, Instant};

use chrono::Utc;
use derive_new::new;
use holo_utils::task::TimeoutTask;

use crate::adjacency::Adjacency;
use crate::collections::{Arena, Interfaces};
use crate::debug::Debug;
use crate::error::Error;
use crate::instance::{InstanceArenas, InstanceUpView};
use crate::lsdb::LspEntry;
use crate::lsdb::{LspEntry, LspLogId};
use crate::packet::LevelNumber;
use crate::tasks;

// Maximum size of the SPF log record.
const SPF_LOG_MAX_SIZE: usize = 32;
// Maximum number of trigger LSPs per entry in the SPF log record.
const SPF_LOG_TRIGGER_LSPS_MAX_SIZE: usize = 8;

#[derive(Debug, Default)]
pub struct SpfScheduler {
pub last_event_rcvd: Option<Instant>,
Expand All @@ -29,6 +35,25 @@ pub struct SpfScheduler {
pub delay_timer: Option<TimeoutTask>,
pub hold_down_timer: Option<TimeoutTask>,
pub learn_timer: Option<TimeoutTask>,
pub trigger_lsps: Vec<LspLogId>,
pub schedule_time: Option<Instant>,
}

#[derive(Clone, Copy, Debug)]
pub enum SpfType {
Full,
RouteOnly,
}

#[derive(Debug, new)]
pub struct SpfLogEntry {
pub id: u32,
pub spf_type: SpfType,
pub level: LevelNumber,
pub schedule_time: Instant,
pub start_time: Instant,
pub end_time: Instant,
pub trigger_lsps: Vec<LspLogId>,
}

// SPF Delay State Machine.
Expand Down Expand Up @@ -257,6 +282,16 @@ fn compute_spf(
) {
let spf_sched = instance.state.spf_sched.get_mut(level);

// Get time the SPF was scheduled.
let schedule_time =
spf_sched.schedule_time.take().unwrap_or_else(Instant::now);

// Record time the SPF computation was started.
let start_time = Instant::now();

// Get list of new or updated LSPs that triggered the SPF computation.
let trigger_lsps = std::mem::take(&mut spf_sched.trigger_lsps);

// TODO: Run SPF.

// Update statistics.
Expand All @@ -266,4 +301,48 @@ fn compute_spf(
// Update time of last SPF computation.
let end_time = Instant::now();
spf_sched.last_time = Some(end_time);

// Add entry to SPF log.
log_spf_run(
level,
instance,
SpfType::Full,
schedule_time,
start_time,
end_time,
trigger_lsps,
);
}

// Adds log entry for the SPF run.
fn log_spf_run(
level: LevelNumber,
instance: &mut InstanceUpView<'_>,
spf_type: SpfType,
schedule_time: Instant,
start_time: Instant,
end_time: Instant,
mut trigger_lsps: Vec<LspLogId>,
) {
// Get next log ID.
let log_id = &mut instance.state.spf_log_next_id;
*log_id += 1;

// Get trigger LSPs in log format.
trigger_lsps.truncate(SPF_LOG_TRIGGER_LSPS_MAX_SIZE);

// Add new log entry.
let log_entry = SpfLogEntry::new(
*log_id,
spf_type,
level,
schedule_time,
start_time,
end_time,
trigger_lsps,
);
instance.state.spf_log.push_front(log_entry);

// Remove old entries if necessary.
instance.state.spf_log.truncate(SPF_LOG_MAX_SIZE);
}

0 comments on commit 7332bd4

Please sign in to comment.