Skip to main content

netget/client/gnmi/
request.rs

1use crate::server::gnmi::{
2    proto::gnmi as pb,
3    semantic::{self, Model},
4    value::{self, Path, Update},
5};
6use serde::Deserialize;
7use serde_json::Value;
8use tonic::Status;
9fn proto() -> String {
10    "PROTO".into()
11}
12fn all() -> String {
13    "ALL".into()
14}
15#[derive(Deserialize)]
16#[serde(deny_unknown_fields)]
17struct Get {
18    #[serde(default)]
19    prefix: Path,
20    #[serde(default)]
21    path: Vec<Path>,
22    #[serde(default = "all")]
23    data_type: String,
24    #[serde(default = "proto")]
25    encoding: String,
26    #[serde(default)]
27    use_models: Vec<Model>,
28}
29#[derive(Deserialize)]
30#[serde(deny_unknown_fields)]
31struct Set {
32    #[serde(default)]
33    prefix: Path,
34    #[serde(default)]
35    delete: Vec<Path>,
36    #[serde(default)]
37    replace: Vec<Update>,
38    #[serde(default)]
39    update: Vec<Update>,
40}
41#[derive(Deserialize)]
42#[serde(deny_unknown_fields)]
43struct Subscription {
44    path: Path,
45    #[serde(default)]
46    mode: Option<String>,
47}
48#[derive(Deserialize)]
49#[serde(deny_unknown_fields)]
50struct Subscribe {
51    #[serde(default)]
52    prefix: Path,
53    mode: String,
54    subscription: Vec<Subscription>,
55    #[serde(default = "proto")]
56    encoding: String,
57    #[serde(default)]
58    updates_only: bool,
59    #[serde(default)]
60    use_models: Vec<Model>,
61}
62pub enum Request {
63    Capabilities(pb::CapabilityRequest),
64    Get(pb::GetRequest),
65    Set(pb::SetRequest),
66    Subscribe(pb::SubscriptionList),
67    Poll,
68    Cancel,
69    Wait,
70    Disconnect,
71}
72fn decode<T: serde::de::DeserializeOwned>(value: &Value) -> Result<T, Status> {
73    serde_json::from_value(value.clone())
74        .map_err(|_| Status::invalid_argument("invalid typed gNMI request"))
75}
76pub fn parse(action: &Value) -> Result<Request, Status> {
77    super::check_action(action)?;
78    Ok(match action["type"].as_str().unwrap_or("") {
79        "disconnect" => Request::Disconnect,
80        "wait_for_more" => Request::Wait,
81        "gnmi_poll" => Request::Poll,
82        "gnmi_cancel" => Request::Cancel,
83        "gnmi_capabilities" => Request::Capabilities(pb::CapabilityRequest { extension: vec![] }),
84        "gnmi_get" => {
85            let request: Get = decode(&action["request"])?;
86            let kind = match request.data_type.as_str() {
87                "ALL" => 0,
88                "CONFIG" => 1,
89                "STATE" => 2,
90                "OPERATIONAL" => 3,
91                _ => return Err(Status::invalid_argument("unknown data_type")),
92            };
93            let request = value::checked(pb::GetRequest {
94                prefix: Some(request.prefix.into_proto()?),
95                path: request
96                    .path
97                    .into_iter()
98                    .map(Path::into_proto)
99                    .collect::<Result<_, _>>()?,
100                r#type: kind,
101                encoding: value::encoding(&request.encoding)?,
102                use_models: semantic::model_proto(request.use_models)?,
103                extension: vec![],
104            })?;
105            semantic::get_request(&request)?;
106            Request::Get(request)
107        }
108        "gnmi_set" => {
109            let request: Set = decode(&action["request"])?;
110            let request = value::checked(pb::SetRequest {
111                prefix: Some(request.prefix.into_proto()?),
112                delete: request
113                    .delete
114                    .into_iter()
115                    .map(Path::into_proto)
116                    .collect::<Result<_, _>>()?,
117                replace: request
118                    .replace
119                    .into_iter()
120                    .map(Update::into_proto)
121                    .collect::<Result<_, _>>()?,
122                update: request
123                    .update
124                    .into_iter()
125                    .map(Update::into_proto)
126                    .collect::<Result<_, _>>()?,
127                union_replace: vec![],
128                extension: vec![],
129            })?;
130            semantic::set_request(&request)?;
131            Request::Set(request)
132        }
133        "gnmi_subscribe" => {
134            let request: Subscribe = decode(&action["request"])?;
135            let mode = match request.mode.as_str() {
136                "STREAM" => 0,
137                "ONCE" => 1,
138                "POLL" => 2,
139                _ => return Err(Status::invalid_argument("unknown subscribe mode")),
140            };
141            let list = pb::SubscriptionList {
142                prefix: Some(request.prefix.into_proto()?),
143                subscription: request
144                    .subscription
145                    .into_iter()
146                    .map(|s| {
147                        let mode = match s.mode.as_deref().unwrap_or("TARGET_DEFINED") {
148                            "TARGET_DEFINED" => 0,
149                            "ON_CHANGE" => 1,
150                            _ => return Err(Status::unimplemented("SAMPLE is excluded")),
151                        };
152                        Ok(pb::Subscription {
153                            path: Some(s.path.into_proto()?),
154                            mode,
155                            sample_interval: 0,
156                            suppress_redundant: false,
157                            heartbeat_interval: 0,
158                        })
159                    })
160                    .collect::<Result<_, Status>>()?,
161                qos: None,
162                mode,
163                allow_aggregation: false,
164                use_models: semantic::model_proto(request.use_models)?,
165                encoding: value::encoding(&request.encoding)?,
166                updates_only: request.updates_only,
167            };
168            semantic::subscription(&list)?;
169            value::checked(pb::SubscribeRequest {
170                request: Some(pb::subscribe_request::Request::Subscribe(list.clone())),
171                extension: vec![],
172            })?;
173            Request::Subscribe(list)
174        }
175        _ => return Err(Status::invalid_argument("unknown gNMI client action")),
176    })
177}