pub struct Session { /* private fields */ }Expand description
A NETCONF session with pipelining support.
Architecture:
- Writer: the session holds the write half of the stream and a codec for encoding outgoing RPCs.
- Reader task: a background tokio task reads framed messages from
the read half, classifies them (
<rpc-reply>vs<notification>), and routes them to the correct handler. - Pipelining:
rpc_send()writes an RPC and returns anRpcFuturewithout waiting for the reply. Multiple RPCs can be in flight simultaneously.
Implementations§
Source§impl Session
impl Session
Sourcepub async fn connect(
host: &str,
port: u16,
username: &str,
password: &str,
) -> Result<Self>
pub async fn connect( host: &str, port: u16, username: &str, password: &str, ) -> Result<Self>
Connect to a NETCONF server over SSH with password authentication.
Examples found in repository?
4async fn main() -> netconf_rust::Result<()> {
5 // --- Connect ---
6 let mut session = Session::connect("localhost", 830, "netconf", "netconf").await?;
7
8 println!("Session ID: {}", session.session_id());
9 println!("Framing: {:?}", session.framing_mode());
10 println!("Capabilities: {}", session.server_capabilities().join(", "));
11
12 // --- Get full running config ---
13 let config = session.get_config(Datastore::Running, None).await?;
14 println!("\n=== Running config ===\n{config}");
15
16 // --- Filtered get-config with subtree filter ---
17 let interfaces = session
18 .get_config(
19 Datastore::Running,
20 Some(r#"<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"/>"#),
21 )
22 .await?;
23 println!("\n=== Interfaces ===\n{interfaces}");
24
25 // --- Zero-copy DataPayload ---
26 let payload = session.get_config_payload(Datastore::Running, None).await?;
27 println!("\n=== Config payload size: {} bytes ===", payload.len());
28
29 // Stream XML events directly from the payload's bytes
30 let mut reader = payload.reader();
31 let mut element_count = 0;
32 loop {
33 match reader.read_event() {
34 Ok(quick_xml::events::Event::Start(_)) => element_count += 1,
35 Ok(quick_xml::events::Event::Eof) => break,
36 _ => {}
37 }
38 }
39 println!("Total XML elements: {element_count}");
40
41 // --- Edit config with candidate commit ---
42 // Note: NACM may deny access to certain data models depending on the user's
43 // permissions. We use the netconf-server model which the netconf user can write.
44 session.lock(Datastore::Candidate).await?;
45 println!("\n=== Locked candidate ===");
46
47 match session
48 .edit_config(
49 Datastore::Candidate,
50 r#"
51 <netconf-server xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-server">
52 <listen>
53 <endpoint>
54 <name>default-ssh</name>
55 <ssh>
56 <tcp-server-parameters>
57 <local-address>0.0.0.0</local-address>
58 </tcp-server-parameters>
59 </ssh>
60 </endpoint>
61 </listen>
62 </netconf-server>
63 "#,
64 )
65 .await
66 {
67 Ok(()) => {
68 println!("Edited candidate config");
69 session.commit().await?;
70 println!("Committed");
71 }
72 Err(e) => {
73 eprintln!("edit-config failed (NACM?): {e}");
74 }
75 }
76
77 session.unlock(Datastore::Candidate).await?;
78 println!("Unlocked candidate");
79
80 // --- Pipelining: send 3 RPCs before awaiting any replies ---
81 println!("\n=== Pipelining ===");
82 let fut1 = session
83 .rpc_send("<get-config><source><running/></source></get-config>")
84 .await?;
85 let fut2 = session
86 .rpc_send("<get-config><source><running/></source></get-config>")
87 .await?;
88 let fut3 = session
89 .rpc_send("<get-config><source><running/></source></get-config>")
90 .await?;
91
92 println!(
93 "Sent 3 RPCs (message-ids: {}, {}, {})",
94 fut1.message_id(),
95 fut2.message_id(),
96 fut3.message_id()
97 );
98
99 let reply1 = fut1.response().await?;
100 let reply2 = fut2.response().await?;
101 let reply3 = fut3.response().await?;
102 println!(
103 "Got 3 replies (message-ids: {}, {}, {})",
104 reply1.message_id, reply2.message_id, reply3.message_id
105 );
106
107 // --- Raw RPC for operations not covered by convenience methods ---
108 println!("\n=== Raw RPC ===");
109 let reply = session
110 .rpc_raw("<get-schema><identifier>ietf-interfaces</identifier></get-schema>")
111 .await?;
112 match reply.body {
113 netconf_rust::RpcReplyBody::Data(payload) => {
114 println!("Got schema ({} bytes)", payload.len())
115 }
116 netconf_rust::RpcReplyBody::Ok => println!("ok"),
117 netconf_rust::RpcReplyBody::Error(errors) => {
118 for e in &errors {
119 eprintln!("Error: {}: {}", e.error_tag, e.error_message);
120 }
121 }
122 }
123
124 // --- Graceful close ---
125 session.close_session().await?;
126 println!("\nSession closed (state: {})", session.state());
127
128 Ok(())
129}Sourcepub async fn connect_with_config(
host: &str,
port: u16,
username: &str,
password: &str,
config: SessionConfig,
) -> Result<Self>
pub async fn connect_with_config( host: &str, port: u16, username: &str, password: &str, config: SessionConfig, ) -> Result<Self>
Connect with custom configuration.
Sourcepub async fn from_stream<S: AsyncRead + AsyncWrite + Unpin + Send + 'static>(
stream: S,
) -> Result<Self>
pub async fn from_stream<S: AsyncRead + AsyncWrite + Unpin + Send + 'static>( stream: S, ) -> Result<Self>
Create a session from an existing stream (useful for testing).
Sourcepub async fn from_stream_with_config<S: AsyncRead + AsyncWrite + Unpin + Send + 'static>(
stream: S,
config: SessionConfig,
) -> Result<Self>
pub async fn from_stream_with_config<S: AsyncRead + AsyncWrite + Unpin + Send + 'static>( stream: S, config: SessionConfig, ) -> Result<Self>
Create a session from an existing stream with custom configuration.
Sourcepub fn session_id(&self) -> u32
pub fn session_id(&self) -> u32
Examples found in repository?
4async fn main() -> netconf_rust::Result<()> {
5 // --- Connect ---
6 let mut session = Session::connect("localhost", 830, "netconf", "netconf").await?;
7
8 println!("Session ID: {}", session.session_id());
9 println!("Framing: {:?}", session.framing_mode());
10 println!("Capabilities: {}", session.server_capabilities().join(", "));
11
12 // --- Get full running config ---
13 let config = session.get_config(Datastore::Running, None).await?;
14 println!("\n=== Running config ===\n{config}");
15
16 // --- Filtered get-config with subtree filter ---
17 let interfaces = session
18 .get_config(
19 Datastore::Running,
20 Some(r#"<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"/>"#),
21 )
22 .await?;
23 println!("\n=== Interfaces ===\n{interfaces}");
24
25 // --- Zero-copy DataPayload ---
26 let payload = session.get_config_payload(Datastore::Running, None).await?;
27 println!("\n=== Config payload size: {} bytes ===", payload.len());
28
29 // Stream XML events directly from the payload's bytes
30 let mut reader = payload.reader();
31 let mut element_count = 0;
32 loop {
33 match reader.read_event() {
34 Ok(quick_xml::events::Event::Start(_)) => element_count += 1,
35 Ok(quick_xml::events::Event::Eof) => break,
36 _ => {}
37 }
38 }
39 println!("Total XML elements: {element_count}");
40
41 // --- Edit config with candidate commit ---
42 // Note: NACM may deny access to certain data models depending on the user's
43 // permissions. We use the netconf-server model which the netconf user can write.
44 session.lock(Datastore::Candidate).await?;
45 println!("\n=== Locked candidate ===");
46
47 match session
48 .edit_config(
49 Datastore::Candidate,
50 r#"
51 <netconf-server xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-server">
52 <listen>
53 <endpoint>
54 <name>default-ssh</name>
55 <ssh>
56 <tcp-server-parameters>
57 <local-address>0.0.0.0</local-address>
58 </tcp-server-parameters>
59 </ssh>
60 </endpoint>
61 </listen>
62 </netconf-server>
63 "#,
64 )
65 .await
66 {
67 Ok(()) => {
68 println!("Edited candidate config");
69 session.commit().await?;
70 println!("Committed");
71 }
72 Err(e) => {
73 eprintln!("edit-config failed (NACM?): {e}");
74 }
75 }
76
77 session.unlock(Datastore::Candidate).await?;
78 println!("Unlocked candidate");
79
80 // --- Pipelining: send 3 RPCs before awaiting any replies ---
81 println!("\n=== Pipelining ===");
82 let fut1 = session
83 .rpc_send("<get-config><source><running/></source></get-config>")
84 .await?;
85 let fut2 = session
86 .rpc_send("<get-config><source><running/></source></get-config>")
87 .await?;
88 let fut3 = session
89 .rpc_send("<get-config><source><running/></source></get-config>")
90 .await?;
91
92 println!(
93 "Sent 3 RPCs (message-ids: {}, {}, {})",
94 fut1.message_id(),
95 fut2.message_id(),
96 fut3.message_id()
97 );
98
99 let reply1 = fut1.response().await?;
100 let reply2 = fut2.response().await?;
101 let reply3 = fut3.response().await?;
102 println!(
103 "Got 3 replies (message-ids: {}, {}, {})",
104 reply1.message_id, reply2.message_id, reply3.message_id
105 );
106
107 // --- Raw RPC for operations not covered by convenience methods ---
108 println!("\n=== Raw RPC ===");
109 let reply = session
110 .rpc_raw("<get-schema><identifier>ietf-interfaces</identifier></get-schema>")
111 .await?;
112 match reply.body {
113 netconf_rust::RpcReplyBody::Data(payload) => {
114 println!("Got schema ({} bytes)", payload.len())
115 }
116 netconf_rust::RpcReplyBody::Ok => println!("ok"),
117 netconf_rust::RpcReplyBody::Error(errors) => {
118 for e in &errors {
119 eprintln!("Error: {}: {}", e.error_tag, e.error_message);
120 }
121 }
122 }
123
124 // --- Graceful close ---
125 session.close_session().await?;
126 println!("\nSession closed (state: {})", session.state());
127
128 Ok(())
129}Sourcepub fn server_capabilities(&self) -> &[String]
pub fn server_capabilities(&self) -> &[String]
Examples found in repository?
4async fn main() -> netconf_rust::Result<()> {
5 // --- Connect ---
6 let mut session = Session::connect("localhost", 830, "netconf", "netconf").await?;
7
8 println!("Session ID: {}", session.session_id());
9 println!("Framing: {:?}", session.framing_mode());
10 println!("Capabilities: {}", session.server_capabilities().join(", "));
11
12 // --- Get full running config ---
13 let config = session.get_config(Datastore::Running, None).await?;
14 println!("\n=== Running config ===\n{config}");
15
16 // --- Filtered get-config with subtree filter ---
17 let interfaces = session
18 .get_config(
19 Datastore::Running,
20 Some(r#"<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"/>"#),
21 )
22 .await?;
23 println!("\n=== Interfaces ===\n{interfaces}");
24
25 // --- Zero-copy DataPayload ---
26 let payload = session.get_config_payload(Datastore::Running, None).await?;
27 println!("\n=== Config payload size: {} bytes ===", payload.len());
28
29 // Stream XML events directly from the payload's bytes
30 let mut reader = payload.reader();
31 let mut element_count = 0;
32 loop {
33 match reader.read_event() {
34 Ok(quick_xml::events::Event::Start(_)) => element_count += 1,
35 Ok(quick_xml::events::Event::Eof) => break,
36 _ => {}
37 }
38 }
39 println!("Total XML elements: {element_count}");
40
41 // --- Edit config with candidate commit ---
42 // Note: NACM may deny access to certain data models depending on the user's
43 // permissions. We use the netconf-server model which the netconf user can write.
44 session.lock(Datastore::Candidate).await?;
45 println!("\n=== Locked candidate ===");
46
47 match session
48 .edit_config(
49 Datastore::Candidate,
50 r#"
51 <netconf-server xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-server">
52 <listen>
53 <endpoint>
54 <name>default-ssh</name>
55 <ssh>
56 <tcp-server-parameters>
57 <local-address>0.0.0.0</local-address>
58 </tcp-server-parameters>
59 </ssh>
60 </endpoint>
61 </listen>
62 </netconf-server>
63 "#,
64 )
65 .await
66 {
67 Ok(()) => {
68 println!("Edited candidate config");
69 session.commit().await?;
70 println!("Committed");
71 }
72 Err(e) => {
73 eprintln!("edit-config failed (NACM?): {e}");
74 }
75 }
76
77 session.unlock(Datastore::Candidate).await?;
78 println!("Unlocked candidate");
79
80 // --- Pipelining: send 3 RPCs before awaiting any replies ---
81 println!("\n=== Pipelining ===");
82 let fut1 = session
83 .rpc_send("<get-config><source><running/></source></get-config>")
84 .await?;
85 let fut2 = session
86 .rpc_send("<get-config><source><running/></source></get-config>")
87 .await?;
88 let fut3 = session
89 .rpc_send("<get-config><source><running/></source></get-config>")
90 .await?;
91
92 println!(
93 "Sent 3 RPCs (message-ids: {}, {}, {})",
94 fut1.message_id(),
95 fut2.message_id(),
96 fut3.message_id()
97 );
98
99 let reply1 = fut1.response().await?;
100 let reply2 = fut2.response().await?;
101 let reply3 = fut3.response().await?;
102 println!(
103 "Got 3 replies (message-ids: {}, {}, {})",
104 reply1.message_id, reply2.message_id, reply3.message_id
105 );
106
107 // --- Raw RPC for operations not covered by convenience methods ---
108 println!("\n=== Raw RPC ===");
109 let reply = session
110 .rpc_raw("<get-schema><identifier>ietf-interfaces</identifier></get-schema>")
111 .await?;
112 match reply.body {
113 netconf_rust::RpcReplyBody::Data(payload) => {
114 println!("Got schema ({} bytes)", payload.len())
115 }
116 netconf_rust::RpcReplyBody::Ok => println!("ok"),
117 netconf_rust::RpcReplyBody::Error(errors) => {
118 for e in &errors {
119 eprintln!("Error: {}: {}", e.error_tag, e.error_message);
120 }
121 }
122 }
123
124 // --- Graceful close ---
125 session.close_session().await?;
126 println!("\nSession closed (state: {})", session.state());
127
128 Ok(())
129}Sourcepub fn framing_mode(&self) -> FramingMode
pub fn framing_mode(&self) -> FramingMode
Examples found in repository?
4async fn main() -> netconf_rust::Result<()> {
5 // --- Connect ---
6 let mut session = Session::connect("localhost", 830, "netconf", "netconf").await?;
7
8 println!("Session ID: {}", session.session_id());
9 println!("Framing: {:?}", session.framing_mode());
10 println!("Capabilities: {}", session.server_capabilities().join(", "));
11
12 // --- Get full running config ---
13 let config = session.get_config(Datastore::Running, None).await?;
14 println!("\n=== Running config ===\n{config}");
15
16 // --- Filtered get-config with subtree filter ---
17 let interfaces = session
18 .get_config(
19 Datastore::Running,
20 Some(r#"<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"/>"#),
21 )
22 .await?;
23 println!("\n=== Interfaces ===\n{interfaces}");
24
25 // --- Zero-copy DataPayload ---
26 let payload = session.get_config_payload(Datastore::Running, None).await?;
27 println!("\n=== Config payload size: {} bytes ===", payload.len());
28
29 // Stream XML events directly from the payload's bytes
30 let mut reader = payload.reader();
31 let mut element_count = 0;
32 loop {
33 match reader.read_event() {
34 Ok(quick_xml::events::Event::Start(_)) => element_count += 1,
35 Ok(quick_xml::events::Event::Eof) => break,
36 _ => {}
37 }
38 }
39 println!("Total XML elements: {element_count}");
40
41 // --- Edit config with candidate commit ---
42 // Note: NACM may deny access to certain data models depending on the user's
43 // permissions. We use the netconf-server model which the netconf user can write.
44 session.lock(Datastore::Candidate).await?;
45 println!("\n=== Locked candidate ===");
46
47 match session
48 .edit_config(
49 Datastore::Candidate,
50 r#"
51 <netconf-server xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-server">
52 <listen>
53 <endpoint>
54 <name>default-ssh</name>
55 <ssh>
56 <tcp-server-parameters>
57 <local-address>0.0.0.0</local-address>
58 </tcp-server-parameters>
59 </ssh>
60 </endpoint>
61 </listen>
62 </netconf-server>
63 "#,
64 )
65 .await
66 {
67 Ok(()) => {
68 println!("Edited candidate config");
69 session.commit().await?;
70 println!("Committed");
71 }
72 Err(e) => {
73 eprintln!("edit-config failed (NACM?): {e}");
74 }
75 }
76
77 session.unlock(Datastore::Candidate).await?;
78 println!("Unlocked candidate");
79
80 // --- Pipelining: send 3 RPCs before awaiting any replies ---
81 println!("\n=== Pipelining ===");
82 let fut1 = session
83 .rpc_send("<get-config><source><running/></source></get-config>")
84 .await?;
85 let fut2 = session
86 .rpc_send("<get-config><source><running/></source></get-config>")
87 .await?;
88 let fut3 = session
89 .rpc_send("<get-config><source><running/></source></get-config>")
90 .await?;
91
92 println!(
93 "Sent 3 RPCs (message-ids: {}, {}, {})",
94 fut1.message_id(),
95 fut2.message_id(),
96 fut3.message_id()
97 );
98
99 let reply1 = fut1.response().await?;
100 let reply2 = fut2.response().await?;
101 let reply3 = fut3.response().await?;
102 println!(
103 "Got 3 replies (message-ids: {}, {}, {})",
104 reply1.message_id, reply2.message_id, reply3.message_id
105 );
106
107 // --- Raw RPC for operations not covered by convenience methods ---
108 println!("\n=== Raw RPC ===");
109 let reply = session
110 .rpc_raw("<get-schema><identifier>ietf-interfaces</identifier></get-schema>")
111 .await?;
112 match reply.body {
113 netconf_rust::RpcReplyBody::Data(payload) => {
114 println!("Got schema ({} bytes)", payload.len())
115 }
116 netconf_rust::RpcReplyBody::Ok => println!("ok"),
117 netconf_rust::RpcReplyBody::Error(errors) => {
118 for e in &errors {
119 eprintln!("Error: {}: {}", e.error_tag, e.error_message);
120 }
121 }
122 }
123
124 // --- Graceful close ---
125 session.close_session().await?;
126 println!("\nSession closed (state: {})", session.state());
127
128 Ok(())
129}Sourcepub fn state(&self) -> SessionState
pub fn state(&self) -> SessionState
Examples found in repository?
4async fn main() -> netconf_rust::Result<()> {
5 // --- Connect ---
6 let mut session = Session::connect("localhost", 830, "netconf", "netconf").await?;
7
8 println!("Session ID: {}", session.session_id());
9 println!("Framing: {:?}", session.framing_mode());
10 println!("Capabilities: {}", session.server_capabilities().join(", "));
11
12 // --- Get full running config ---
13 let config = session.get_config(Datastore::Running, None).await?;
14 println!("\n=== Running config ===\n{config}");
15
16 // --- Filtered get-config with subtree filter ---
17 let interfaces = session
18 .get_config(
19 Datastore::Running,
20 Some(r#"<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"/>"#),
21 )
22 .await?;
23 println!("\n=== Interfaces ===\n{interfaces}");
24
25 // --- Zero-copy DataPayload ---
26 let payload = session.get_config_payload(Datastore::Running, None).await?;
27 println!("\n=== Config payload size: {} bytes ===", payload.len());
28
29 // Stream XML events directly from the payload's bytes
30 let mut reader = payload.reader();
31 let mut element_count = 0;
32 loop {
33 match reader.read_event() {
34 Ok(quick_xml::events::Event::Start(_)) => element_count += 1,
35 Ok(quick_xml::events::Event::Eof) => break,
36 _ => {}
37 }
38 }
39 println!("Total XML elements: {element_count}");
40
41 // --- Edit config with candidate commit ---
42 // Note: NACM may deny access to certain data models depending on the user's
43 // permissions. We use the netconf-server model which the netconf user can write.
44 session.lock(Datastore::Candidate).await?;
45 println!("\n=== Locked candidate ===");
46
47 match session
48 .edit_config(
49 Datastore::Candidate,
50 r#"
51 <netconf-server xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-server">
52 <listen>
53 <endpoint>
54 <name>default-ssh</name>
55 <ssh>
56 <tcp-server-parameters>
57 <local-address>0.0.0.0</local-address>
58 </tcp-server-parameters>
59 </ssh>
60 </endpoint>
61 </listen>
62 </netconf-server>
63 "#,
64 )
65 .await
66 {
67 Ok(()) => {
68 println!("Edited candidate config");
69 session.commit().await?;
70 println!("Committed");
71 }
72 Err(e) => {
73 eprintln!("edit-config failed (NACM?): {e}");
74 }
75 }
76
77 session.unlock(Datastore::Candidate).await?;
78 println!("Unlocked candidate");
79
80 // --- Pipelining: send 3 RPCs before awaiting any replies ---
81 println!("\n=== Pipelining ===");
82 let fut1 = session
83 .rpc_send("<get-config><source><running/></source></get-config>")
84 .await?;
85 let fut2 = session
86 .rpc_send("<get-config><source><running/></source></get-config>")
87 .await?;
88 let fut3 = session
89 .rpc_send("<get-config><source><running/></source></get-config>")
90 .await?;
91
92 println!(
93 "Sent 3 RPCs (message-ids: {}, {}, {})",
94 fut1.message_id(),
95 fut2.message_id(),
96 fut3.message_id()
97 );
98
99 let reply1 = fut1.response().await?;
100 let reply2 = fut2.response().await?;
101 let reply3 = fut3.response().await?;
102 println!(
103 "Got 3 replies (message-ids: {}, {}, {})",
104 reply1.message_id, reply2.message_id, reply3.message_id
105 );
106
107 // --- Raw RPC for operations not covered by convenience methods ---
108 println!("\n=== Raw RPC ===");
109 let reply = session
110 .rpc_raw("<get-schema><identifier>ietf-interfaces</identifier></get-schema>")
111 .await?;
112 match reply.body {
113 netconf_rust::RpcReplyBody::Data(payload) => {
114 println!("Got schema ({} bytes)", payload.len())
115 }
116 netconf_rust::RpcReplyBody::Ok => println!("ok"),
117 netconf_rust::RpcReplyBody::Error(errors) => {
118 for e in &errors {
119 eprintln!("Error: {}: {}", e.error_tag, e.error_message);
120 }
121 }
122 }
123
124 // --- Graceful close ---
125 session.close_session().await?;
126 println!("\nSession closed (state: {})", session.state());
127
128 Ok(())
129}Sourcepub async fn rpc_send(&mut self, inner_xml: &str) -> Result<RpcFuture>
pub async fn rpc_send(&mut self, inner_xml: &str) -> Result<RpcFuture>
Send a raw RPC and return a future for the reply (pipelining).
This writes the RPC to the server immediately but does not wait
for the reply. Call RpcFuture::response() to await the reply.
Multiple RPCs can be pipelined by calling this repeatedly before
awaiting any of them.
Examples found in repository?
4async fn main() -> netconf_rust::Result<()> {
5 // --- Connect ---
6 let mut session = Session::connect("localhost", 830, "netconf", "netconf").await?;
7
8 println!("Session ID: {}", session.session_id());
9 println!("Framing: {:?}", session.framing_mode());
10 println!("Capabilities: {}", session.server_capabilities().join(", "));
11
12 // --- Get full running config ---
13 let config = session.get_config(Datastore::Running, None).await?;
14 println!("\n=== Running config ===\n{config}");
15
16 // --- Filtered get-config with subtree filter ---
17 let interfaces = session
18 .get_config(
19 Datastore::Running,
20 Some(r#"<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"/>"#),
21 )
22 .await?;
23 println!("\n=== Interfaces ===\n{interfaces}");
24
25 // --- Zero-copy DataPayload ---
26 let payload = session.get_config_payload(Datastore::Running, None).await?;
27 println!("\n=== Config payload size: {} bytes ===", payload.len());
28
29 // Stream XML events directly from the payload's bytes
30 let mut reader = payload.reader();
31 let mut element_count = 0;
32 loop {
33 match reader.read_event() {
34 Ok(quick_xml::events::Event::Start(_)) => element_count += 1,
35 Ok(quick_xml::events::Event::Eof) => break,
36 _ => {}
37 }
38 }
39 println!("Total XML elements: {element_count}");
40
41 // --- Edit config with candidate commit ---
42 // Note: NACM may deny access to certain data models depending on the user's
43 // permissions. We use the netconf-server model which the netconf user can write.
44 session.lock(Datastore::Candidate).await?;
45 println!("\n=== Locked candidate ===");
46
47 match session
48 .edit_config(
49 Datastore::Candidate,
50 r#"
51 <netconf-server xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-server">
52 <listen>
53 <endpoint>
54 <name>default-ssh</name>
55 <ssh>
56 <tcp-server-parameters>
57 <local-address>0.0.0.0</local-address>
58 </tcp-server-parameters>
59 </ssh>
60 </endpoint>
61 </listen>
62 </netconf-server>
63 "#,
64 )
65 .await
66 {
67 Ok(()) => {
68 println!("Edited candidate config");
69 session.commit().await?;
70 println!("Committed");
71 }
72 Err(e) => {
73 eprintln!("edit-config failed (NACM?): {e}");
74 }
75 }
76
77 session.unlock(Datastore::Candidate).await?;
78 println!("Unlocked candidate");
79
80 // --- Pipelining: send 3 RPCs before awaiting any replies ---
81 println!("\n=== Pipelining ===");
82 let fut1 = session
83 .rpc_send("<get-config><source><running/></source></get-config>")
84 .await?;
85 let fut2 = session
86 .rpc_send("<get-config><source><running/></source></get-config>")
87 .await?;
88 let fut3 = session
89 .rpc_send("<get-config><source><running/></source></get-config>")
90 .await?;
91
92 println!(
93 "Sent 3 RPCs (message-ids: {}, {}, {})",
94 fut1.message_id(),
95 fut2.message_id(),
96 fut3.message_id()
97 );
98
99 let reply1 = fut1.response().await?;
100 let reply2 = fut2.response().await?;
101 let reply3 = fut3.response().await?;
102 println!(
103 "Got 3 replies (message-ids: {}, {}, {})",
104 reply1.message_id, reply2.message_id, reply3.message_id
105 );
106
107 // --- Raw RPC for operations not covered by convenience methods ---
108 println!("\n=== Raw RPC ===");
109 let reply = session
110 .rpc_raw("<get-schema><identifier>ietf-interfaces</identifier></get-schema>")
111 .await?;
112 match reply.body {
113 netconf_rust::RpcReplyBody::Data(payload) => {
114 println!("Got schema ({} bytes)", payload.len())
115 }
116 netconf_rust::RpcReplyBody::Ok => println!("ok"),
117 netconf_rust::RpcReplyBody::Error(errors) => {
118 for e in &errors {
119 eprintln!("Error: {}: {}", e.error_tag, e.error_message);
120 }
121 }
122 }
123
124 // --- Graceful close ---
125 session.close_session().await?;
126 println!("\nSession closed (state: {})", session.state());
127
128 Ok(())
129}Sourcepub async fn rpc_raw(&mut self, inner_xml: &str) -> Result<RpcReply>
pub async fn rpc_raw(&mut self, inner_xml: &str) -> Result<RpcReply>
Send a raw RPC and wait for the reply.
Examples found in repository?
4async fn main() -> netconf_rust::Result<()> {
5 // --- Connect ---
6 let mut session = Session::connect("localhost", 830, "netconf", "netconf").await?;
7
8 println!("Session ID: {}", session.session_id());
9 println!("Framing: {:?}", session.framing_mode());
10 println!("Capabilities: {}", session.server_capabilities().join(", "));
11
12 // --- Get full running config ---
13 let config = session.get_config(Datastore::Running, None).await?;
14 println!("\n=== Running config ===\n{config}");
15
16 // --- Filtered get-config with subtree filter ---
17 let interfaces = session
18 .get_config(
19 Datastore::Running,
20 Some(r#"<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"/>"#),
21 )
22 .await?;
23 println!("\n=== Interfaces ===\n{interfaces}");
24
25 // --- Zero-copy DataPayload ---
26 let payload = session.get_config_payload(Datastore::Running, None).await?;
27 println!("\n=== Config payload size: {} bytes ===", payload.len());
28
29 // Stream XML events directly from the payload's bytes
30 let mut reader = payload.reader();
31 let mut element_count = 0;
32 loop {
33 match reader.read_event() {
34 Ok(quick_xml::events::Event::Start(_)) => element_count += 1,
35 Ok(quick_xml::events::Event::Eof) => break,
36 _ => {}
37 }
38 }
39 println!("Total XML elements: {element_count}");
40
41 // --- Edit config with candidate commit ---
42 // Note: NACM may deny access to certain data models depending on the user's
43 // permissions. We use the netconf-server model which the netconf user can write.
44 session.lock(Datastore::Candidate).await?;
45 println!("\n=== Locked candidate ===");
46
47 match session
48 .edit_config(
49 Datastore::Candidate,
50 r#"
51 <netconf-server xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-server">
52 <listen>
53 <endpoint>
54 <name>default-ssh</name>
55 <ssh>
56 <tcp-server-parameters>
57 <local-address>0.0.0.0</local-address>
58 </tcp-server-parameters>
59 </ssh>
60 </endpoint>
61 </listen>
62 </netconf-server>
63 "#,
64 )
65 .await
66 {
67 Ok(()) => {
68 println!("Edited candidate config");
69 session.commit().await?;
70 println!("Committed");
71 }
72 Err(e) => {
73 eprintln!("edit-config failed (NACM?): {e}");
74 }
75 }
76
77 session.unlock(Datastore::Candidate).await?;
78 println!("Unlocked candidate");
79
80 // --- Pipelining: send 3 RPCs before awaiting any replies ---
81 println!("\n=== Pipelining ===");
82 let fut1 = session
83 .rpc_send("<get-config><source><running/></source></get-config>")
84 .await?;
85 let fut2 = session
86 .rpc_send("<get-config><source><running/></source></get-config>")
87 .await?;
88 let fut3 = session
89 .rpc_send("<get-config><source><running/></source></get-config>")
90 .await?;
91
92 println!(
93 "Sent 3 RPCs (message-ids: {}, {}, {})",
94 fut1.message_id(),
95 fut2.message_id(),
96 fut3.message_id()
97 );
98
99 let reply1 = fut1.response().await?;
100 let reply2 = fut2.response().await?;
101 let reply3 = fut3.response().await?;
102 println!(
103 "Got 3 replies (message-ids: {}, {}, {})",
104 reply1.message_id, reply2.message_id, reply3.message_id
105 );
106
107 // --- Raw RPC for operations not covered by convenience methods ---
108 println!("\n=== Raw RPC ===");
109 let reply = session
110 .rpc_raw("<get-schema><identifier>ietf-interfaces</identifier></get-schema>")
111 .await?;
112 match reply.body {
113 netconf_rust::RpcReplyBody::Data(payload) => {
114 println!("Got schema ({} bytes)", payload.len())
115 }
116 netconf_rust::RpcReplyBody::Ok => println!("ok"),
117 netconf_rust::RpcReplyBody::Error(errors) => {
118 for e in &errors {
119 eprintln!("Error: {}: {}", e.error_tag, e.error_message);
120 }
121 }
122 }
123
124 // --- Graceful close ---
125 session.close_session().await?;
126 println!("\nSession closed (state: {})", session.state());
127
128 Ok(())
129}Sourcepub async fn get_config(
&mut self,
source: Datastore,
filter: Option<&str>,
) -> Result<String>
pub async fn get_config( &mut self, source: Datastore, filter: Option<&str>, ) -> Result<String>
Retrieve configuration from a datastore.
Examples found in repository?
4async fn main() -> netconf_rust::Result<()> {
5 // --- Connect ---
6 let mut session = Session::connect("localhost", 830, "netconf", "netconf").await?;
7
8 println!("Session ID: {}", session.session_id());
9 println!("Framing: {:?}", session.framing_mode());
10 println!("Capabilities: {}", session.server_capabilities().join(", "));
11
12 // --- Get full running config ---
13 let config = session.get_config(Datastore::Running, None).await?;
14 println!("\n=== Running config ===\n{config}");
15
16 // --- Filtered get-config with subtree filter ---
17 let interfaces = session
18 .get_config(
19 Datastore::Running,
20 Some(r#"<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"/>"#),
21 )
22 .await?;
23 println!("\n=== Interfaces ===\n{interfaces}");
24
25 // --- Zero-copy DataPayload ---
26 let payload = session.get_config_payload(Datastore::Running, None).await?;
27 println!("\n=== Config payload size: {} bytes ===", payload.len());
28
29 // Stream XML events directly from the payload's bytes
30 let mut reader = payload.reader();
31 let mut element_count = 0;
32 loop {
33 match reader.read_event() {
34 Ok(quick_xml::events::Event::Start(_)) => element_count += 1,
35 Ok(quick_xml::events::Event::Eof) => break,
36 _ => {}
37 }
38 }
39 println!("Total XML elements: {element_count}");
40
41 // --- Edit config with candidate commit ---
42 // Note: NACM may deny access to certain data models depending on the user's
43 // permissions. We use the netconf-server model which the netconf user can write.
44 session.lock(Datastore::Candidate).await?;
45 println!("\n=== Locked candidate ===");
46
47 match session
48 .edit_config(
49 Datastore::Candidate,
50 r#"
51 <netconf-server xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-server">
52 <listen>
53 <endpoint>
54 <name>default-ssh</name>
55 <ssh>
56 <tcp-server-parameters>
57 <local-address>0.0.0.0</local-address>
58 </tcp-server-parameters>
59 </ssh>
60 </endpoint>
61 </listen>
62 </netconf-server>
63 "#,
64 )
65 .await
66 {
67 Ok(()) => {
68 println!("Edited candidate config");
69 session.commit().await?;
70 println!("Committed");
71 }
72 Err(e) => {
73 eprintln!("edit-config failed (NACM?): {e}");
74 }
75 }
76
77 session.unlock(Datastore::Candidate).await?;
78 println!("Unlocked candidate");
79
80 // --- Pipelining: send 3 RPCs before awaiting any replies ---
81 println!("\n=== Pipelining ===");
82 let fut1 = session
83 .rpc_send("<get-config><source><running/></source></get-config>")
84 .await?;
85 let fut2 = session
86 .rpc_send("<get-config><source><running/></source></get-config>")
87 .await?;
88 let fut3 = session
89 .rpc_send("<get-config><source><running/></source></get-config>")
90 .await?;
91
92 println!(
93 "Sent 3 RPCs (message-ids: {}, {}, {})",
94 fut1.message_id(),
95 fut2.message_id(),
96 fut3.message_id()
97 );
98
99 let reply1 = fut1.response().await?;
100 let reply2 = fut2.response().await?;
101 let reply3 = fut3.response().await?;
102 println!(
103 "Got 3 replies (message-ids: {}, {}, {})",
104 reply1.message_id, reply2.message_id, reply3.message_id
105 );
106
107 // --- Raw RPC for operations not covered by convenience methods ---
108 println!("\n=== Raw RPC ===");
109 let reply = session
110 .rpc_raw("<get-schema><identifier>ietf-interfaces</identifier></get-schema>")
111 .await?;
112 match reply.body {
113 netconf_rust::RpcReplyBody::Data(payload) => {
114 println!("Got schema ({} bytes)", payload.len())
115 }
116 netconf_rust::RpcReplyBody::Ok => println!("ok"),
117 netconf_rust::RpcReplyBody::Error(errors) => {
118 for e in &errors {
119 eprintln!("Error: {}: {}", e.error_tag, e.error_message);
120 }
121 }
122 }
123
124 // --- Graceful close ---
125 session.close_session().await?;
126 println!("\nSession closed (state: {})", session.state());
127
128 Ok(())
129}Sourcepub async fn get_config_payload(
&mut self,
source: Datastore,
filter: Option<&str>,
) -> Result<DataPayload>
pub async fn get_config_payload( &mut self, source: Datastore, filter: Option<&str>, ) -> Result<DataPayload>
Retrieve configuration as a zero-copy DataPayload.
Same as get_config() but returns a DataPayload instead of String,
avoiding a copy of the response body. Use payload.as_str() for a
zero-copy &str view, or payload.reader() for streaming XML events.
Examples found in repository?
4async fn main() -> netconf_rust::Result<()> {
5 // --- Connect ---
6 let mut session = Session::connect("localhost", 830, "netconf", "netconf").await?;
7
8 println!("Session ID: {}", session.session_id());
9 println!("Framing: {:?}", session.framing_mode());
10 println!("Capabilities: {}", session.server_capabilities().join(", "));
11
12 // --- Get full running config ---
13 let config = session.get_config(Datastore::Running, None).await?;
14 println!("\n=== Running config ===\n{config}");
15
16 // --- Filtered get-config with subtree filter ---
17 let interfaces = session
18 .get_config(
19 Datastore::Running,
20 Some(r#"<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"/>"#),
21 )
22 .await?;
23 println!("\n=== Interfaces ===\n{interfaces}");
24
25 // --- Zero-copy DataPayload ---
26 let payload = session.get_config_payload(Datastore::Running, None).await?;
27 println!("\n=== Config payload size: {} bytes ===", payload.len());
28
29 // Stream XML events directly from the payload's bytes
30 let mut reader = payload.reader();
31 let mut element_count = 0;
32 loop {
33 match reader.read_event() {
34 Ok(quick_xml::events::Event::Start(_)) => element_count += 1,
35 Ok(quick_xml::events::Event::Eof) => break,
36 _ => {}
37 }
38 }
39 println!("Total XML elements: {element_count}");
40
41 // --- Edit config with candidate commit ---
42 // Note: NACM may deny access to certain data models depending on the user's
43 // permissions. We use the netconf-server model which the netconf user can write.
44 session.lock(Datastore::Candidate).await?;
45 println!("\n=== Locked candidate ===");
46
47 match session
48 .edit_config(
49 Datastore::Candidate,
50 r#"
51 <netconf-server xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-server">
52 <listen>
53 <endpoint>
54 <name>default-ssh</name>
55 <ssh>
56 <tcp-server-parameters>
57 <local-address>0.0.0.0</local-address>
58 </tcp-server-parameters>
59 </ssh>
60 </endpoint>
61 </listen>
62 </netconf-server>
63 "#,
64 )
65 .await
66 {
67 Ok(()) => {
68 println!("Edited candidate config");
69 session.commit().await?;
70 println!("Committed");
71 }
72 Err(e) => {
73 eprintln!("edit-config failed (NACM?): {e}");
74 }
75 }
76
77 session.unlock(Datastore::Candidate).await?;
78 println!("Unlocked candidate");
79
80 // --- Pipelining: send 3 RPCs before awaiting any replies ---
81 println!("\n=== Pipelining ===");
82 let fut1 = session
83 .rpc_send("<get-config><source><running/></source></get-config>")
84 .await?;
85 let fut2 = session
86 .rpc_send("<get-config><source><running/></source></get-config>")
87 .await?;
88 let fut3 = session
89 .rpc_send("<get-config><source><running/></source></get-config>")
90 .await?;
91
92 println!(
93 "Sent 3 RPCs (message-ids: {}, {}, {})",
94 fut1.message_id(),
95 fut2.message_id(),
96 fut3.message_id()
97 );
98
99 let reply1 = fut1.response().await?;
100 let reply2 = fut2.response().await?;
101 let reply3 = fut3.response().await?;
102 println!(
103 "Got 3 replies (message-ids: {}, {}, {})",
104 reply1.message_id, reply2.message_id, reply3.message_id
105 );
106
107 // --- Raw RPC for operations not covered by convenience methods ---
108 println!("\n=== Raw RPC ===");
109 let reply = session
110 .rpc_raw("<get-schema><identifier>ietf-interfaces</identifier></get-schema>")
111 .await?;
112 match reply.body {
113 netconf_rust::RpcReplyBody::Data(payload) => {
114 println!("Got schema ({} bytes)", payload.len())
115 }
116 netconf_rust::RpcReplyBody::Ok => println!("ok"),
117 netconf_rust::RpcReplyBody::Error(errors) => {
118 for e in &errors {
119 eprintln!("Error: {}: {}", e.error_tag, e.error_message);
120 }
121 }
122 }
123
124 // --- Graceful close ---
125 session.close_session().await?;
126 println!("\nSession closed (state: {})", session.state());
127
128 Ok(())
129}Sourcepub async fn get(&mut self, filter: Option<&str>) -> Result<String>
pub async fn get(&mut self, filter: Option<&str>) -> Result<String>
Retrieve running configuration and state data.
Sourcepub async fn get_payload(&mut self, filter: Option<&str>) -> Result<DataPayload>
pub async fn get_payload(&mut self, filter: Option<&str>) -> Result<DataPayload>
Retrieve running configuration and state data as a zero-copy DataPayload.
Same as get() but returns a DataPayload instead of String,
avoiding a copy of the response body.
Sourcepub async fn edit_config(
&mut self,
target: Datastore,
config: &str,
) -> Result<()>
pub async fn edit_config( &mut self, target: Datastore, config: &str, ) -> Result<()>
Edit the configuration of a target datastore.
Examples found in repository?
4async fn main() -> netconf_rust::Result<()> {
5 // --- Connect ---
6 let mut session = Session::connect("localhost", 830, "netconf", "netconf").await?;
7
8 println!("Session ID: {}", session.session_id());
9 println!("Framing: {:?}", session.framing_mode());
10 println!("Capabilities: {}", session.server_capabilities().join(", "));
11
12 // --- Get full running config ---
13 let config = session.get_config(Datastore::Running, None).await?;
14 println!("\n=== Running config ===\n{config}");
15
16 // --- Filtered get-config with subtree filter ---
17 let interfaces = session
18 .get_config(
19 Datastore::Running,
20 Some(r#"<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"/>"#),
21 )
22 .await?;
23 println!("\n=== Interfaces ===\n{interfaces}");
24
25 // --- Zero-copy DataPayload ---
26 let payload = session.get_config_payload(Datastore::Running, None).await?;
27 println!("\n=== Config payload size: {} bytes ===", payload.len());
28
29 // Stream XML events directly from the payload's bytes
30 let mut reader = payload.reader();
31 let mut element_count = 0;
32 loop {
33 match reader.read_event() {
34 Ok(quick_xml::events::Event::Start(_)) => element_count += 1,
35 Ok(quick_xml::events::Event::Eof) => break,
36 _ => {}
37 }
38 }
39 println!("Total XML elements: {element_count}");
40
41 // --- Edit config with candidate commit ---
42 // Note: NACM may deny access to certain data models depending on the user's
43 // permissions. We use the netconf-server model which the netconf user can write.
44 session.lock(Datastore::Candidate).await?;
45 println!("\n=== Locked candidate ===");
46
47 match session
48 .edit_config(
49 Datastore::Candidate,
50 r#"
51 <netconf-server xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-server">
52 <listen>
53 <endpoint>
54 <name>default-ssh</name>
55 <ssh>
56 <tcp-server-parameters>
57 <local-address>0.0.0.0</local-address>
58 </tcp-server-parameters>
59 </ssh>
60 </endpoint>
61 </listen>
62 </netconf-server>
63 "#,
64 )
65 .await
66 {
67 Ok(()) => {
68 println!("Edited candidate config");
69 session.commit().await?;
70 println!("Committed");
71 }
72 Err(e) => {
73 eprintln!("edit-config failed (NACM?): {e}");
74 }
75 }
76
77 session.unlock(Datastore::Candidate).await?;
78 println!("Unlocked candidate");
79
80 // --- Pipelining: send 3 RPCs before awaiting any replies ---
81 println!("\n=== Pipelining ===");
82 let fut1 = session
83 .rpc_send("<get-config><source><running/></source></get-config>")
84 .await?;
85 let fut2 = session
86 .rpc_send("<get-config><source><running/></source></get-config>")
87 .await?;
88 let fut3 = session
89 .rpc_send("<get-config><source><running/></source></get-config>")
90 .await?;
91
92 println!(
93 "Sent 3 RPCs (message-ids: {}, {}, {})",
94 fut1.message_id(),
95 fut2.message_id(),
96 fut3.message_id()
97 );
98
99 let reply1 = fut1.response().await?;
100 let reply2 = fut2.response().await?;
101 let reply3 = fut3.response().await?;
102 println!(
103 "Got 3 replies (message-ids: {}, {}, {})",
104 reply1.message_id, reply2.message_id, reply3.message_id
105 );
106
107 // --- Raw RPC for operations not covered by convenience methods ---
108 println!("\n=== Raw RPC ===");
109 let reply = session
110 .rpc_raw("<get-schema><identifier>ietf-interfaces</identifier></get-schema>")
111 .await?;
112 match reply.body {
113 netconf_rust::RpcReplyBody::Data(payload) => {
114 println!("Got schema ({} bytes)", payload.len())
115 }
116 netconf_rust::RpcReplyBody::Ok => println!("ok"),
117 netconf_rust::RpcReplyBody::Error(errors) => {
118 for e in &errors {
119 eprintln!("Error: {}: {}", e.error_tag, e.error_message);
120 }
121 }
122 }
123
124 // --- Graceful close ---
125 session.close_session().await?;
126 println!("\nSession closed (state: {})", session.state());
127
128 Ok(())
129}Sourcepub async fn lock(&mut self, target: Datastore) -> Result<()>
pub async fn lock(&mut self, target: Datastore) -> Result<()>
Lock a datastore
Examples found in repository?
4async fn main() -> netconf_rust::Result<()> {
5 // --- Connect ---
6 let mut session = Session::connect("localhost", 830, "netconf", "netconf").await?;
7
8 println!("Session ID: {}", session.session_id());
9 println!("Framing: {:?}", session.framing_mode());
10 println!("Capabilities: {}", session.server_capabilities().join(", "));
11
12 // --- Get full running config ---
13 let config = session.get_config(Datastore::Running, None).await?;
14 println!("\n=== Running config ===\n{config}");
15
16 // --- Filtered get-config with subtree filter ---
17 let interfaces = session
18 .get_config(
19 Datastore::Running,
20 Some(r#"<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"/>"#),
21 )
22 .await?;
23 println!("\n=== Interfaces ===\n{interfaces}");
24
25 // --- Zero-copy DataPayload ---
26 let payload = session.get_config_payload(Datastore::Running, None).await?;
27 println!("\n=== Config payload size: {} bytes ===", payload.len());
28
29 // Stream XML events directly from the payload's bytes
30 let mut reader = payload.reader();
31 let mut element_count = 0;
32 loop {
33 match reader.read_event() {
34 Ok(quick_xml::events::Event::Start(_)) => element_count += 1,
35 Ok(quick_xml::events::Event::Eof) => break,
36 _ => {}
37 }
38 }
39 println!("Total XML elements: {element_count}");
40
41 // --- Edit config with candidate commit ---
42 // Note: NACM may deny access to certain data models depending on the user's
43 // permissions. We use the netconf-server model which the netconf user can write.
44 session.lock(Datastore::Candidate).await?;
45 println!("\n=== Locked candidate ===");
46
47 match session
48 .edit_config(
49 Datastore::Candidate,
50 r#"
51 <netconf-server xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-server">
52 <listen>
53 <endpoint>
54 <name>default-ssh</name>
55 <ssh>
56 <tcp-server-parameters>
57 <local-address>0.0.0.0</local-address>
58 </tcp-server-parameters>
59 </ssh>
60 </endpoint>
61 </listen>
62 </netconf-server>
63 "#,
64 )
65 .await
66 {
67 Ok(()) => {
68 println!("Edited candidate config");
69 session.commit().await?;
70 println!("Committed");
71 }
72 Err(e) => {
73 eprintln!("edit-config failed (NACM?): {e}");
74 }
75 }
76
77 session.unlock(Datastore::Candidate).await?;
78 println!("Unlocked candidate");
79
80 // --- Pipelining: send 3 RPCs before awaiting any replies ---
81 println!("\n=== Pipelining ===");
82 let fut1 = session
83 .rpc_send("<get-config><source><running/></source></get-config>")
84 .await?;
85 let fut2 = session
86 .rpc_send("<get-config><source><running/></source></get-config>")
87 .await?;
88 let fut3 = session
89 .rpc_send("<get-config><source><running/></source></get-config>")
90 .await?;
91
92 println!(
93 "Sent 3 RPCs (message-ids: {}, {}, {})",
94 fut1.message_id(),
95 fut2.message_id(),
96 fut3.message_id()
97 );
98
99 let reply1 = fut1.response().await?;
100 let reply2 = fut2.response().await?;
101 let reply3 = fut3.response().await?;
102 println!(
103 "Got 3 replies (message-ids: {}, {}, {})",
104 reply1.message_id, reply2.message_id, reply3.message_id
105 );
106
107 // --- Raw RPC for operations not covered by convenience methods ---
108 println!("\n=== Raw RPC ===");
109 let reply = session
110 .rpc_raw("<get-schema><identifier>ietf-interfaces</identifier></get-schema>")
111 .await?;
112 match reply.body {
113 netconf_rust::RpcReplyBody::Data(payload) => {
114 println!("Got schema ({} bytes)", payload.len())
115 }
116 netconf_rust::RpcReplyBody::Ok => println!("ok"),
117 netconf_rust::RpcReplyBody::Error(errors) => {
118 for e in &errors {
119 eprintln!("Error: {}: {}", e.error_tag, e.error_message);
120 }
121 }
122 }
123
124 // --- Graceful close ---
125 session.close_session().await?;
126 println!("\nSession closed (state: {})", session.state());
127
128 Ok(())
129}Sourcepub async fn unlock(&mut self, target: Datastore) -> Result<()>
pub async fn unlock(&mut self, target: Datastore) -> Result<()>
Unlock a datastore.
Examples found in repository?
4async fn main() -> netconf_rust::Result<()> {
5 // --- Connect ---
6 let mut session = Session::connect("localhost", 830, "netconf", "netconf").await?;
7
8 println!("Session ID: {}", session.session_id());
9 println!("Framing: {:?}", session.framing_mode());
10 println!("Capabilities: {}", session.server_capabilities().join(", "));
11
12 // --- Get full running config ---
13 let config = session.get_config(Datastore::Running, None).await?;
14 println!("\n=== Running config ===\n{config}");
15
16 // --- Filtered get-config with subtree filter ---
17 let interfaces = session
18 .get_config(
19 Datastore::Running,
20 Some(r#"<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"/>"#),
21 )
22 .await?;
23 println!("\n=== Interfaces ===\n{interfaces}");
24
25 // --- Zero-copy DataPayload ---
26 let payload = session.get_config_payload(Datastore::Running, None).await?;
27 println!("\n=== Config payload size: {} bytes ===", payload.len());
28
29 // Stream XML events directly from the payload's bytes
30 let mut reader = payload.reader();
31 let mut element_count = 0;
32 loop {
33 match reader.read_event() {
34 Ok(quick_xml::events::Event::Start(_)) => element_count += 1,
35 Ok(quick_xml::events::Event::Eof) => break,
36 _ => {}
37 }
38 }
39 println!("Total XML elements: {element_count}");
40
41 // --- Edit config with candidate commit ---
42 // Note: NACM may deny access to certain data models depending on the user's
43 // permissions. We use the netconf-server model which the netconf user can write.
44 session.lock(Datastore::Candidate).await?;
45 println!("\n=== Locked candidate ===");
46
47 match session
48 .edit_config(
49 Datastore::Candidate,
50 r#"
51 <netconf-server xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-server">
52 <listen>
53 <endpoint>
54 <name>default-ssh</name>
55 <ssh>
56 <tcp-server-parameters>
57 <local-address>0.0.0.0</local-address>
58 </tcp-server-parameters>
59 </ssh>
60 </endpoint>
61 </listen>
62 </netconf-server>
63 "#,
64 )
65 .await
66 {
67 Ok(()) => {
68 println!("Edited candidate config");
69 session.commit().await?;
70 println!("Committed");
71 }
72 Err(e) => {
73 eprintln!("edit-config failed (NACM?): {e}");
74 }
75 }
76
77 session.unlock(Datastore::Candidate).await?;
78 println!("Unlocked candidate");
79
80 // --- Pipelining: send 3 RPCs before awaiting any replies ---
81 println!("\n=== Pipelining ===");
82 let fut1 = session
83 .rpc_send("<get-config><source><running/></source></get-config>")
84 .await?;
85 let fut2 = session
86 .rpc_send("<get-config><source><running/></source></get-config>")
87 .await?;
88 let fut3 = session
89 .rpc_send("<get-config><source><running/></source></get-config>")
90 .await?;
91
92 println!(
93 "Sent 3 RPCs (message-ids: {}, {}, {})",
94 fut1.message_id(),
95 fut2.message_id(),
96 fut3.message_id()
97 );
98
99 let reply1 = fut1.response().await?;
100 let reply2 = fut2.response().await?;
101 let reply3 = fut3.response().await?;
102 println!(
103 "Got 3 replies (message-ids: {}, {}, {})",
104 reply1.message_id, reply2.message_id, reply3.message_id
105 );
106
107 // --- Raw RPC for operations not covered by convenience methods ---
108 println!("\n=== Raw RPC ===");
109 let reply = session
110 .rpc_raw("<get-schema><identifier>ietf-interfaces</identifier></get-schema>")
111 .await?;
112 match reply.body {
113 netconf_rust::RpcReplyBody::Data(payload) => {
114 println!("Got schema ({} bytes)", payload.len())
115 }
116 netconf_rust::RpcReplyBody::Ok => println!("ok"),
117 netconf_rust::RpcReplyBody::Error(errors) => {
118 for e in &errors {
119 eprintln!("Error: {}: {}", e.error_tag, e.error_message);
120 }
121 }
122 }
123
124 // --- Graceful close ---
125 session.close_session().await?;
126 println!("\nSession closed (state: {})", session.state());
127
128 Ok(())
129}Sourcepub async fn commit(&mut self) -> Result<()>
pub async fn commit(&mut self) -> Result<()>
Commit the candidate configuration to running.
Examples found in repository?
4async fn main() -> netconf_rust::Result<()> {
5 // --- Connect ---
6 let mut session = Session::connect("localhost", 830, "netconf", "netconf").await?;
7
8 println!("Session ID: {}", session.session_id());
9 println!("Framing: {:?}", session.framing_mode());
10 println!("Capabilities: {}", session.server_capabilities().join(", "));
11
12 // --- Get full running config ---
13 let config = session.get_config(Datastore::Running, None).await?;
14 println!("\n=== Running config ===\n{config}");
15
16 // --- Filtered get-config with subtree filter ---
17 let interfaces = session
18 .get_config(
19 Datastore::Running,
20 Some(r#"<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"/>"#),
21 )
22 .await?;
23 println!("\n=== Interfaces ===\n{interfaces}");
24
25 // --- Zero-copy DataPayload ---
26 let payload = session.get_config_payload(Datastore::Running, None).await?;
27 println!("\n=== Config payload size: {} bytes ===", payload.len());
28
29 // Stream XML events directly from the payload's bytes
30 let mut reader = payload.reader();
31 let mut element_count = 0;
32 loop {
33 match reader.read_event() {
34 Ok(quick_xml::events::Event::Start(_)) => element_count += 1,
35 Ok(quick_xml::events::Event::Eof) => break,
36 _ => {}
37 }
38 }
39 println!("Total XML elements: {element_count}");
40
41 // --- Edit config with candidate commit ---
42 // Note: NACM may deny access to certain data models depending on the user's
43 // permissions. We use the netconf-server model which the netconf user can write.
44 session.lock(Datastore::Candidate).await?;
45 println!("\n=== Locked candidate ===");
46
47 match session
48 .edit_config(
49 Datastore::Candidate,
50 r#"
51 <netconf-server xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-server">
52 <listen>
53 <endpoint>
54 <name>default-ssh</name>
55 <ssh>
56 <tcp-server-parameters>
57 <local-address>0.0.0.0</local-address>
58 </tcp-server-parameters>
59 </ssh>
60 </endpoint>
61 </listen>
62 </netconf-server>
63 "#,
64 )
65 .await
66 {
67 Ok(()) => {
68 println!("Edited candidate config");
69 session.commit().await?;
70 println!("Committed");
71 }
72 Err(e) => {
73 eprintln!("edit-config failed (NACM?): {e}");
74 }
75 }
76
77 session.unlock(Datastore::Candidate).await?;
78 println!("Unlocked candidate");
79
80 // --- Pipelining: send 3 RPCs before awaiting any replies ---
81 println!("\n=== Pipelining ===");
82 let fut1 = session
83 .rpc_send("<get-config><source><running/></source></get-config>")
84 .await?;
85 let fut2 = session
86 .rpc_send("<get-config><source><running/></source></get-config>")
87 .await?;
88 let fut3 = session
89 .rpc_send("<get-config><source><running/></source></get-config>")
90 .await?;
91
92 println!(
93 "Sent 3 RPCs (message-ids: {}, {}, {})",
94 fut1.message_id(),
95 fut2.message_id(),
96 fut3.message_id()
97 );
98
99 let reply1 = fut1.response().await?;
100 let reply2 = fut2.response().await?;
101 let reply3 = fut3.response().await?;
102 println!(
103 "Got 3 replies (message-ids: {}, {}, {})",
104 reply1.message_id, reply2.message_id, reply3.message_id
105 );
106
107 // --- Raw RPC for operations not covered by convenience methods ---
108 println!("\n=== Raw RPC ===");
109 let reply = session
110 .rpc_raw("<get-schema><identifier>ietf-interfaces</identifier></get-schema>")
111 .await?;
112 match reply.body {
113 netconf_rust::RpcReplyBody::Data(payload) => {
114 println!("Got schema ({} bytes)", payload.len())
115 }
116 netconf_rust::RpcReplyBody::Ok => println!("ok"),
117 netconf_rust::RpcReplyBody::Error(errors) => {
118 for e in &errors {
119 eprintln!("Error: {}: {}", e.error_tag, e.error_message);
120 }
121 }
122 }
123
124 // --- Graceful close ---
125 session.close_session().await?;
126 println!("\nSession closed (state: {})", session.state());
127
128 Ok(())
129}Sourcepub async fn close_session(&mut self) -> Result<()>
pub async fn close_session(&mut self) -> Result<()>
Gracefully close the NETCONF session.
Examples found in repository?
4async fn main() -> netconf_rust::Result<()> {
5 // --- Connect ---
6 let mut session = Session::connect("localhost", 830, "netconf", "netconf").await?;
7
8 println!("Session ID: {}", session.session_id());
9 println!("Framing: {:?}", session.framing_mode());
10 println!("Capabilities: {}", session.server_capabilities().join(", "));
11
12 // --- Get full running config ---
13 let config = session.get_config(Datastore::Running, None).await?;
14 println!("\n=== Running config ===\n{config}");
15
16 // --- Filtered get-config with subtree filter ---
17 let interfaces = session
18 .get_config(
19 Datastore::Running,
20 Some(r#"<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"/>"#),
21 )
22 .await?;
23 println!("\n=== Interfaces ===\n{interfaces}");
24
25 // --- Zero-copy DataPayload ---
26 let payload = session.get_config_payload(Datastore::Running, None).await?;
27 println!("\n=== Config payload size: {} bytes ===", payload.len());
28
29 // Stream XML events directly from the payload's bytes
30 let mut reader = payload.reader();
31 let mut element_count = 0;
32 loop {
33 match reader.read_event() {
34 Ok(quick_xml::events::Event::Start(_)) => element_count += 1,
35 Ok(quick_xml::events::Event::Eof) => break,
36 _ => {}
37 }
38 }
39 println!("Total XML elements: {element_count}");
40
41 // --- Edit config with candidate commit ---
42 // Note: NACM may deny access to certain data models depending on the user's
43 // permissions. We use the netconf-server model which the netconf user can write.
44 session.lock(Datastore::Candidate).await?;
45 println!("\n=== Locked candidate ===");
46
47 match session
48 .edit_config(
49 Datastore::Candidate,
50 r#"
51 <netconf-server xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-server">
52 <listen>
53 <endpoint>
54 <name>default-ssh</name>
55 <ssh>
56 <tcp-server-parameters>
57 <local-address>0.0.0.0</local-address>
58 </tcp-server-parameters>
59 </ssh>
60 </endpoint>
61 </listen>
62 </netconf-server>
63 "#,
64 )
65 .await
66 {
67 Ok(()) => {
68 println!("Edited candidate config");
69 session.commit().await?;
70 println!("Committed");
71 }
72 Err(e) => {
73 eprintln!("edit-config failed (NACM?): {e}");
74 }
75 }
76
77 session.unlock(Datastore::Candidate).await?;
78 println!("Unlocked candidate");
79
80 // --- Pipelining: send 3 RPCs before awaiting any replies ---
81 println!("\n=== Pipelining ===");
82 let fut1 = session
83 .rpc_send("<get-config><source><running/></source></get-config>")
84 .await?;
85 let fut2 = session
86 .rpc_send("<get-config><source><running/></source></get-config>")
87 .await?;
88 let fut3 = session
89 .rpc_send("<get-config><source><running/></source></get-config>")
90 .await?;
91
92 println!(
93 "Sent 3 RPCs (message-ids: {}, {}, {})",
94 fut1.message_id(),
95 fut2.message_id(),
96 fut3.message_id()
97 );
98
99 let reply1 = fut1.response().await?;
100 let reply2 = fut2.response().await?;
101 let reply3 = fut3.response().await?;
102 println!(
103 "Got 3 replies (message-ids: {}, {}, {})",
104 reply1.message_id, reply2.message_id, reply3.message_id
105 );
106
107 // --- Raw RPC for operations not covered by convenience methods ---
108 println!("\n=== Raw RPC ===");
109 let reply = session
110 .rpc_raw("<get-schema><identifier>ietf-interfaces</identifier></get-schema>")
111 .await?;
112 match reply.body {
113 netconf_rust::RpcReplyBody::Data(payload) => {
114 println!("Got schema ({} bytes)", payload.len())
115 }
116 netconf_rust::RpcReplyBody::Ok => println!("ok"),
117 netconf_rust::RpcReplyBody::Error(errors) => {
118 for e in &errors {
119 eprintln!("Error: {}: {}", e.error_tag, e.error_message);
120 }
121 }
122 }
123
124 // --- Graceful close ---
125 session.close_session().await?;
126 println!("\nSession closed (state: {})", session.state());
127
128 Ok(())
129}Sourcepub async fn kill_session(&mut self, session_id: u32) -> Result<()>
pub async fn kill_session(&mut self, session_id: u32) -> Result<()>
Force-close another NETCONF session.