Skip to content
Closed
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
12 changes: 12 additions & 0 deletions proto/holo.proto
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,13 @@ message GetRequest {

// Target YANG path.
Path path = 4;

// YANG paths to exclude from the response (matched at any depth).
// A single-element path with just a name (optionally module-qualified
// as "module:name") matches any node with that name. Multi-element
// paths match a specific subtree wherever it occurs.
// Applies only to state data.
repeated Path exclude = 5;
}

message GetResponse {
Expand Down Expand Up @@ -313,6 +320,11 @@ enum SchemaFormat {
message Path {
// Ordered list of elements forming the path.
repeated PathElem elem = 1;

// Maximum depth of the YANG tree traversal beyond the requested path
// (0 = unlimited). A value of 1 returns only immediate children of
// the target. Applies only to state data.
uint32 max_depth = 2;
}

// A single element within a YANG data path.
Expand Down
26 changes: 23 additions & 3 deletions src/grpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,30 @@ impl GrpcClient {
format: DataFormat,
with_defaults: bool,
xpath: Option<String>,
max_depth: u32,
exclude: &[String],
) -> Result<proto::data_tree::Data, Error> {
let path = xpath.map(|x| proto::Path::from_xpath(&x));
let path = match xpath {
Some(x) => {
let mut p = proto::Path::from_xpath(&x);
p.max_depth = max_depth;
Some(p)
}
None if max_depth > 0 => Some(proto::Path {
elem: Vec::new(),
max_depth,
}),
None => None,
};
let exclude =
exclude.iter().map(|s| proto::Path::from_xpath(s)).collect();
let data = self
.rpc_sync_get(proto::GetRequest {
r#type: data_type as i32,
encoding: proto::Encoding::from(format) as i32,
with_defaults,
path,
exclude,
})
.map_err(Error::Backend)?
.into_inner()
Expand Down Expand Up @@ -266,7 +282,8 @@ impl proto::Path {
Some(pos) => {
let name = &segment[..pos];
let mut keys = HashMap::new();
for kv in segment[pos..].split('[').filter(|s| !s.is_empty())
for kv in
segment[pos..].split('[').filter(|s| !s.is_empty())
{
let kv = kv.trim_end_matches(']');
if let Some(eq_pos) = kv.find('=') {
Expand All @@ -287,7 +304,10 @@ impl proto::Path {
}
})
.collect();
proto::Path { elem: elems }
proto::Path {
elem: elems,
max_depth: 0,
}
}
}

Expand Down
68 changes: 63 additions & 5 deletions src/internal_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,16 @@ const XPATH_PROTOCOL: &str =
"/ietf-routing:routing/control-plane-protocols/control-plane-protocol";
const XPATH_RIB: &str = "/ietf-routing:routing/ribs/rib";

/// Excluded from all YangTableBuilder queries by default for performance —
/// BGP RIBs are large sub-trees that slow down unrelated show commands.
const DEFAULT_EXCLUDES: &[&str] = &["ietf-bgp:rib"];

struct YangTableBuilder<'a> {
session: &'a mut Session,
data_type: proto::get_request::DataType,
paths: Vec<(String, Vec<YangTableColumn>)>,
max_depth: u32,
exclude: Vec<String>,
}

struct YangTableColumn {
Expand Down Expand Up @@ -62,6 +68,8 @@ impl<'a> YangTableBuilder<'a> {
session,
data_type,
paths: Vec::new(),
max_depth: 0,
exclude: DEFAULT_EXCLUDES.iter().map(|s| s.to_string()).collect(),
}
}

Expand All @@ -71,6 +79,20 @@ impl<'a> YangTableBuilder<'a> {
self
}

// Sets the maximum depth for the fetch operation.
#[allow(dead_code)]
pub fn max_depth(mut self, depth: u32) -> Self {
self.max_depth = depth;
self
}

// Adds a YANG node name to the exclude list.
#[allow(dead_code)]
pub fn exclude(mut self, name: &str) -> Self {
self.exclude.push(name.to_string());
self
}

// Adds a YANG list key filter to the last added XPath in the builder.
pub fn filter_list_key<S>(mut self, key: &str, value: Option<S>) -> Self
where
Expand Down Expand Up @@ -202,7 +224,13 @@ impl<'a> YangTableBuilder<'a> {
let xpath_req = "/ietf-routing:routing/control-plane-protocols";

// Fetch data.
let data = fetch_data(self.session, self.data_type, xpath_req)?;
let data = fetch_data_filtered(
self.session,
self.data_type,
xpath_req,
self.max_depth,
&self.exclude,
)?;
let Some(dnode) = data.reference() else {
return Ok(());
};
Expand Down Expand Up @@ -258,15 +286,24 @@ fn write_output(
Ok(())
}

fn fetch_data(
fn fetch_data_filtered(
session: &mut Session,
data_type: proto::get_request::DataType,
xpath: &str,
max_depth: u32,
exclude: &[String],
) -> Result<DataTree<'static>, String> {
let yang_ctx = YANG_CTX.get().unwrap();
let data_format = DataFormat::LYB;
let data = session
.get(data_type, data_format, true, Some(xpath.to_owned()))
.get(
data_type,
data_format,
true,
Some(xpath.to_owned()),
max_depth,
exclude,
)
.map_err(|error| format!("% failed to fetch state data: {}", error))?;
DataTree::parse_string(
yang_ctx,
Expand All @@ -278,6 +315,15 @@ fn fetch_data(
.map_err(|error| format!("% failed to parse data: {}", error))
}

#[inline]
fn fetch_data(
session: &mut Session,
data_type: proto::get_request::DataType,
xpath: &str,
) -> Result<DataTree<'static>, String> {
fetch_data_filtered(session, data_type, xpath, 0, &[])
}

// ===== impl DataNodeRef =====

/// Extension methods for DataNodeRef.
Expand Down Expand Up @@ -638,15 +684,27 @@ pub fn cmd_show_state(
) -> Result<bool, CallbackError> {
let xpath = get_opt_arg(&mut args, "xpath");
let format = get_opt_arg(&mut args, "format");
let max_depth = get_opt_arg(&mut args, "depth")
.and_then(|d| d.parse::<u32>().ok())
.unwrap_or_default();
let exclude: Vec<String> = get_opt_arg(&mut args, "exclude")
.map(|s| s.split(',').map(str::to_string).collect())
.unwrap_or_default();
let format = match format.as_deref() {
Some("json") => DataFormat::JSON,
Some("xml") => DataFormat::XML,
Some(_) => panic!("unknown format"),
None => DataFormat::JSON,
};

match session.get(proto::get_request::DataType::State, format, false, xpath)
{
match session.get(
proto::get_request::DataType::State,
format,
false,
xpath,
max_depth,
&exclude,
) {
Ok(proto::data_tree::Data::DataString(data)) => {
write_output(session, &data)?;
}
Expand Down
16 changes: 14 additions & 2 deletions src/internal_commands.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,29 @@
</token>
<token name="state" help="Show operational state." cmd="cmd_show_state">
<token name="xpath" help="XPath expression.">
<token name="xpath" argument="xpath" kind="string" help="XPath expression." cmd="cmd_show_state">
<token name="xpath" argument="xpath" kind="string" help="XPath expression." cmd="cmd_show_state">
<token name="format" help="Data format.">
<token name="json" argument="format" help="JSON output format." cmd="cmd_show_state"/>
<token name="xml" argument="format" help="XML output format." cmd="cmd_show_state"/>
<token name="xml" argument="format" help="XML output format." cmd="cmd_show_state"/>
</token>
<token name="depth" help="Max depth YANG tree traversal.">
<token name="N" argument="depth" kind="string" help="Max depth (positive integer)." cmd="cmd_show_state"/>
</token>
<token name="exclude" help="YANG node names to exclude from response.">
<token name="LIST" argument="exclude" kind="string" help="Comma-separated names (name1,name2,...)." cmd="cmd_show_state"/>
</token>
</token>
</token>
<token name="format" help="Data format.">
<token name="json" argument="format" help="JSON output format." cmd="cmd_show_state"/>
<token name="xml" argument="format" help="XML output format." cmd="cmd_show_state"/>
</token>
<token name="depth" help="Max depth YANG tree traversal.">
<token name="N" argument="depth" kind="string" help="Max depth (positive integer)." cmd="cmd_show_state"/>
</token>
<token name="exclude" help="YANG node names to exclude from response.">
<token name="LIST" argument="exclude" kind="string" help="Comma-separated names (name1,name2,...)." cmd="cmd_show_state"/>
</token>
</token>
<token name="yang" help="YANG information.">
<token name="modules" help="Show loaded YANG modules." cmd="cmd_show_yang_modules"/>
Expand Down
14 changes: 12 additions & 2 deletions src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ impl Session {
data_format,
false,
None,
0,
&[],
)
.unwrap();
let running = DataTree::parse_string(
Expand Down Expand Up @@ -314,9 +316,17 @@ impl Session {
format: DataFormat,
with_defaults: bool,
xpath: Option<String>,
max_depth: u32,
exclude: &[String],
) -> Result<proto::data_tree::Data, Error> {
self.grpc_client
.get(data_type, format, with_defaults, xpath)
self.grpc_client.get(
data_type,
format,
with_defaults,
xpath,
max_depth,
exclude,
)
}

pub fn execute(
Expand Down
Loading