diff --git a/holo-bfd/src/northbound/notification.rs b/holo-bfd/src/northbound/notification.rs index 5e689af2..46d8f1b0 100644 --- a/holo-bfd/src/northbound/notification.rs +++ b/holo-bfd/src/northbound/notification.rs @@ -36,24 +36,20 @@ fn state_change_singlehop( use yang::singlehop_notification::{self, SinglehopNotification}; let data = SinglehopNotification { - local_discr: Some(sess.state.local_discr.to_string().into()), - remote_discr: sess - .state - .remote - .as_ref() - .map(|remote| remote.discr.to_string().into()), + local_discr: Some(sess.state.local_discr), + remote_discr: sess.state.remote.as_ref().map(|remote| remote.discr), new_state: Some(sess.state.local_state.to_yang()), state_change_reason: Some(sess.state.local_diag.to_yang()), time_of_last_state_change: sess .statistics .last_state_change_time - .map(|time| time.to_rfc3339().into()), - dest_addr: Some(dst.to_string().into()), - source_addr: sess.config.src.map(|src| src.to_string().into()), - session_index: Some(sess.id.to_string().into()), + .as_ref(), + dest_addr: Some(dst), + source_addr: sess.config.src.as_ref(), + session_index: Some(sess.id as u32), path_type: Some(sess.key.path_type().to_yang()), interface: Some(ifname.into()), - echo_enabled: Some("false".into()), + echo_enabled: Some(false), }; notification::send(nb_tx, singlehop_notification::PATH, data); } @@ -67,21 +63,17 @@ fn state_change_multihop( use yang::multihop_notification::{self, MultihopNotification}; let data = MultihopNotification { - local_discr: Some(sess.state.local_discr.to_string().into()), - remote_discr: sess - .state - .remote - .as_ref() - .map(|remote| remote.discr.to_string().into()), + local_discr: Some(sess.state.local_discr), + remote_discr: sess.state.remote.as_ref().map(|remote| remote.discr), new_state: Some(sess.state.local_state.to_yang()), state_change_reason: Some(sess.state.local_diag.to_yang()), time_of_last_state_change: sess .statistics .last_state_change_time - .map(|time| time.to_rfc3339().into()), - dest_addr: Some(dst.to_string().into()), - source_addr: Some(src.to_string().into()), - session_index: Some(sess.id.to_string().into()), + .as_ref(), + dest_addr: Some(dst), + source_addr: Some(src), + session_index: Some(sess.id as u32), path_type: Some(sess.key.path_type().to_yang()), }; notification::send(nb_tx, multihop_notification::PATH, data); diff --git a/holo-bgp/src/northbound/notification.rs b/holo-bgp/src/northbound/notification.rs index 7c9fb87c..793d09d9 100644 --- a/holo-bgp/src/northbound/notification.rs +++ b/holo-bgp/src/northbound/notification.rs @@ -54,22 +54,18 @@ pub(crate) fn backward_transition( remote_addr: Some(nbr.remote_addr.to_string().into()), notification_received: nbr.notification_rcvd.as_ref().map( |(time, notif)| NotificationReceived { - last_notification: Some(time.to_rfc3339().into()), + last_notification: Some(time), last_error: Some(notif.to_yang()), - last_error_code: Some(notif.error_code.to_string().into()), - last_error_subcode: Some( - notif.error_subcode.to_string().into(), - ), + last_error_code: Some(notif.error_code), + last_error_subcode: Some(notif.error_subcode), }, ), notification_sent: nbr.notification_sent.as_ref().map( |(time, notif)| NotificationSent { - last_notification: Some(time.to_rfc3339().into()), + last_notification: Some(time), last_error: Some(notif.to_yang()), - last_error_code: Some(notif.error_code.to_string().into()), - last_error_subcode: Some( - notif.error_subcode.to_string().into(), - ), + last_error_code: Some(notif.error_code), + last_error_subcode: Some(notif.error_subcode), }, ), }; diff --git a/holo-ldp/src/northbound/notification.rs b/holo-ldp/src/northbound/notification.rs index 15bc25f9..49105998 100644 --- a/holo-ldp/src/northbound/notification.rs +++ b/holo-ldp/src/northbound/notification.rs @@ -51,11 +51,11 @@ pub(crate) fn mpls_ldp_hello_adjacency_event( protocol_name: Some(instance_name.into()), event_type: Some(event_type.into()), targeted: ifname.is_none().then_some(Targeted { - target_address: Some(addr.to_string().into()), + target_address: Some(addr), }), link: ifname.map(|ifname| Link { next_hop_interface: Some(ifname.into()), - next_hop_address: Some(addr.to_string().into()), + next_hop_address: Some(addr), }), }; notification::send(nb_tx, mpls_ldp_hello_adjacency_event::PATH, data); @@ -72,7 +72,7 @@ pub(crate) fn mpls_ldp_fec_event( let data = MplsLdpFecEvent { event_type: Some(event_type.into()), protocol_name: Some(instance_name.into()), - fec: Some(fec.inner.prefix.to_string().into()), + fec: Some(&fec.inner.prefix), }; notification::send(nb_tx, mpls_ldp_fec_event::PATH, data); } diff --git a/holo-northbound/build.rs b/holo-northbound/build.rs index 3449ac8c..675ac3d3 100644 --- a/holo-northbound/build.rs +++ b/holo-northbound/build.rs @@ -14,7 +14,58 @@ use check_keyword::CheckKeyword; use convert_case::{Boundary, Case, Casing}; use holo_yang as yang; use holo_yang::YANG_IMPLEMENTED_MODULES; -use yang2::schema::{DataValue, SchemaNode, SchemaNodeKind, SchemaPathFormat}; +use yang2::schema::{ + DataValue, DataValueType, SchemaNode, SchemaNodeKind, SchemaPathFormat, +}; + +const HEADER: &str = r#" +use std::borrow::Cow; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::time::{Duration, Instant}; + +use chrono::{DateTime, Utc}; +use holo_yang::{YangObject, YangPath, YANG_CTX}; +use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network}; +use yang2::data::DataNodeRef; +use yang2::schema::SchemaModule; + +fn timer_secs16_to_yang(timer: &Duration) -> String { + let remaining = timer.as_secs(); + // Round up the remaining time to 1 in case it's less than one second. + let remaining = if remaining == 0 { 1 } else { remaining }; + let remaining = u16::try_from(remaining).unwrap_or(u16::MAX); + remaining.to_string() +} + +#[allow(dead_code)] +fn timer_secs32_to_yang(timer: &Duration) -> String { + let remaining = timer.as_secs(); + // Round up the remaining time to 1 in case it's less than one second. + let remaining = if remaining == 0 { 1 } else { remaining }; + let remaining = u32::try_from(remaining).unwrap_or(u32::MAX); + remaining.to_string() +} + +fn timer_millis_to_yang(timer: &Duration) -> String { + let remaining = timer.as_millis(); + // Round up the remaining time to 1 in case it's less than one millisecond. + let remaining = if remaining == 0 { 1 } else { remaining }; + let remaining = u32::try_from(remaining).unwrap_or(u32::MAX); + remaining.to_string() +} + +fn timeticks_to_yang(timeticks: &Instant) -> String { + let uptime = Instant::now() - *timeticks; + let uptime = u32::try_from(uptime.as_millis() / 10).unwrap_or(u32::MAX); + uptime.to_string() +} + +fn timeticks64_to_yang(timeticks: &Instant) -> String { + let uptime = Instant::now() - *timeticks; + let uptime = u64::try_from(uptime.as_millis() / 10).unwrap_or(u64::MAX); + uptime.to_string() +} +"#; struct StructBuilder<'a> { level: usize, @@ -88,7 +139,7 @@ impl<'a> StructBuilder<'a> { snode_normalized_name(snode, Case::Pascal) ) } else { - "Cow<'a, str>".to_owned() + snode_type_map(snode).to_owned() }; writeln!( @@ -98,6 +149,14 @@ impl<'a> StructBuilder<'a> { ) .unwrap(); } + if self.fields.iter().all(|snode| snode_is_base_type(snode)) { + writeln!( + output, + "{}_marker: std::marker::PhantomData<&'a str>,", + indent2 + ) + .unwrap(); + } writeln!(output, "{}}}", indent1).unwrap(); // YangObject trait implementation. @@ -114,6 +173,12 @@ impl<'a> StructBuilder<'a> { indent2 ) .unwrap(); + writeln!( + output, + "{}let module: Option<&SchemaModule<'_>> = None;", + indent3 + ) + .unwrap(); for snode in &self.fields { let field_name = snode_normalized_name(snode, Case::Snake); let module = snode.module(); @@ -137,8 +202,6 @@ impl<'a> StructBuilder<'a> { .unwrap(); writeln!(output, "{}let module = Some(&module);", indent4) .unwrap(); - } else { - writeln!(output, "{}let module = None;", indent4).unwrap(); } if snode.kind() == SchemaNodeKind::Container { @@ -156,12 +219,13 @@ impl<'a> StructBuilder<'a> { ) .unwrap(); } else { + let value = snode_type_value(snode, &field_name); writeln!( output, "{}dnode.new_term(module, \"{}\", {}).unwrap();", indent4, snode.name(), - field_name + value ) .unwrap(); } @@ -208,6 +272,133 @@ fn snode_normalized_name(snode: &SchemaNode<'_>, case: Case) -> String { name } +fn snode_is_base_type(snode: &SchemaNode<'_>) -> bool { + matches!( + snode.base_type(), + Some( + DataValueType::Uint8 + | DataValueType::Uint16 + | DataValueType::Uint32 + | DataValueType::Uint64 + | DataValueType::Int8 + | DataValueType::Int16 + | DataValueType::Int32 + | DataValueType::Int64 + | DataValueType::Bool + | DataValueType::Empty + ) + ) +} + +fn snode_typedef_map(snode: &SchemaNode<'_>) -> Option<&'static str> { + match snode.typedef_name().as_deref() { + Some("ip-address") => Some("&'a IpAddr"), + Some("ipv4-address") => Some("&'a Ipv4Addr"), + Some("ipv6-address") => Some("&'a Ipv6Addr"), + Some("ip-prefix") => Some("&'a IpNetwork"), + Some("ipv4-prefix") => Some("&'a Ipv4Network"), + Some("ipv6-prefix") => Some("&'a Ipv6Network"), + Some("date-and-time") => Some("&'a DateTime"), + Some("timer-value-seconds16") => Some("&'a Duration"), + Some("timer-value-seconds32") => Some("&'a Duration"), + Some("timer-value-milliseconds") => Some("&'a Duration"), + Some("timeticks") => Some("&'a Instant"), + Some("timeticks64") => Some("&'a Instant"), + _ => None, + } +} + +fn snode_typedef_value( + snode: &SchemaNode<'_>, + field_name: &str, +) -> Option { + match snode.typedef_name().as_deref() { + Some("ip-address") | Some("ipv4-address") | Some("ipv6-address") + | Some("ip-prefix") | Some("ipv4-prefix") | Some("ipv6-prefix") => { + Some(format!("Some(&{}.to_string())", field_name)) + } + Some("date-and-time") => { + Some(format!("Some(&{}.to_rfc3339())", field_name)) + } + Some("timer-value-seconds16") => { + Some(format!("Some(&timer_secs16_to_yang({}))", field_name)) + } + Some("timer-value-seconds32") => { + Some(format!("Some(&timer_secs32_to_yang({}))", field_name)) + } + Some("timer-value-milliseconds") => { + Some(format!("Some(&timer_millis_to_yang({}))", field_name)) + } + Some("timeticks") => { + Some(format!("Some(&timeticks_to_yang({}))", field_name)) + } + Some("timeticks64") => { + Some(format!("Some(&timeticks64_to_yang({}))", field_name)) + } + _ => None, + } +} + +fn snode_type_map(snode: &SchemaNode<'_>) -> &'static str { + if let Some(typedef) = snode_typedef_map(snode) { + return typedef; + } + + match snode.base_type().unwrap() { + DataValueType::Unknown => panic!("Unknown leaf type"), + DataValueType::Uint8 => "u8", + DataValueType::Uint16 => "u16", + DataValueType::Uint32 => "u32", + DataValueType::Uint64 => "u64", + DataValueType::Int8 => "i8", + DataValueType::Int16 => "i16", + DataValueType::Int32 => "i32", + DataValueType::Int64 => "i64", + DataValueType::Bool => "bool", + DataValueType::Empty => "()", + DataValueType::String + | DataValueType::Union + | DataValueType::Dec64 + | DataValueType::Enum + | DataValueType::IdentityRef + | DataValueType::InstanceId + | DataValueType::LeafRef + | DataValueType::Binary + | DataValueType::Bits => "Cow<'a, str>", + } +} + +fn snode_type_value(snode: &SchemaNode<'_>, field_name: &str) -> String { + if let Some(typedef_value) = snode_typedef_value(snode, field_name) { + return typedef_value; + } + + match snode.base_type().unwrap() { + DataValueType::Unknown => panic!("Unknown leaf type"), + DataValueType::Uint8 + | DataValueType::Uint16 + | DataValueType::Uint32 + | DataValueType::Uint64 + | DataValueType::Int8 + | DataValueType::Int16 + | DataValueType::Int32 + | DataValueType::Int64 + | DataValueType::Bool => { + format!("Some(&{}.to_string())", field_name) + } + DataValueType::Empty => "None".to_owned(), + DataValueType::String + | DataValueType::Union + | DataValueType::Dec64 + | DataValueType::Enum + | DataValueType::IdentityRef + | DataValueType::InstanceId + | DataValueType::LeafRef + | DataValueType::Binary + | DataValueType::Bits => format!("Some({})", field_name), + } +} + fn generate_module(output: &mut String, snode: &SchemaNode<'_>, level: usize) { let indent = " ".repeat(level * 2); @@ -393,11 +584,7 @@ fn main() { // Generate file header. let mut output = String::new(); - writeln!(output, "use std::borrow::Cow;").unwrap(); - writeln!(output, "use holo_yang::{{YangObject, YangPath, YANG_CTX}};") - .unwrap(); - writeln!(output, "use yang2::data::DataNodeRef;").unwrap(); - writeln!(output).unwrap(); + writeln!(output, "{}", HEADER).unwrap(); // Generate modules. for snode in yang_ctx diff --git a/holo-ospf/src/northbound/notification.rs b/holo-ospf/src/northbound/notification.rs index bb3a6d33..5adf8477 100644 --- a/holo-ospf/src/northbound/notification.rs +++ b/holo-ospf/src/northbound/notification.rs @@ -5,6 +5,7 @@ // use std::net::Ipv4Addr; +use std::time::Duration; use holo_northbound::{notification, yang}; use holo_yang::ToYang; @@ -52,13 +53,14 @@ pub(crate) fn if_config_error( use yang::if_config_error::interface::Interface; use yang::if_config_error::{self, IfConfigError}; + let src = (*src).into(); let data = IfConfigError { routing_protocol_name: Some(instance.name.into()), address_family: Some(instance.state.af.to_yang()), interface: Some(Interface { interface: Some(ifname.into()), }), - packet_source: Some(src.to_string().into()), + packet_source: Some(&src), packet_type: Some(pkt_type.to_yang()), error: Some(error.to_yang()), }; @@ -75,6 +77,7 @@ pub(crate) fn nbr_state_change( use yang::nbr_state_change::interface::Interface; use yang::nbr_state_change::{self, NbrStateChange}; + let nbr_src = nbr.src.into(); let data = NbrStateChange { routing_protocol_name: Some(instance.name.into()), address_family: Some(instance.state.af.to_yang()), @@ -82,7 +85,7 @@ pub(crate) fn nbr_state_change( interface: Some(iface.name.as_str().into()), }), neighbor_router_id: Some(nbr.router_id.to_string().into()), - neighbor_ip_addr: Some(nbr.src.to_string().into()), + neighbor_ip_addr: Some(&nbr_src), state: Some(nbr.state.to_yang()), }; notification::send(&instance.tx.nb, nbr_state_change::PATH, data); @@ -101,6 +104,8 @@ pub(crate) fn nbr_restart_helper_enter( self, NbrRestartHelperStatusChange, }; + let nbr_src = nbr.src.into(); + let age = Duration::from_secs(age.into()); let data = NbrRestartHelperStatusChange { routing_protocol_name: Some(instance.name.into()), address_family: Some(instance.state.af.to_yang()), @@ -108,9 +113,9 @@ pub(crate) fn nbr_restart_helper_enter( interface: Some(iface.name.as_str().into()), }), neighbor_router_id: Some(nbr.router_id.to_string().into()), - neighbor_ip_addr: Some(nbr.src.to_string().into()), + neighbor_ip_addr: Some(&nbr_src), status: Some("helping".into()), - age: Some(age.to_string().into()), + age: Some(&age), exit_reason: None, }; notification::send( @@ -133,6 +138,7 @@ pub(crate) fn nbr_restart_helper_exit( self, NbrRestartHelperStatusChange, }; + let nbr_src = nbr.src.into(); let data = NbrRestartHelperStatusChange { routing_protocol_name: Some(instance.name.into()), address_family: Some(instance.state.af.to_yang()), @@ -140,7 +146,7 @@ pub(crate) fn nbr_restart_helper_exit( interface: Some(iface.name.as_str().into()), }), neighbor_router_id: Some(nbr.router_id.to_string().into()), - neighbor_ip_addr: Some(nbr.src.to_string().into()), + neighbor_ip_addr: Some(&nbr_src), status: Some("not-helping".into()), age: None, exit_reason: Some(reason.to_yang()), @@ -162,13 +168,14 @@ pub(crate) fn if_rx_bad_packet( use yang::if_rx_bad_packet::interface::Interface; use yang::if_rx_bad_packet::{self, IfRxBadPacket}; + let src = src.into(); let data = IfRxBadPacket { routing_protocol_name: Some(instance.name.into()), address_family: Some(instance.state.af.to_yang()), interface: Some(Interface { interface: Some(iface.name.as_str().into()), }), - packet_source: Some(src.to_string().into()), + packet_source: Some(&src), // TODO: set the packet-type whenever possible. packet_type: None, }; @@ -184,9 +191,10 @@ pub(crate) fn if_rx_bad_lsa( { use yang::if_rx_bad_lsa::{self, IfRxBadLsa}; + let src = src.into(); let data = IfRxBadLsa { routing_protocol_name: Some(instance.name.into()), - packet_source: Some(src.to_string().into()), + packet_source: Some(&src), error: Some(error.to_yang()), }; notification::send(&instance.tx.nb, if_rx_bad_lsa::PATH, data); @@ -205,7 +213,7 @@ pub(crate) fn sr_index_out_of_range( let data = SegmentRoutingIndexOutOfRange { received_target: Some(nbr_router_id.to_string().into()), - received_index: Some(index.to_string().into()), + received_index: Some(index), routing_protocol: Some(instance.name.into()), }; notification::send(