Skip to main content

Session

Struct Session 

Source
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 an RpcFuture without waiting for the reply. Multiple RPCs can be in flight simultaneously.

Implementations§

Source§

impl Session

Source

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?
examples/basic.rs (line 6)
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}
Source

pub async fn connect_with_config( host: &str, port: u16, username: &str, password: &str, config: SessionConfig, ) -> Result<Self>

Connect with custom configuration.

Source

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).

Source

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.

Source

pub fn session_id(&self) -> u32

Examples found in repository?
examples/basic.rs (line 8)
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}
Source

pub fn server_capabilities(&self) -> &[String]

Examples found in repository?
examples/basic.rs (line 10)
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}
Source

pub fn framing_mode(&self) -> FramingMode

Examples found in repository?
examples/basic.rs (line 9)
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}
Source

pub fn state(&self) -> SessionState

Examples found in repository?
examples/basic.rs (line 126)
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}
Source

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?
examples/basic.rs (line 83)
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}
Source

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?
examples/basic.rs (line 110)
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}
Source

pub async fn get_config( &mut self, source: Datastore, filter: Option<&str>, ) -> Result<String>

Retrieve configuration from a datastore.

Examples found in repository?
examples/basic.rs (line 13)
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}
Source

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?
examples/basic.rs (line 26)
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}
Source

pub async fn get(&mut self, filter: Option<&str>) -> Result<String>

Retrieve running configuration and state data.

Source

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.

Source

pub async fn edit_config( &mut self, target: Datastore, config: &str, ) -> Result<()>

Edit the configuration of a target datastore.

Examples found in repository?
examples/basic.rs (lines 48-64)
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}
Source

pub async fn lock(&mut self, target: Datastore) -> Result<()>

Lock a datastore

Examples found in repository?
examples/basic.rs (line 44)
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}
Source

pub async fn unlock(&mut self, target: Datastore) -> Result<()>

Unlock a datastore.

Examples found in repository?
examples/basic.rs (line 77)
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}
Source

pub async fn commit(&mut self) -> Result<()>

Commit the candidate configuration to running.

Examples found in repository?
examples/basic.rs (line 69)
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}
Source

pub async fn close_session(&mut self) -> Result<()>

Gracefully close the NETCONF session.

Examples found in repository?
examples/basic.rs (line 125)
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}
Source

pub async fn kill_session(&mut self, session_id: u32) -> Result<()>

Force-close another NETCONF session.

Trait Implementations§

Source§

impl Drop for Session

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Classify for T

Source§

type Classified = T

Source§

fn classify(self) -> T

Source§

impl<T> Declassify for T

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V