diff --git a/README.md b/README.md
index 8a2f080..e9a3c93 100644
--- a/README.md
+++ b/README.md
@@ -146,7 +146,7 @@ func main() {
| [RFC5277 NETCONF Event Notifications][RFC5277] | :white_check_mark: supported |
| [RFC5717 Partial Lock Remote Procedure Call (RPC) for NETCONF][RFC5717] | :bulb: planned |
| [RFC8071 NETCONF Call Home and RESTCONF Call Home][RFC8071] | :bulb: planned |
-| [RFC6243 With-defaults Capability for NETCONF][RFC6243] | :bulb: planned |
+| [RFC6243 With-defaults Capability for NETCONF][RFC6243] | :white_check_mark: supported |
| [RFC4743 Using NETCONF over the Simple Object Access Protocol (SOAP)][RFC4743] | :x: not planned |
| [RFC4744 Using the NETCONF Protocol over the BEEP][RFC4744] | :x: not planned |
diff --git a/TODO.md b/TODO.md
index 0dfb826..e809786 100644
--- a/TODO.md
+++ b/TODO.md
@@ -21,6 +21,6 @@
- [ ] Pool/SessionManager for automatic reconnects, retries, etc.
- [ ] Call Home support
- [ ] nccurl command to issue rpc requests from the cli
-- [ ] More RFC support
+- [~] More RFC support
- [ ] Partial Lock
- [ ] with-defaults
+ - [X] with-defaults
diff --git a/rpc/config.go b/rpc/config.go
index e0c8c37..085dd66 100644
--- a/rpc/config.go
+++ b/rpc/config.go
@@ -62,20 +62,26 @@ func (u URL) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
//
// [RFC6241 7.1]: https://www.rfc-editor.org/rfc/rfc6241.html#section-7.1
type GetConfig struct {
- Source Datastore
- Filter Filter
+ Source Datastore
+ Filter Filter
+ WithDefaults WithDefaultsMode
}
func (op GetConfig) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
req := struct {
- XMLName xml.Name `xml:"urn:ietf:params:xml:ns:netconf:base:1.0 get-config"`
- Source Datastore `xml:"source"`
- Filter Filter `xml:"filter,omitempty"`
+ XMLName xml.Name `xml:"urn:ietf:params:xml:ns:netconf:base:1.0 get-config"`
+ Source Datastore `xml:"source"`
+ Filter Filter `xml:"filter,omitempty"`
+ WithDefaults *withDefaultsElement `xml:",omitempty"`
}{
Source: op.Source,
Filter: op.Filter,
}
+ if op.WithDefaults != "" {
+ req.WithDefaults = &withDefaultsElement{Mode: op.WithDefaults}
+ }
+
return e.Encode(&req)
}
@@ -232,20 +238,26 @@ func (rpc EditConfig) Exec(ctx context.Context, session *netconf.Session) error
//
// [RFC6241 7.3] https://www.rfc-editor.org/rfc/rfc6241.html#section-7.3
type CopyConfig struct {
- Source any
- Target any
+ Source any
+ Target any
+ WithDefaults WithDefaultsMode
}
func (rpc CopyConfig) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
req := struct {
- XMLName xml.Name `xml:"urn:ietf:params:xml:ns:netconf:base:1.0 copy-config"`
- Source any `xml:"source"`
- Target any `xml:"target"`
+ XMLName xml.Name `xml:"urn:ietf:params:xml:ns:netconf:base:1.0 copy-config"`
+ Source any `xml:"source"`
+ Target any `xml:"target"`
+ WithDefaults *withDefaultsElement `xml:",omitempty"`
}{
Source: rpc.Source,
Target: rpc.Target,
}
+ if rpc.WithDefaults != "" {
+ req.WithDefaults = &withDefaultsElement{Mode: rpc.WithDefaults}
+ }
+
return e.Encode(&req)
}
diff --git a/rpc/rpc.go b/rpc/rpc.go
index 3ad8d82..92236e8 100644
--- a/rpc/rpc.go
+++ b/rpc/rpc.go
@@ -34,16 +34,23 @@ type OkReply struct {
}
type Get struct {
- Filter Filter `xml:"filter,omitempty"`
+ Filter Filter
+ WithDefaults WithDefaultsMode
}
func (rpc *Get) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
req := struct {
- XMLName xml.Name `xml:"urn:ietf:params:xml:ns:netconf:base:1.0 get"`
- Filter Filter `xml:"filter,omitempty"`
+ XMLName xml.Name `xml:"urn:ietf:params:xml:ns:netconf:base:1.0 get"`
+ Filter Filter `xml:"filter,omitempty"`
+ WithDefaults *withDefaultsElement `xml:",omitempty"`
}{
Filter: rpc.Filter,
}
+
+ if rpc.WithDefaults != "" {
+ req.WithDefaults = &withDefaultsElement{Mode: rpc.WithDefaults}
+ }
+
return e.Encode(&req)
}
diff --git a/rpc/with_defaults.go b/rpc/with_defaults.go
new file mode 100644
index 0000000..95e9c51
--- /dev/null
+++ b/rpc/with_defaults.go
@@ -0,0 +1,30 @@
+package rpc
+
+import "encoding/xml"
+
+// WithDefaultsMode specifies how default values should be reported
+// as defined in RFC 6243.
+type WithDefaultsMode string
+
+const (
+ // DefaultsReportAll returns all data nodes including those set to their
+ // schema default values.
+ DefaultsReportAll WithDefaultsMode = "report-all"
+
+ // DefaultsReportAllTagged returns all data nodes, with default nodes
+ // marked with a default="true" attribute.
+ DefaultsReportAllTagged WithDefaultsMode = "report-all-tagged"
+
+ // DefaultsTrim omits data nodes set to their schema default values.
+ DefaultsTrim WithDefaultsMode = "trim"
+
+ // DefaultsExplicit reports only nodes that have been explicitly set
+ // by the client, plus any state data.
+ DefaultsExplicit WithDefaultsMode = "explicit"
+)
+
+// withDefaultsElement is a helper for marshaling the with-defaults element.
+type withDefaultsElement struct {
+ XMLName xml.Name `xml:"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults with-defaults"`
+ Mode WithDefaultsMode `xml:",chardata"`
+}
diff --git a/rpc/with_defaults_test.go b/rpc/with_defaults_test.go
new file mode 100644
index 0000000..fe16928
--- /dev/null
+++ b/rpc/with_defaults_test.go
@@ -0,0 +1,131 @@
+package rpc
+
+import (
+ "encoding/xml"
+ "testing"
+
+ "github.com/carlmjohnson/be"
+)
+
+func TestGetConfig_WithDefaults_MarshalXML(t *testing.T) {
+ tests := []struct {
+ name string
+ op GetConfig
+ expected string
+ }{
+ {
+ name: "without with-defaults",
+ op: GetConfig{
+ Source: Running,
+ },
+ expected: ``,
+ },
+ {
+ name: "report-all",
+ op: GetConfig{
+ Source: Running,
+ WithDefaults: DefaultsReportAll,
+ },
+ expected: `report-all`,
+ },
+ {
+ name: "trim",
+ op: GetConfig{
+ Source: Running,
+ WithDefaults: DefaultsTrim,
+ },
+ expected: `trim`,
+ },
+ {
+ name: "explicit",
+ op: GetConfig{
+ Source: Running,
+ WithDefaults: DefaultsExplicit,
+ },
+ expected: `explicit`,
+ },
+ {
+ name: "report-all-tagged",
+ op: GetConfig{
+ Source: Running,
+ WithDefaults: DefaultsReportAllTagged,
+ },
+ expected: `report-all-tagged`,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := xml.Marshal(tt.op)
+ be.NilErr(t, err)
+ be.Equal(t, tt.expected, string(got))
+ })
+ }
+}
+
+func TestGet_WithDefaults_MarshalXML(t *testing.T) {
+ tests := []struct {
+ name string
+ op Get
+ expected string
+ }{
+ {
+ name: "without with-defaults",
+ op: Get{},
+ expected: ``,
+ },
+ {
+ name: "report-all",
+ op: Get{
+ WithDefaults: DefaultsReportAll,
+ },
+ expected: `report-all`,
+ },
+ {
+ name: "trim",
+ op: Get{
+ WithDefaults: DefaultsTrim,
+ },
+ expected: `trim`,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := xml.Marshal(&tt.op)
+ be.NilErr(t, err)
+ be.Equal(t, tt.expected, string(got))
+ })
+ }
+}
+
+func TestCopyConfig_WithDefaults_MarshalXML(t *testing.T) {
+ tests := []struct {
+ name string
+ op CopyConfig
+ expected string
+ }{
+ {
+ name: "without with-defaults",
+ op: CopyConfig{
+ Source: Running,
+ Target: Startup,
+ },
+ expected: ``,
+ },
+ {
+ name: "with explicit",
+ op: CopyConfig{
+ Source: Running,
+ Target: Startup,
+ WithDefaults: DefaultsExplicit,
+ },
+ expected: `explicit`,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := xml.Marshal(tt.op)
+ be.NilErr(t, err)
+ be.Equal(t, tt.expected, string(got))
+ })
+ }
+}