Skip to main content

basic/
basic.rs

1use netconf_rust::{Datastore, Session};
2
3#[tokio::main]
4async fn main() -> netconf_rust::Result<()> {
5    // --- Connect ---
6    let 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}