Skip to content

Commit

Permalink
daemon-grpc: introduce the GetSchema RPC
Browse files Browse the repository at this point in the history
This RPC mirrors NETCONF's get-schema operation but offers more
flexibility, as it can fetch YANG submodules in addition to YANG
modules.

The primary use case is for a CLI program to retrieve the list of
supported YANG modules at startup, then use this RPC to fetch any
missing modules.

Signed-off-by: Renato Westphal <[email protected]>
  • Loading branch information
rwestphal committed Oct 14, 2024
1 parent 277809b commit ffa4a67
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 0 deletions.
77 changes: 77 additions & 0 deletions holo-daemon/src/northbound/client/grpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use yang3::data::{
Data, DataDiff, DataFormat, DataOperation, DataParserFlags,
DataPrinterFlags, DataTree, DataValidationFlags,
};
use yang3::schema::{SchemaOutputFormat, SchemaPrinterFlags};

use crate::northbound::client::api;
use crate::{config, northbound};
Expand Down Expand Up @@ -88,6 +89,65 @@ impl proto::Northbound for NorthboundService {
Ok(Response::new(reply))
}

async fn get_schema(
&self,
grpc_request: Request<proto::GetSchemaRequest>,
) -> Result<Response<proto::GetSchemaResponse>, Status> {
let grpc_request = grpc_request.into_inner();
debug_span!("northbound").in_scope(|| {
debug_span!("client", name = "grpc").in_scope(|| {
debug!("received GetSchema() request");
trace!("{:?}", grpc_request);
});
});

// Lookup schema module.
let yang_ctx = YANG_CTX.get().unwrap();
let module_name = grpc_request.module_name;
let module_rev = get_optional_string(grpc_request.module_revision);
let submodule_name = get_optional_string(grpc_request.submodule_name);
let submodule_rev =
get_optional_string(grpc_request.submodule_revision);
let format = proto::SchemaFormat::try_from(grpc_request.format)
.map_err(|_| Status::invalid_argument("Invalid schema format"))?;

// Get module.
let module = match module_rev {
Some(module_rev) => {
yang_ctx.get_module(&module_name, Some(&module_rev))
}
None => yang_ctx.get_module_latest(&module_name),
}
.ok_or_else(|| Status::not_found("YANG module not found"))?;

let data = match submodule_name {
Some(submodule_name) => {
// Get submodule.
let submodule = match submodule_rev {
Some(submodule_rev) => module
.get_submodule(&submodule_name, Some(&submodule_rev)),
None => module.get_submodule_latest(&submodule_name),
}
.ok_or_else(|| Status::not_found("YANG submodule not found"))?;

// Print submodule data based on the requested format.
submodule
.print_string(format.into(), SchemaPrinterFlags::empty())
.expect("Failed to print YANG submodule")
}
None => {
// Print module data based on the requested format.
module
.print_string(format.into(), SchemaPrinterFlags::empty())
.expect("Failed to print YANG module")
}
};

// Return schema data to the gRPC client.
let grpc_response = proto::GetSchemaResponse { data };
Ok(Response::new(grpc_response))
}

async fn get(
&self,
grpc_request: Request<proto::GetRequest>,
Expand Down Expand Up @@ -400,6 +460,15 @@ impl From<proto::Encoding> for DataFormat {
}
}

impl From<proto::SchemaFormat> for SchemaOutputFormat {
fn from(format: proto::SchemaFormat) -> SchemaOutputFormat {
match format {
proto::SchemaFormat::Yang => SchemaOutputFormat::YANG,
proto::SchemaFormat::Yin => SchemaOutputFormat::YIN,
}
}
}

impl TryFrom<i32> for api::DataType {
type Error = Status;

Expand All @@ -424,6 +493,14 @@ fn get_timestamp() -> i64 {
.as_secs() as i64
}

fn get_optional_string(data: String) -> Option<String> {
if data.is_empty() {
None
} else {
Some(data)
}
}

fn data_tree_init(
dtree: &DataTree<'static>,
encoding: proto::Encoding,
Expand Down
37 changes: 37 additions & 0 deletions proto/holo.proto
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ service Northbound {
// Retrieve the capabilities supported by the target.
rpc Capabilities(CapabilitiesRequest) returns (CapabilitiesResponse) {}

// Retrieve the specified schema data from the target.
rpc GetSchema(GetSchemaRequest) returns (GetSchemaResponse) {}

// Retrieve configuration data, state data or both from the target.
rpc Get(GetRequest) returns (GetResponse) {}

Expand Down Expand Up @@ -71,6 +74,34 @@ message CapabilitiesResponse {
repeated Encoding supported_encodings = 3;
}

//
// RPC: GetSchema()
//
message GetSchemaRequest {
// The name of the module to retrieve.
string module_name = 1;

// The specific revision of the module (optional).
string module_revision = 2;

// The name of the submodule to retrieve (optional).
string submodule_name = 3;

// The specific revision of the submodule (optional).
string submodule_revision = 4;

// The desired format of the schema output (optional).
SchemaFormat format = 5;
}

message GetSchemaResponse {
// - grpc::StatusCode::OK: Success.
// - grpc::StatusCode::NOT_FOUND: Schema module not found.

// The requested schema data.
string data = 1;
}

//
// RPC: Get()
//
Expand Down Expand Up @@ -243,6 +274,12 @@ enum Encoding {
LYB = 2;
}

// Supported schema formats.
enum SchemaFormat {
YANG = 0;
YIN = 1;
}

// YANG instance data.
message DataTree {
// The encoding format of the data.
Expand Down

0 comments on commit ffa4a67

Please sign in to comment.