├── LICENSE
├── Makefile
├── README.md
├── address_test.go
├── browser_handler.go
├── callhome
├── call_home.go
└── call_home_node.go
├── client
├── client.go
├── client_test.go
├── node.go
├── notify_test.go
├── operations_test.go
├── sse_decoder.go
├── sse_decoder_test.go
└── testdata
│ └── gold
│ └── client-read.json
├── cmd
└── rc-test-server
│ ├── main.go
│ └── startup.json
├── compliance.go
├── device
├── device.go
├── gold
│ └── yang_lib.json
├── local.go
├── map.go
├── testdata
│ └── test.yang
├── yang_lib.go
├── yang_lib_node.go
└── yang_lib_test.go
├── estream
├── api.go
├── api_test.go
├── codegen.go
├── doc.go
├── filter.go
├── recv.go
├── service.go
├── stream.go
├── sub.go
└── sub_test.go
├── form.go
├── form_test.go
├── go.mod
├── go.sum
├── node.go
├── secure
├── authenticate.go
├── authenticate_test.go
├── authorize.go
├── authorize_test.go
├── cred_gen.go
├── cred_gen_test.go
├── mgmt.go
├── mgmt_test.go
└── rbac.go
├── server.go
├── server_test.go
├── stock
├── testdata
│ ├── test.crt
│ └── test.key
├── tls.go
├── tls_test.go
└── web.go
├── testdata
├── bird.go
├── bird.yang
├── car.go
├── car.yang
├── gold
│ ├── car.json
│ ├── car.yang
│ ├── error.json
│ ├── error.xml
│ └── well-known
├── main.go
└── x.yang
├── util.go
├── util_test.go
├── wire_format.go
├── yang
├── fc-call-home-client.yang
├── fc-call-home-server.yang
├── fc-doc.yang
├── fc-map.yang
├── fc-restconf.yang
├── fc-secure.yang
├── fc-stocklib.yang
├── fc-yang.yang
├── ietf-datastores.yang
├── ietf-inet-types.yang
├── ietf-restconf-monitoring.yang
├── ietf-restconf.yang
├── ietf-rfc
│ ├── iana-bfd-types.yang
│ ├── iana-bgp-l2-encaps.yang
│ ├── iana-crypt-hash.yang
│ ├── iana-dots-signal-channel.yang
│ ├── iana-hardware.yang
│ ├── iana-if-type.yang
│ ├── iana-pseudowire-types.yang
│ ├── iana-routing-types.yang
│ ├── iana-tunnel-type.yang
│ ├── ietf-access-control-list.yang
│ ├── ietf-acldns.yang
│ ├── ietf-alarms-x733.yang
│ ├── ietf-alarms.yang
│ ├── ietf-bfd-ip-mh.yang
│ ├── ietf-bfd-ip-sh.yang
│ ├── ietf-bfd-lag.yang
│ ├── ietf-bfd-mpls.yang
│ ├── ietf-bfd-types.yang
│ ├── ietf-bfd.yang
│ ├── ietf-complex-types.yang
│ ├── ietf-connection-oriented-oam.yang
│ ├── ietf-connectionless-oam-methods.yang
│ ├── ietf-connectionless-oam.yang
│ ├── ietf-datastores.yang
│ ├── ietf-dc-fabric-topology-state.yang
│ ├── ietf-dc-fabric-topology.yang
│ ├── ietf-dc-fabric-types.yang
│ ├── ietf-dhcpv6-client.yang
│ ├── ietf-dhcpv6-common.yang
│ ├── ietf-dhcpv6-relay.yang
│ ├── ietf-dhcpv6-server.yang
│ ├── ietf-dots-call-home.yang
│ ├── ietf-dots-data-channel.yang
│ ├── ietf-dots-mapping.yang
│ ├── ietf-dots-signal-channel.yang
│ ├── ietf-dots-telemetry.yang
│ ├── ietf-ethernet-segment.yang
│ ├── ietf-ethertypes.yang
│ ├── ietf-factory-default.yang
│ ├── ietf-foo.yang
│ ├── ietf-geo-location.yang
│ ├── ietf-hardware-state.yang
│ ├── ietf-hardware.yang
│ ├── ietf-i2nsf-ike.yang
│ ├── ietf-i2nsf-ikec.yang
│ ├── ietf-i2nsf-ikeless.yang
│ ├── ietf-i2rs-rib.yang
│ ├── ietf-igmp-mld-snooping.yang
│ ├── ietf-igmp-mld.yang
│ ├── ietf-inet-types.yang
│ ├── ietf-interface-protection.yang
│ ├── ietf-interfaces.yang
│ ├── ietf-ip.yang
│ ├── ietf-ipfix-psamp.yang
│ ├── ietf-ipv4-unicast-routing.yang
│ ├── ietf-ipv6-router-advertisements.yang
│ ├── ietf-ipv6-unicast-routing.yang
│ ├── ietf-isis-reverse-metric.yang
│ ├── ietf-isis.yang
│ ├── ietf-key-chain.yang
│ ├── ietf-l2-topology-state.yang
│ ├── ietf-l2-topology.yang
│ ├── ietf-l2vpn-ntw.yang
│ ├── ietf-l2vpn-svc.yang
│ ├── ietf-l3-unicast-topology-state.yang
│ ├── ietf-l3-unicast-topology.yang
│ ├── ietf-l3vpn-ntw.yang
│ ├── ietf-l3vpn-svc.yang
│ ├── ietf-layer0-types.yang
│ ├── ietf-lime-time-types.yang
│ ├── ietf-lmap-common.yang
│ ├── ietf-lmap-control.yang
│ ├── ietf-lmap-report.yang
│ ├── ietf-logical-network-element.yang
│ ├── ietf-microwave-radio-link.yang
│ ├── ietf-microwave-types.yang
│ ├── ietf-module-tags-state.yang
│ ├── ietf-module-tags.yang
│ ├── ietf-mpls-ldp-extended.yang
│ ├── ietf-mpls-ldp.yang
│ ├── ietf-mpls.yang
│ ├── ietf-msdp.yang
│ ├── ietf-mud-detext-example.yang
│ ├── ietf-mud.yang
│ ├── ietf-netconf-acm.yang
│ ├── ietf-netconf-monitoring.yang
│ ├── ietf-netconf-nmda.yang
│ ├── ietf-netconf-notifications.yang
│ ├── ietf-netconf-partial-lock.yang
│ ├── ietf-netconf-time.yang
│ ├── ietf-netconf-with-defaults.yang
│ ├── ietf-netconf.yang
│ ├── ietf-network-instance.yang
│ ├── ietf-network-state.yang
│ ├── ietf-network-topology-state.yang
│ ├── ietf-network-topology.yang
│ ├── ietf-network.yang
│ ├── ietf-nmda-compare.yang
│ ├── ietf-notification-capabilities.yang
│ ├── ietf-ntp.yang
│ ├── ietf-origin.yang
│ ├── ietf-ospf.yang
│ ├── ietf-packet-fields.yang
│ ├── ietf-pim-base.yang
│ ├── ietf-pim-bidir.yang
│ ├── ietf-pim-dm.yang
│ ├── ietf-pim-rp.yang
│ ├── ietf-pim-sm.yang
│ ├── ietf-ptp.yang
│ ├── ietf-restconf-monitoring.yang
│ ├── ietf-restconf-subscribed-notifications.yang
│ ├── ietf-restconf.yang
│ ├── ietf-routing-policy.yang
│ ├── ietf-routing-types.yang
│ ├── ietf-routing.yang
│ ├── ietf-segment-routing-common.yang
│ ├── ietf-segment-routing-mpls.yang
│ ├── ietf-segment-routing.yang
│ ├── ietf-snmp-common.yang
│ ├── ietf-snmp-community.yang
│ ├── ietf-snmp-engine.yang
│ ├── ietf-snmp-notification.yang
│ ├── ietf-snmp-proxy.yang
│ ├── ietf-snmp-ssh.yang
│ ├── ietf-snmp-target.yang
│ ├── ietf-snmp-tls.yang
│ ├── ietf-snmp-tsm.yang
│ ├── ietf-snmp-usm.yang
│ ├── ietf-snmp-vacm.yang
│ ├── ietf-snmp.yang
│ ├── ietf-softwire-br.yang
│ ├── ietf-softwire-ce.yang
│ ├── ietf-softwire-common.yang
│ ├── ietf-subscribed-notifications.yang
│ ├── ietf-system-capabilities.yang
│ ├── ietf-system-tacacs-plus.yang
│ ├── ietf-system.yang
│ ├── ietf-sztp-bootstrap-server.yang
│ ├── ietf-sztp-conveyed-info.yang
│ ├── ietf-te-packet-types.yang
│ ├── ietf-te-topology-state.yang
│ ├── ietf-te-topology.yang
│ ├── ietf-te-types.yang
│ ├── ietf-template.yang
│ ├── ietf-twamp.yang
│ ├── ietf-voucher-request.yang
│ ├── ietf-voucher.yang
│ ├── ietf-vpn-common.yang
│ ├── ietf-vrrp.yang
│ ├── ietf-wson-topology.yang
│ ├── ietf-x509-cert-to-name.yang
│ ├── ietf-yang-instance-data.yang
│ ├── ietf-yang-library.yang
│ ├── ietf-yang-metadata.yang
│ ├── ietf-yang-patch.yang
│ ├── ietf-yang-push.yang
│ ├── ietf-yang-schema-mount.yang
│ ├── ietf-yang-smiv2.yang
│ ├── ietf-yang-structure-ext.yang
│ └── ietf-yang-types.yang
├── ietf-yang-library.yang
└── ietf-yang-types.yang
└── ypath.go
/Makefile:
--------------------------------------------------------------------------------
1 | # export YANGPATH=$(abspath ./yang)
2 |
3 | test:
4 | go test -coverprofile test-coverage.out . ./...
5 | go tool cover -html=test-coverage.out -o test-coverage.html
6 | go tool cover -func test-coverage.out
7 |
8 | TARGET_DOCS = \
9 | ietf-subscribed-notifications
10 |
11 | DOCS_OUT = \
12 | $(foreach F,$(TARGET_DOCS),docs/$(F).html)
13 |
14 | .PHONY: docs
15 | docs: $(DOCS_OUT)
16 |
17 | $(DOCS_OUT) : docs/%.html :
18 | go run github.com/freeconf/yang/cmd/fc-yang doc \
19 | -f dot \
20 | -on replay \
21 | -on configured \
22 | -ypath yang/ietf-rfc \
23 | -module $* > docs/$*.dot
24 | dot -Tsvg docs/$*.dot -o docs/$*.svg
25 | go run github.com/freeconf/yang/cmd/fc-yang doc \
26 | -f html \
27 | -on replay \
28 | -on configured \
29 | -ypath yang/ietf-rfc \
30 | -module $* > $@
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 
2 |
3 | [Add support or configuration, metrics, alerts and management functions to your application!](https://freeconf.org)
4 |
5 | # In this repository
6 |
7 | * [IETF RESTCONF RFC8040](https://tools.ietf.org/html/rfc8040) server-side and client-side protocol handlers. See compliance for all [supporting RFCs](https://freeconf.org/docs/reference/compliance/rfcs/).
8 | * Works in conjunction with [FreeCONF YANG project](https://github.com/freeconf/yang)
9 |
10 | # Getting Started
11 |
12 | [Getting started docs here](https://freeconf.org/docs/gettingstarted/)
13 |
14 | # Building Requirements
15 |
16 | Requires Go version 1.20 or greater.
17 |
18 | # Getting the source
19 |
20 | ```bash
21 | go get -u github.com/freeconf/restconf
22 | ```
23 |
24 | # Resources
25 | * [Web site](https://freeconf.org)
26 | * [Documentation](https://freeconf.org/docs)
27 | * [Getting Started](https://freeconf.org/docs/gettingstarted/)
28 | * [Next Steps](https://freeconf.org/docs/examples/next-step/)
29 | * [Generating Documentation](https://freeconf.org/docs/reference/docs/) from YANG files
30 | * [RFC Compliance](https://freeconf.org/docs/reference/compliance/rfcs/)
31 | * [Go API Docs](https://pkg.go.dev/github.com/freeconf/restconf)
32 | * [Issues](https://github.com/freeconf/restconf/issues)
33 | * [Discussions](https://github.com/freeconf/restconf/discussions)
34 |
--------------------------------------------------------------------------------
/address_test.go:
--------------------------------------------------------------------------------
1 | package restconf
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/freeconf/yang/fc"
7 | )
8 |
9 | func TestFindDeviceIdInUrl(t *testing.T) {
10 | dev := FindDeviceIdInUrl("http://server:port/restconf=abc/")
11 | fc.AssertEqual(t, "abc", dev)
12 | dev = FindDeviceIdInUrl("http://server:port/restconf/")
13 | fc.AssertEqual(t, "", dev)
14 | }
15 |
--------------------------------------------------------------------------------
/callhome/call_home.go:
--------------------------------------------------------------------------------
1 | package callhome
2 |
3 | import (
4 | "container/list"
5 | "fmt"
6 | "os"
7 | "time"
8 |
9 | "github.com/freeconf/restconf/client"
10 | "github.com/freeconf/restconf/device"
11 | "github.com/freeconf/yang/fc"
12 | "github.com/freeconf/yang/node"
13 | "github.com/freeconf/yang/nodeutil"
14 | )
15 |
16 | // Implements RFC Draft
17 | //
18 | // https://www.rfc-editor.org/rfc/rfc8071.html
19 | type CallHome struct {
20 | options Options
21 | proto device.ProtocolHandler
22 | Registered bool
23 | registrar device.Device // handle to the remote controller
24 | LastErr string
25 | listeners *list.List
26 | }
27 |
28 | type Options struct {
29 | DeviceId string
30 | Address string
31 | LocalAddress string
32 | RetryRateMs int
33 | }
34 |
35 | func DefaultOptions() Options {
36 | return Options{
37 | DeviceId: os.Getenv("DEVICE_ID"),
38 | Address: os.Getenv("CALLHOME_ADDR"),
39 | }
40 | }
41 |
42 | func New(proto device.ProtocolHandler) *CallHome {
43 | return &CallHome{
44 | proto: proto,
45 | listeners: list.New(),
46 | options: DefaultOptions(),
47 | }
48 | }
49 |
50 | // Install creates and registered Call Home support into local device.
51 | func Install(d *device.Local) error {
52 | ch := New(client.ProtocolHandler(d.SchemaSource()))
53 | return d.Add("fc-call-home-client", CallHomeNode(ch))
54 | }
55 |
56 | type RegisterUpdate int
57 |
58 | const (
59 | Register RegisterUpdate = iota
60 | Unregister
61 | )
62 |
63 | type RegisterListener func(d device.Device, update RegisterUpdate)
64 |
65 | func (callh *CallHome) OnRegister(l RegisterListener) nodeutil.Subscription {
66 | if callh.Registered {
67 | l(callh.registrar, Register)
68 | }
69 | return nodeutil.NewSubscription(callh.listeners, callh.listeners.PushBack(l))
70 | }
71 |
72 | func (callh *CallHome) Options() Options {
73 | return callh.options
74 | }
75 |
76 | func (callh *CallHome) ApplyOptions(options Options) error {
77 | if nonfatal := callh.unregister(); nonfatal != nil {
78 | fc.Err.Printf("could not unregister. %s", nonfatal)
79 | }
80 | callh.options = options
81 | callh.Registered = false
82 | if callh.options.Address == "" {
83 | fc.Debug.Print("no call home address configured")
84 | return nil
85 | }
86 | fc.Debug.Print("connecting to ", callh.options.Address)
87 | callh.Register()
88 | return nil
89 | }
90 |
91 | func (callh *CallHome) updateListeners(registrar device.Device, update RegisterUpdate) {
92 | callh.registrar = registrar
93 | p := callh.listeners.Front()
94 | for p != nil {
95 | p.Value.(RegisterListener)(callh.registrar, update)
96 | p = p.Next()
97 | }
98 | }
99 |
100 | func (callh *CallHome) Register() {
101 | retry:
102 | registrar, err := callh.proto(callh.options.Address)
103 | if err != nil {
104 | fc.Err.Printf("failed to build device with address %s. %s", callh.options.Address, err)
105 | } else {
106 | if err = callh.register(registrar); err != nil {
107 | fc.Err.Printf("failed to register %s", err)
108 | } else {
109 | return
110 | }
111 | }
112 | if callh.options.RetryRateMs == 0 {
113 | panic("failed to register and no retry rate configured")
114 | }
115 | <-time.After(time.Duration(callh.options.RetryRateMs) * time.Millisecond)
116 | goto retry
117 | }
118 |
119 | func (callh *CallHome) serverApi(registrar device.Device) (*node.Browser, error) {
120 | modname := "fc-call-home-server"
121 | reg, err := registrar.Browser(modname)
122 | if err != nil {
123 | return nil, err
124 | }
125 | if reg == nil {
126 | return nil, fmt.Errorf("%s module not found on remote target", modname)
127 | }
128 | return reg, nil
129 | }
130 |
131 | func (callh *CallHome) unregister() error {
132 | if !callh.Registered {
133 | return nil
134 | }
135 | registrar, err := callh.proto(callh.options.Address)
136 | if err != nil {
137 | return err
138 | }
139 | defer func() {
140 | callh.updateListeners(registrar, Unregister)
141 | callh.Registered = false
142 | }()
143 | reg, err := callh.serverApi(registrar)
144 | if err != nil {
145 | return err
146 | }
147 | sel, err := reg.Root().Find("register")
148 | if err != nil {
149 | return err
150 | }
151 | _, err = sel.Action(nil)
152 | return err
153 | }
154 |
155 | func (callh *CallHome) register(registrar device.Device) error {
156 | reg, err := callh.serverApi(registrar)
157 | if err != nil {
158 | return err
159 | }
160 | r := map[string]interface{}{
161 | "deviceId": callh.options.DeviceId,
162 | "address": callh.options.LocalAddress,
163 | }
164 | sel, err := reg.Root().Find("register")
165 | if err != nil {
166 | return err
167 | }
168 | _, err = sel.Action(nodeutil.ReflectChild(r))
169 | if err == nil {
170 | callh.updateListeners(registrar, Register)
171 | callh.Registered = true
172 | }
173 | return err
174 | }
175 |
--------------------------------------------------------------------------------
/callhome/call_home_node.go:
--------------------------------------------------------------------------------
1 | package callhome
2 |
3 | import (
4 | "github.com/freeconf/yang/node"
5 | "github.com/freeconf/yang/nodeutil"
6 | "github.com/freeconf/yang/val"
7 | )
8 |
9 | func CallHomeNode(ch *CallHome) node.Node {
10 | options := ch.Options()
11 | return &nodeutil.Extend{
12 | OnPeek: func(parent node.Node, sel *node.Selection, consumer interface{}) interface{} {
13 | return ch
14 | },
15 | Base: nodeutil.ReflectChild(&options),
16 | OnField: func(p node.Node, r node.FieldRequest, hnd *node.ValueHandle) error {
17 | switch r.Meta.Ident() {
18 | case "registered":
19 | hnd.Val = val.Bool(ch.Registered)
20 | default:
21 | return p.Field(r, hnd)
22 | }
23 | return nil
24 | },
25 | OnEndEdit: func(p node.Node, r node.NodeRequest) error {
26 | if err := p.EndEdit(r); err != nil {
27 | return err
28 | }
29 | return ch.ApplyOptions(options)
30 | },
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/client/client_test.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "strings"
7 | "testing"
8 |
9 | "github.com/freeconf/restconf"
10 | "github.com/freeconf/restconf/device"
11 | "github.com/freeconf/restconf/testdata"
12 | "github.com/freeconf/yang/fc"
13 | "github.com/freeconf/yang/node"
14 | "github.com/freeconf/yang/nodeutil"
15 | "github.com/freeconf/yang/source"
16 | )
17 |
18 | var updateFlag = flag.Bool("update", false, "update golden files instead of verifying against them")
19 |
20 | func TestClient(t *testing.T) {
21 | // setup a server on a port and use client to connect
22 | ypath := source.Path("../yang:../testdata")
23 | car := testdata.New()
24 | local := device.New(ypath)
25 | local.Add("car", testdata.Manage(car))
26 | s := restconf.NewServer(local)
27 | defer s.Close()
28 | cfg := `{
29 | "fc-restconf": {
30 | "debug": true,
31 | "web" : {
32 | "port": ":10999"
33 | }
34 | },
35 | "car" : {
36 | "speed": 5
37 | }
38 | }`
39 | fc.RequireEqual(t, nil, local.ApplyStartupConfig(strings.NewReader(cfg)))
40 |
41 | testClient := func(compliance restconf.ComplianceOptions) error {
42 | t.Logf("compliance %s", compliance)
43 | c := Client{YangPath: ypath, Complance: compliance}
44 | dev, err := c.NewDevice("http://localhost:10999/restconf")
45 | fc.RequireEqual(t, nil, err)
46 | b, err := dev.Browser("car")
47 | fc.RequireEqual(t, nil, err)
48 |
49 | root := b.Root()
50 |
51 | // read
52 | actual, err := nodeutil.WritePrettyJSON(sel(root.Constrain("content=config")))
53 | fc.AssertEqual(t, nil, err)
54 | fc.Gold(t, *updateFlag, []byte(actual), "testdata/gold/client-read.json")
55 |
56 | // test
57 | fc.AssertEqual(t, true, sel(root.Find("tire=0")) != nil)
58 | tireSel, _ := root.Find("tire=99")
59 | fc.AssertEqual(t, true, tireSel == nil)
60 |
61 | // rpc
62 | before := car.Tire[0].Wear
63 | sel(sel(root.Find("replaceTires")).Action(nil))
64 | after := car.Tire[0].Wear
65 | fc.AssertEqual(t, false, before > after, fmt.Sprintf("%f > %f", before, after))
66 |
67 | // rpc i/o
68 | req := struct {
69 | Source string
70 | }{
71 | Source: "tripa",
72 | }
73 | out := sel(sel(root.Find("getMiles")).Action(&nodeutil.Node{Object: &req}))
74 | resp := struct {
75 | Miles int64
76 | }{}
77 | fc.AssertEqual(t, nil, out.UpsertInto(&nodeutil.Node{Object: &resp}))
78 | fc.AssertEqual(t, true, resp.Miles > 0, fmt.Sprintf("miles %d", resp.Miles))
79 |
80 | // notify
81 | done := make(chan bool)
82 | sub, err := sel(root.Find("update?filter=running%3D'false'")).Notifications(func(n node.Notification) {
83 | done <- true
84 | })
85 | fc.RequireEqual(t, nil, err)
86 | <-done
87 | sub()
88 | return nil
89 | }
90 | testClient(restconf.Simplified)
91 | testClient(restconf.Strict)
92 | }
93 |
94 | func sel(sel *node.Selection, err error) *node.Selection {
95 | if err != nil {
96 | panic(err)
97 | }
98 | return sel
99 | }
100 |
--------------------------------------------------------------------------------
/client/notify_test.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | "testing"
7 | "time"
8 |
9 | "github.com/freeconf/restconf"
10 | "github.com/freeconf/restconf/device"
11 | "github.com/freeconf/yang/fc"
12 | "github.com/freeconf/yang/node"
13 | "github.com/freeconf/yang/nodeutil"
14 | "github.com/freeconf/yang/parser"
15 | "github.com/freeconf/yang/source"
16 | )
17 |
18 | func TestClientNotif(t *testing.T) {
19 | ypath := source.Path("../testdata:../yang")
20 | m := parser.RequireModule(ypath, "x")
21 | var s *restconf.Server
22 | send := make(chan string)
23 | n := &nodeutil.Basic{
24 | OnNotify: func(r node.NotifyRequest) (node.NotifyCloser, error) {
25 | go func() {
26 | s := <-send
27 | fmt.Println("sending message")
28 | r.Send(nodeutil.ReflectChild(map[string]interface{}{
29 | "z": s,
30 | }))
31 | }()
32 | return func() error {
33 | return nil
34 | }, nil
35 | },
36 | }
37 | bServer := node.NewBrowser(m, n)
38 | d := device.New(ypath)
39 | d.AddBrowser(bServer)
40 | s = restconf.NewServer(d)
41 | defer s.Close()
42 | err := d.ApplyStartupConfig(strings.NewReader(`
43 | {
44 | "fc-restconf" : {
45 | "web": {
46 | "port" : ":9081"
47 | },
48 | "debug" : true
49 | }
50 | }`))
51 | if err != nil {
52 | t.Fatal(err)
53 | }
54 | // wait for server to startup
55 | <-time.After(2 * time.Second)
56 |
57 | recv := make(chan string)
58 |
59 | testClient := func(compliance restconf.ComplianceOptions) error {
60 | factory := Client{YangPath: ypath, Complance: compliance}
61 | dev, err := factory.NewDevice("http://localhost:9081/restconf")
62 | if err != nil {
63 | return err
64 | }
65 | b, err := dev.Browser("x")
66 | if err != nil {
67 | return err
68 | }
69 | sub, err := sel(b.Root().Find("y")).Notifications(func(msg node.Notification) {
70 | fmt.Println("receiving message")
71 | actual, err := nodeutil.WriteJSON(msg.Event)
72 | if err != nil {
73 | panic(err)
74 | }
75 | recv <- actual
76 | })
77 | if err != nil {
78 | return err
79 | }
80 | send <- "original session"
81 | actual := <-recv
82 | if actual != `{"z":"original session"}` {
83 | return fmt.Errorf("not expected output %s", actual)
84 | }
85 | sub()
86 | return nil
87 | }
88 |
89 | fc.AssertEqual(t, nil, testClient(restconf.Simplified))
90 | fc.AssertEqual(t, nil, testClient(restconf.Strict))
91 | }
92 |
--------------------------------------------------------------------------------
/client/operations_test.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "io"
7 | "strings"
8 | "testing"
9 |
10 | "io/ioutil"
11 |
12 | "github.com/freeconf/yang/fc"
13 | "github.com/freeconf/yang/node"
14 | "github.com/freeconf/yang/nodeutil"
15 | "github.com/freeconf/yang/parser"
16 | )
17 |
18 | func Test_ClientOperations(t *testing.T) {
19 | m, err := parser.LoadModuleFromString(nil, `module x {namespace ""; prefix ""; revision 0;
20 | container car {
21 | container mileage {
22 | leaf odometer {
23 | type int32;
24 | }
25 | leaf trip {
26 | type int32;
27 | }
28 | }
29 | container make {
30 | leaf model {
31 | type string;
32 | }
33 | }
34 | }
35 | }`)
36 | if err != nil {
37 | t.Fatal(err)
38 | }
39 | support := newTestDriverFlowSupport(t)
40 | expected := `{"mileage":{"odometer":1000}}`
41 | support.get = map[string]string{
42 | "car": expected,
43 | }
44 | d := &clientNode{support: support}
45 | b := node.NewBrowser(m, d.node())
46 | if actual, err := nodeutil.WriteJSON(sel(b.Root().Find("car"))); err != nil {
47 | t.Error(err)
48 | } else {
49 | fc.AssertEqual(t, expected, actual)
50 | }
51 |
52 | support.get = map[string]string{
53 | "car": `{}`,
54 | }
55 | expectedEdit := `{"mileage":{"odometer":1001}}`
56 | edit, err := nodeutil.ReadJSON(expectedEdit)
57 | fc.RequireEqual(t, nil, err)
58 | fc.RequireEqual(t, nil, sel(b.Root().Find("car")).UpsertFrom(edit))
59 | fc.AssertEqual(t, expectedEdit, support.patch["car"])
60 | }
61 |
62 | type testDriverFlowSupport struct {
63 | t *testing.T
64 | get map[string]string
65 | put map[string]string
66 | post map[string]string
67 | patch map[string]string
68 | options map[string]string
69 | }
70 |
71 | func newTestDriverFlowSupport(t *testing.T) *testDriverFlowSupport {
72 | return &testDriverFlowSupport{
73 | t: t,
74 | put: make(map[string]string),
75 | post: make(map[string]string),
76 | patch: make(map[string]string),
77 | options: make(map[string]string),
78 | }
79 | }
80 |
81 | func (self *testDriverFlowSupport) clientDo(method string, params string, p *node.Path, payload io.Reader) (io.ReadCloser, error) {
82 | path := p.StringNoModule()
83 | var to map[string]string
84 | switch method {
85 | case "GET":
86 | in, found := self.get[path]
87 | if !found {
88 | return nil, fmt.Errorf("no response for %s", path)
89 | }
90 | return io.NopCloser(strings.NewReader(in)), nil
91 | case "PUT":
92 | to = self.put
93 | case "POST":
94 | to = self.post
95 | case "PATCH":
96 | to = self.patch
97 | case "OPTIONS":
98 | to = self.options
99 | }
100 |
101 | var body string
102 | if payload != nil {
103 | data, _ := ioutil.ReadAll(payload)
104 | body = string(data)
105 | }
106 | to[path] = string(body)
107 | return nil, nil
108 | }
109 |
110 | func (self *testDriverFlowSupport) clientStream(params string, p *node.Path, ctx context.Context) (<-chan streamEvent, error) {
111 | panic("not implemented")
112 | }
113 |
--------------------------------------------------------------------------------
/client/sse_decoder.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "bufio"
5 | "bytes"
6 | "io"
7 | "strings"
8 | )
9 |
10 | const (
11 | sseDataPrefix = "data: "
12 | )
13 |
14 | // we only have to decode whatever server is sending. so far it's just "data: " fields
15 | func decodeSse(in io.Reader) <-chan []byte {
16 | events := make(chan []byte)
17 | r := bufio.NewReader(in)
18 | go func() {
19 | defer close(events)
20 | var buff bytes.Buffer
21 | send := func() {
22 | if buff.Len() > 0 {
23 | orig := buff.Bytes()
24 | dup := make([]byte, len(orig))
25 | copy(dup, orig)
26 | events <- dup
27 | buff.Reset()
28 | }
29 | }
30 | for {
31 | line, err := r.ReadBytes('\n')
32 | size := len(line)
33 | if size <= 1 {
34 | send()
35 | } else if strings.HasPrefix(string(line), sseDataPrefix) {
36 | end := size
37 | if line[end-1] == '\n' {
38 | end--
39 | }
40 | chunk := line[len(sseDataPrefix):end]
41 | buff.Write(chunk)
42 | }
43 | if err != nil {
44 | // EOF or other; stream is no longer
45 | send()
46 | return
47 | }
48 | }
49 | }()
50 | return events
51 | }
52 |
--------------------------------------------------------------------------------
/client/sse_decoder_test.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "strings"
5 | "testing"
6 | )
7 |
8 | func TestSseDecode(t *testing.T) {
9 | tests := []struct {
10 | payload string
11 | expected []string
12 | }{
13 | {
14 | payload: `
15 | data: x
16 |
17 | data: hello
18 | data: world`,
19 | expected: []string{"x", "hello world"},
20 | },
21 | {
22 | payload: "data: z",
23 | expected: []string{"z"},
24 | },
25 | {
26 | payload: `
27 | ignored: z
28 | data: foo
29 | `,
30 | expected: []string{"foo"},
31 | },
32 | }
33 | for _, test := range tests {
34 | events := decodeSse(strings.NewReader(test.payload))
35 | for _, expected := range test.expected {
36 | actual := <-events
37 | if expected != string(actual) {
38 | t.Errorf("expected '%s' got '%s'", expected, actual)
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/client/testdata/gold/client-read.json:
--------------------------------------------------------------------------------
1 | {
2 | "tire":[
3 | {
4 | "pos":0,
5 | "size":"15"},
6 | {
7 | "pos":1,
8 | "size":"15"},
9 | {
10 | "pos":2,
11 | "size":"15"},
12 | {
13 | "pos":3,
14 | "size":"15"}],
15 | "speed":5}
--------------------------------------------------------------------------------
/cmd/rc-test-server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/freeconf/restconf"
5 | "github.com/freeconf/restconf/device"
6 | "github.com/freeconf/yang/fc"
7 | "github.com/freeconf/yang/source"
8 | "github.com/freeconf/yang/testdata/car"
9 | )
10 |
11 | // Start the car app with RESTCONF Server support to test RESTCONF clients
12 | // against.
13 |
14 | func main() {
15 | fc.DebugLog(true)
16 | c := car.New()
17 | api := car.Manage(c)
18 | ypath := source.Any(
19 | source.Dir("../../yang"),
20 | restconf.InternalYPath,
21 | car.YPath,
22 | )
23 | d := device.New(ypath)
24 | d.Add("car", api)
25 | restconf.NewServer(d)
26 | chkerr(d.ApplyStartupConfigFile("startup.json"))
27 | select {}
28 | }
29 |
30 | func chkerr(err error) {
31 | if err != nil {
32 | panic(err)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/cmd/rc-test-server/startup.json:
--------------------------------------------------------------------------------
1 | {
2 | "car" : {},
3 | "fc-restconf" : {
4 | "web" : {
5 | "port": ":8080"
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/compliance.go:
--------------------------------------------------------------------------------
1 | package restconf
2 |
3 | import "fmt"
4 |
5 | // Compliance is the global variable that sets the default behavior if the
6 | // FreeCONF RESTCONF library.
7 | //
8 | // By default this is for strict IETF compliance!
9 | //
10 | // This sets just the default behavior of data structures, each individual
11 | // instance should allow for controlling the compliance of that instance should
12 | // you need to have instances in different modes at the same time.
13 | var Strict = ComplianceOptions{}
14 |
15 | // Simplified are the settings pre 2023 before true IETF compliance was
16 | // attempted. To use this:
17 | //
18 | // restconf.Compliance = restconf.Simplified
19 | //
20 | // or you can just set individual settings on restconf.Compliance global variable.
21 | var Simplified = ComplianceOptions{
22 | AllowRpcUnderData: true,
23 | DisableNotificationWrapper: true,
24 | DisableActionWrapper: true,
25 | SimpleErrorResponse: true,
26 | QualifyNamespaceDisabled: true,
27 | }
28 |
29 | // ComplianceOptions hold all the compliance settings. If you enable any of these
30 | // settings, then you run the risk of not being complatible with other RESTCONF
31 | // implementations
32 | type ComplianceOptions struct {
33 |
34 | // allow rpc to serve under /restconf/data/{module:}/{rpc} which while intuative
35 | // it is not in compliance w/RESTCONF spec
36 | AllowRpcUnderData bool
37 |
38 | // IETF notification messages with extra data including
39 | // event time and ietf-restconf:notfication container
40 | // https://datatracker.ietf.org/doc/html/rfc8040#section-6.4
41 | DisableNotificationWrapper bool
42 |
43 | // IETF rpc/action inputs and outputs are wrapped with extra container
44 | // https://datatracker.ietf.org/doc/html/rfc8040#section-6.
45 | DisableActionWrapper bool
46 |
47 | // Errors have a specific structure
48 | // https://datatracker.ietf.org/doc/html/rfc8040#section-3.6.3
49 | SimpleErrorResponse bool
50 |
51 | // QualifyNamespaceDisabled when true then all JSON object keys will not
52 | // include YANG module according to RFC7952.
53 | QualifyNamespaceDisabled bool
54 | }
55 |
56 | func (compliance ComplianceOptions) String() string {
57 | if compliance == Simplified {
58 | return "simplified"
59 | }
60 | if compliance == Strict {
61 | return "strict"
62 | }
63 | return fmt.Sprintf("mixed %#v", compliance)
64 | }
65 |
--------------------------------------------------------------------------------
/device/device.go:
--------------------------------------------------------------------------------
1 | package device
2 |
3 | import (
4 | "github.com/freeconf/yang/meta"
5 | "github.com/freeconf/yang/node"
6 | "github.com/freeconf/yang/source"
7 | )
8 |
9 | // Create device from address string associated with protocol
10 | // often referred to south/east/west bound
11 | type ProtocolHandler func(addr string) (Device, error)
12 |
13 | type Device interface {
14 | SchemaSource() source.Opener
15 | UiSource() source.Opener
16 | Browser(module string) (*node.Browser, error)
17 | Modules() map[string]*meta.Module
18 | Close()
19 | }
20 |
--------------------------------------------------------------------------------
/device/gold/yang_lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "yang-library":{
3 | "module-set":[
4 | {
5 | "name":"all",
6 | "module":[
7 | {
8 | "name":"bird",
9 | "revision":"0",
10 | "namespace":"",
11 | "location":"bird"},
12 | {
13 | "name":"ietf-yang-library",
14 | "revision":"2019-01-04",
15 | "namespace":"urn:ietf:params:xml:ns:yang:ietf-yang-library",
16 | "location":"ietf-yang-library"}]}]},
17 | "modules-state":{
18 | "module":[
19 | {
20 | "name":"bird",
21 | "revision":"0",
22 | "schema":"bird",
23 | "namespace":""},
24 | {
25 | "name":"ietf-yang-library",
26 | "revision":"2019-01-04",
27 | "schema":"ietf-yang-library",
28 | "namespace":"urn:ietf:params:xml:ns:yang:ietf-yang-library"}]}}
--------------------------------------------------------------------------------
/device/local.go:
--------------------------------------------------------------------------------
1 | package device
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io"
7 | "os"
8 |
9 | "github.com/freeconf/yang/fc"
10 | "github.com/freeconf/yang/meta"
11 | "github.com/freeconf/yang/node"
12 | "github.com/freeconf/yang/nodeutil"
13 | "github.com/freeconf/yang/parser"
14 | "github.com/freeconf/yang/source"
15 | )
16 |
17 | type Local struct {
18 | browsers map[string]*node.Browser
19 | schemaSource source.Opener
20 | uiSource source.Opener
21 | }
22 |
23 | func New(schemaSource source.Opener) *Local {
24 | return &Local{
25 | schemaSource: schemaSource,
26 | browsers: make(map[string]*node.Browser),
27 | }
28 | }
29 |
30 | func NewWithUi(schemaSource source.Opener, uiSource source.Opener) *Local {
31 | return &Local{
32 | schemaSource: schemaSource,
33 | uiSource: uiSource,
34 | browsers: make(map[string]*node.Browser),
35 | }
36 | }
37 |
38 | func (self *Local) SchemaSource() source.Opener {
39 | return self.schemaSource
40 | }
41 |
42 | func (self *Local) UiSource() source.Opener {
43 | return self.uiSource
44 | }
45 |
46 | func (self *Local) Modules() map[string]*meta.Module {
47 | mods := make(map[string]*meta.Module)
48 | for _, b := range self.browsers {
49 | mods[b.Meta.Ident()] = b.Meta
50 | }
51 | return mods
52 | }
53 |
54 | func (self *Local) Browser(module string) (*node.Browser, error) {
55 | return self.browsers[module], nil
56 | }
57 |
58 | func (self *Local) Close() {
59 | }
60 |
61 | func (self *Local) Add(module string, n node.Node) error {
62 | m, err := parser.LoadModule(self.schemaSource, module)
63 | if err != nil {
64 | return err
65 | }
66 | self.browsers[module] = node.NewBrowser(m, n)
67 | return nil
68 | }
69 |
70 | func (self *Local) AddSource(module string, src func() node.Node) error {
71 | m, err := parser.LoadModule(self.schemaSource, module)
72 | if err != nil {
73 | return err
74 | }
75 | self.browsers[module] = node.NewBrowserSource(m, src)
76 | return nil
77 | }
78 |
79 | func (self *Local) AddBrowser(b *node.Browser) {
80 | self.browsers[b.Meta.Ident()] = b
81 | }
82 |
83 | func (self *Local) ApplyStartupConfig(config io.Reader) error {
84 | var cfg map[string]interface{}
85 | if err := json.NewDecoder(config).Decode(&cfg); err != nil {
86 | return err
87 | }
88 | return self.ApplyStartupConfigData(cfg)
89 | }
90 |
91 | func (self *Local) ApplyStartupConfigData(config map[string]interface{}) error {
92 | for module, data := range config {
93 | b, err := self.Browser(module)
94 | if err != nil {
95 | return err
96 | }
97 | if b == nil {
98 | return fmt.Errorf("%w. browser not found: %s", fc.NotFoundError, module)
99 | }
100 | moduleCfg := data.(map[string]interface{})
101 | if err := b.Root().UpsertFromSetDefaults(nodeutil.ReflectChild(moduleCfg)); err != nil {
102 | return err
103 | }
104 | }
105 | return nil
106 | }
107 |
108 | func (self *Local) ApplyStartupConfigFile(fname string) error {
109 | cfgRdr, err := os.Open(fname)
110 | defer cfgRdr.Close()
111 | if err != nil {
112 | return err
113 | }
114 | return self.ApplyStartupConfig(cfgRdr)
115 | }
116 |
--------------------------------------------------------------------------------
/device/map.go:
--------------------------------------------------------------------------------
1 | package device
2 |
3 | // Map is used my server to host multiple devices in a single web server
4 | // at restconf=[device]/...
5 | type Map interface {
6 | Device(deviceId string) (Device, error)
7 | }
8 |
--------------------------------------------------------------------------------
/device/testdata/test.yang:
--------------------------------------------------------------------------------
1 | module test {
2 | prefix "";
3 | namespace "";
4 | revision 0;
5 |
6 | notification update {
7 | }
8 | }
--------------------------------------------------------------------------------
/device/yang_lib.go:
--------------------------------------------------------------------------------
1 | package device
2 |
3 | import (
4 | "github.com/freeconf/yang/meta"
5 | "github.com/freeconf/yang/node"
6 | "github.com/freeconf/yang/nodeutil"
7 | "github.com/freeconf/yang/val"
8 | )
9 |
10 | type ResolveModule interface {
11 | ResolveModuleHnd(hnd ModuleHnd) (*meta.Module, error)
12 | }
13 |
14 | func LoadModules(ietfYangLib *node.Browser, resolver ResolveModule) (map[string]*meta.Module, error) {
15 | mods := make(map[string]*meta.Module)
16 | n := loadModulesListNode(mods, resolver)
17 | sel, err := ietfYangLib.Root().Find("modules-state/module")
18 | if err != nil {
19 | return nil, err
20 | }
21 | if err = sel.InsertInto(n); err != nil {
22 | return nil, err
23 | }
24 | return mods, nil
25 | }
26 |
27 | func loadModulesListNode(mods map[string]*meta.Module, resolver ResolveModule) node.Node {
28 | return &nodeutil.Basic{
29 | OnNext: func(r node.ListRequest) (node.Node, []val.Value, error) {
30 | key := r.Key
31 | if r.New {
32 | hnd := ModuleHnd{Name: r.Key[0].String()}
33 | return loadModuleNode(mods, resolver, &hnd), key, nil
34 | }
35 | return nil, nil, nil
36 | },
37 | }
38 | }
39 |
40 | func loadModuleNode(mods map[string]*meta.Module, resolver ResolveModule, hnd *ModuleHnd) node.Node {
41 | return &nodeutil.Extend{
42 | Base: nodeutil.ReflectChild(hnd),
43 | OnEndEdit: func(p node.Node, r node.NodeRequest) error {
44 | if err := p.EndEdit(r); err != nil {
45 | return err
46 | }
47 | mod, err := resolver.ResolveModuleHnd(*hnd)
48 | if err != nil {
49 | return err
50 | }
51 | mods[mod.Ident()] = mod
52 | return nil
53 | },
54 | }
55 | }
56 |
57 | const (
58 | ConformanceTypeImplement = "implement"
59 | ConformanceTypeImport = "import"
60 | )
61 |
62 | type DeviationHnd struct {
63 | Name string
64 | Revision string
65 | }
66 |
67 | type ModuleHnd struct {
68 | Name string
69 | Schema string
70 | Revision string
71 | Namespace string
72 | Feature []string
73 | ConformanceType string
74 | Deviation []*DeviationHnd
75 | }
76 |
--------------------------------------------------------------------------------
/device/yang_lib_test.go:
--------------------------------------------------------------------------------
1 | package device_test
2 |
3 | import (
4 | "flag"
5 | "testing"
6 |
7 | "github.com/freeconf/restconf/device"
8 | "github.com/freeconf/restconf/testdata"
9 | "github.com/freeconf/yang/fc"
10 | "github.com/freeconf/yang/meta"
11 | "github.com/freeconf/yang/nodeutil"
12 | )
13 |
14 | var update = flag.Bool("update", false, "update golden test files")
15 |
16 | func TestYangLibNode(t *testing.T) {
17 | d, _ := testdata.BirdDevice(`{"bird":[{
18 | "name" : "robin"
19 | },{
20 | "name" : "blue jay"
21 | }]}`)
22 | moduleNameAsAddress := func(m *meta.Module) string {
23 | return m.Ident()
24 | }
25 | if err := d.Add("ietf-yang-library", device.LocalDeviceYangLibNode(moduleNameAsAddress, d)); err != nil {
26 | t.Error(err)
27 | }
28 | b, err := d.Browser("ietf-yang-library")
29 | if err != nil {
30 | t.Error(err)
31 | return
32 | }
33 | if b == nil {
34 | t.Error("no browser")
35 | return
36 | }
37 | actual, err := nodeutil.WritePrettyJSON(b.Root())
38 | if err != nil {
39 | t.Error(err)
40 | }
41 | fc.Gold(t, *update, []byte(actual), "gold/yang_lib.json")
42 | }
43 |
--------------------------------------------------------------------------------
/estream/api.go:
--------------------------------------------------------------------------------
1 | package estream
2 |
3 | import (
4 | "reflect"
5 | "time"
6 |
7 | "github.com/freeconf/yang/meta"
8 | "github.com/freeconf/yang/node"
9 | "github.com/freeconf/yang/nodeutil"
10 | "github.com/freeconf/yang/val"
11 | )
12 |
13 | //go:generate go run codegen.go
14 |
15 | type api struct{}
16 |
17 | // Manage is implementation of ietf-subscribed-notifications.yang
18 | func Manage(s *Service) node.Node {
19 | var api api
20 | timePtr := reflect.TypeOf(&time.Time{})
21 | return &nodeutil.Node{
22 | Object: s,
23 | OnRead: func(p *nodeutil.Node, m meta.Definition, t reflect.Type, v reflect.Value) (reflect.Value, error) {
24 | if t == timePtr {
25 | if v.Interface().(*time.Time).IsZero() {
26 | return node.NO_VALUE, nil
27 | }
28 | }
29 | return v, nil
30 | },
31 | OnNewNode: func(p *nodeutil.Node, m meta.Meta, o any) (node.Node, error) {
32 | switch x := o.(type) {
33 | case *Subscription:
34 | return api.subscription(p, m, x)
35 | }
36 | return p.DoNewNode(m, o)
37 | },
38 | OnChild: func(p *nodeutil.Node, r node.ChildRequest) (node.Node, error) {
39 | switch r.Meta.Ident() {
40 | case "subscriptions":
41 | return p.New(r.Meta, s.subscriptions)
42 | case "filters":
43 | return p.New(r.Meta, s.filters)
44 | case "streams":
45 | return p.New(r.Meta, s.streams)
46 | }
47 | return p.DoChild(r)
48 | },
49 | OnNotify: func(p *nodeutil.Node, r node.NotifyRequest) (node.NotifyCloser, error) {
50 | switch r.Meta.Ident() {
51 | case "subscription-suspended":
52 | return api.eventListener(s, SubEventSuspended, r), nil
53 | case "subscription-terminated":
54 | return api.eventListener(s, SubEventTerminated, r), nil
55 | case "replay-completed":
56 | return api.eventListener(s, SubEventCompleted, r), nil
57 | case "subscription-modified":
58 | return api.eventListener(s, SubEventModified, r), nil
59 | case "subscription-resumed":
60 | return api.eventListener(s, SubEventResumed, r), nil
61 | case "subscription-started":
62 | return api.eventListener(s, SubEventStarted, r), nil
63 | }
64 | return nil, nil
65 | },
66 | }
67 | }
68 |
69 | func (api api) subscription(p *nodeutil.Node, m meta.Meta, s *Subscription) (node.Node, error) {
70 | opts := s.Options()
71 | base, err := p.New(m, &opts)
72 | if err != nil {
73 | return nil, err
74 | }
75 | return &nodeutil.Extend{
76 | Base: base,
77 | OnField: func(p node.Node, r node.FieldRequest, hnd *node.ValueHandle) error {
78 | switch r.Meta.Ident() {
79 | case "subscription-id", "id":
80 | hnd.Val = val.String(s.Id)
81 | case "replay-start-time-revision":
82 | // TODO
83 | default:
84 | return p.Field(r, hnd)
85 | }
86 | return nil
87 | },
88 | }, nil
89 | }
90 |
91 | func (api api) eventListener(s *Service, etype SubEventType, r node.NotifyRequest) node.NotifyCloser {
92 | l := s.onEvent(func(e SubEvent) {
93 | if etype == e.EventId {
94 | r.Send(api.event(e))
95 | }
96 | })
97 | return l.Close
98 | }
99 |
100 | func (api api) event(e SubEvent) node.Node {
101 | return &nodeutil.Node{
102 | Object: e.Subscription,
103 | OnField: func(p *nodeutil.Node, r node.FieldRequest, hnd *node.ValueHandle) error {
104 | switch r.Meta.Ident() {
105 | case "replay-previous-event-time":
106 | // TODO
107 | case "reason":
108 | hnd.Val = val.String(e.Reason)
109 | default:
110 | return p.DoField(r, hnd)
111 | }
112 | return nil
113 | },
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/estream/api_test.go:
--------------------------------------------------------------------------------
1 | package estream
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/freeconf/restconf/testdata"
7 | "github.com/freeconf/yang/fc"
8 | "github.com/freeconf/yang/meta"
9 | "github.com/freeconf/yang/node"
10 | "github.com/freeconf/yang/nodeutil"
11 | "github.com/freeconf/yang/parser"
12 | "github.com/freeconf/yang/source"
13 | )
14 |
15 | func TestApi(t *testing.T) {
16 | ypath := source.Path("../testdata:../yang/ietf-rfc")
17 | car := testdata.New()
18 | carMod := parser.RequireModule(ypath, "car")
19 | carBwsr := node.NewBrowser(carMod, testdata.Manage(car))
20 |
21 | opts := parser.Options{
22 | Features: meta.FeaturesOn([]string{
23 | "replay", "configured", "xpath", "encode-json", "encode-xml",
24 | }),
25 | }
26 | m, err := parser.LoadModuleWithOptions(ypath, "ietf-subscribed-notifications", opts)
27 | fc.RequireEqual(t, nil, err)
28 | s := NewService()
29 | events := make(chan SubEvent, 10)
30 | s.onEvent(func(e SubEvent) {
31 | events <- e
32 | })
33 | b := node.NewBrowser(m, Manage(s))
34 | root := b.Root()
35 | s.AddFilter(Filter{
36 | Name: "my-filter",
37 | Filter: func(s *node.Selection) *node.Selection {
38 | return s
39 | },
40 | })
41 | s.AddStream(Stream{
42 | Name: "my-stream",
43 | Open: func() (*node.Selection, error) {
44 | return carBwsr.Root().Find("update")
45 | },
46 | })
47 | req := `{
48 | "stream-filter-name" : "my-filter",
49 | "stream" : "my-stream"
50 | }`
51 | rpc := sel(root.Find("establish-subscription"))
52 | out, err := rpc.Action(readJson(req))
53 | fc.AssertEqual(t, nil, err)
54 | actual, err := nodeutil.WriteJSON(out)
55 | fc.AssertEqual(t, nil, err)
56 | fc.AssertEqual(t, `{"id":"100"}`, actual)
57 | fc.AssertEqual(t, SubEventStarted, (<-events).EventId)
58 |
59 | _, err = nodeutil.WritePrettyJSON(root)
60 | fc.AssertEqual(t, nil, err)
61 |
62 | badFilter := `{
63 | "stream-filter-name" : "nope",
64 | "stream" : "my-stream"
65 | }`
66 | _, err = rpc.Action(readJson(badFilter))
67 | fc.AssertEqual(t, true, err != nil)
68 |
69 | badStream := `{
70 | "stream-filter-name" : "my-filter",
71 | "stream" : "nope"
72 | }`
73 | _, err = rpc.Action(readJson(badStream))
74 | fc.AssertEqual(t, true, err != nil)
75 | }
76 |
77 | func readJson(s string) node.Node {
78 | n, err := nodeutil.ReadJSON(s)
79 | if err != nil {
80 | panic(err)
81 | }
82 | return n
83 | }
84 |
85 | func sel(s *node.Selection, err error) *node.Selection {
86 | if err != nil {
87 | panic(err)
88 | }
89 | return s
90 | }
91 |
--------------------------------------------------------------------------------
/estream/codegen.go:
--------------------------------------------------------------------------------
1 | //go:build ignore
2 | // +build ignore
3 |
4 | package main
5 |
6 | import (
7 | "os"
8 | "text/template"
9 |
10 | "github.com/freeconf/yang/meta"
11 | "github.com/freeconf/yang/parser"
12 | "github.com/freeconf/yang/source"
13 | )
14 |
15 | var src = `
16 | package estream
17 |
18 | const Working = 1
19 |
20 | `
21 |
22 | // parse proto buf files into Go objects and then call templates to take
23 | // Go objects and generate code based on the data defined in the proto file(s)
24 | func main() {
25 | ypath := source.Dir("../yang/ietf-rfc")
26 | m, err := parser.LoadModule(ypath, "ietf-subscribed-notifications")
27 | chkerr(err)
28 | t, err := template.New("test").Parse(src)
29 | chkerr(err)
30 | vars := struct {
31 | M *meta.Module
32 | }{
33 | M: m,
34 | }
35 | t.Execute(os.Stdout, vars)
36 | }
37 |
38 | func chkerr(err error) {
39 | if err != nil {
40 | panic(err)
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/estream/doc.go:
--------------------------------------------------------------------------------
1 | package estream
2 |
3 | // Event Streams. Implements basic of NETCONF Notifications RFC5277 and also the backend
4 | // for YANG Push Notifications RFC8639.
5 | //
6 | // Both RFCs use "named" event stream where names are assigned on
7 | // the server side (publisher) and correlate to YANG notification paths OR reserved
8 | // named streams like "NETCONF".
9 | //
10 |
--------------------------------------------------------------------------------
/estream/filter.go:
--------------------------------------------------------------------------------
1 | package estream
2 |
3 | import "github.com/freeconf/yang/node"
4 |
5 | type Filter struct {
6 | Name string
7 | Filter func(*node.Selection) *node.Selection
8 | }
9 |
10 | func (f Filter) Empty() bool {
11 | return f.Name == "" && f.Filter == nil
12 | }
13 |
--------------------------------------------------------------------------------
/estream/recv.go:
--------------------------------------------------------------------------------
1 | package estream
2 |
3 | import (
4 | "errors"
5 | "time"
6 |
7 | "github.com/freeconf/yang/node"
8 | )
9 |
10 | type RecvState int
11 |
12 | const (
13 | RecvStateDisconnected = iota
14 | RecvStateActive
15 | RecvStateSuspended
16 | RecvStateConnecting
17 | )
18 |
19 | type Receiver func(e ReceiverEvent) error
20 |
21 | type receiverSubscription interface {
22 | activateReceiver(r *receiverEntry, active bool, reason string)
23 | }
24 |
25 | var ErrBufferOverflow = errors.New("event buffer full")
26 |
27 | type ReceiverEvent struct {
28 | Name string
29 | EventTime time.Time
30 | Event *node.Selection
31 | }
32 |
33 | type receiverEntry struct {
34 | Name string
35 | sub receiverSubscription
36 | State RecvState
37 | ExcludedEventRecords int64
38 | SentEventRecords int64
39 | receiver Receiver
40 | }
41 |
42 | func (r *receiverEntry) Reset() {
43 | // TODO: not sure spec says to do this
44 | r.ExcludedEventRecords = 0
45 | r.SentEventRecords = 0
46 |
47 | r.sub.activateReceiver(r, true, "")
48 | }
49 |
--------------------------------------------------------------------------------
/estream/service.go:
--------------------------------------------------------------------------------
1 | package estream
2 |
3 | import (
4 | "container/list"
5 | "fmt"
6 | "strconv"
7 | "time"
8 |
9 | "github.com/freeconf/yang/fc"
10 | "github.com/freeconf/yang/nodeutil"
11 | )
12 |
13 | type Service struct {
14 | subscriptions map[string]*Subscription
15 | filters map[string]Filter
16 | streams map[string]Stream
17 | listeners *list.List
18 | subcriptionCounter int64
19 | }
20 |
21 | func NewService() *Service {
22 | return &Service{
23 | subscriptions: make(map[string]*Subscription),
24 | filters: make(map[string]Filter),
25 | streams: make(map[string]Stream),
26 | listeners: list.New(),
27 | subcriptionCounter: 100, // starting at zero or one seems disconcerting
28 | }
29 | }
30 |
31 | func (s *Service) AddFilter(f Filter) {
32 | s.filters[f.Name] = f
33 | }
34 |
35 | func (s *Service) AddStream(stream Stream) {
36 | s.streams[stream.Name] = stream
37 | }
38 |
39 | type eventListener func(e SubEvent)
40 |
41 | func (s *Service) onEvent(l eventListener) nodeutil.Subscription {
42 | return nodeutil.NewSubscription(s.listeners, s.listeners.PushBack(l))
43 | }
44 |
45 | type EstablishRequest struct {
46 | Stream string
47 | StreamFilterName string
48 | ReplayStartTime time.Time
49 | StopTime time.Time
50 | }
51 |
52 | func (s *Service) EstablishSubscription(req EstablishRequest) (*Subscription, error) {
53 | sub := NewSubscription(s.nextSubId(), s)
54 | var opts SubscriptionOptions
55 | if err := s.updateFilter(&opts, req.StreamFilterName); err != nil {
56 | return nil, err
57 | }
58 | if err := s.updateStream(&opts, req.Stream); err != nil {
59 | return nil, err
60 | }
61 | if err := sub.Apply(opts); err != nil {
62 | return nil, err
63 | }
64 | s.subscriptions[sub.Id] = sub
65 | s.updateListeners(SubEvent{Subscription: sub, EventId: SubEventStarted})
66 | return sub, nil
67 | }
68 |
69 | func (s *Service) updateFilter(opts *SubscriptionOptions, filterName string) error {
70 | if filterName == "" {
71 | opts.Filter = Filter{}
72 | } else {
73 | f, found := s.filters[filterName]
74 | if !found {
75 | return fmt.Errorf("filter %w %s", fc.NotFoundError, filterName)
76 | }
77 | opts.Filter = f
78 | }
79 | return nil
80 | }
81 |
82 | func (s *Service) updateStream(opts *SubscriptionOptions, streamName string) error {
83 | if streamName == "" {
84 | opts.Stream = Stream{}
85 | } else {
86 | s, found := s.streams[streamName]
87 | if !found {
88 | return fmt.Errorf("stream %w %s", fc.NotFoundError, streamName)
89 | }
90 | opts.Stream = s
91 | }
92 | return nil
93 | }
94 |
95 | type ModifyRequest struct {
96 | SubscriptionId string
97 | StreamFilterName string
98 | StopTime time.Time
99 | }
100 |
101 | func (s *Service) ModifySubscription(req ModifyRequest) error {
102 | sub, found := s.subscriptions[req.SubscriptionId]
103 | if !found {
104 | return fmt.Errorf("subscription %w %s", fc.NotFoundError, req.SubscriptionId)
105 | }
106 | opts := sub.Options()
107 | opts.StopTime = req.StopTime
108 | if err := s.updateFilter(&opts, req.StreamFilterName); err != nil {
109 | return err
110 | }
111 | if err := sub.Apply(opts); err != nil {
112 | return err
113 | }
114 | s.updateListeners(SubEvent{Subscription: sub, EventId: SubEventModified})
115 | return nil
116 | }
117 |
118 | func (s *Service) updateListeners(e SubEvent) {
119 | fc.Debug.Printf("updateListeners %v", e)
120 | for l := s.listeners.Front(); l != nil; l = l.Next() {
121 | l.Value.(eventListener)(e)
122 | }
123 | }
124 |
125 | func (s *Service) KillSubscription(subId string) error {
126 | return nil
127 | }
128 |
129 | func (s *Service) DeleteSubsccription(subId string) error {
130 | return nil
131 | }
132 |
133 | func (s *Service) nextSubId() string {
134 | var id int64
135 | s.subcriptionCounter, id = s.subcriptionCounter+1, s.subcriptionCounter
136 | return strconv.FormatInt(id, 10)
137 | }
138 |
--------------------------------------------------------------------------------
/estream/stream.go:
--------------------------------------------------------------------------------
1 | package estream
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/freeconf/yang/node"
7 | )
8 |
9 | type Stream struct {
10 | Name string
11 | Description string
12 | ReplaySupport bool
13 | ReplayLogCreationTime time.Time
14 | ReplayLogAgedTime time.Time
15 | Open func() (*node.Selection, error)
16 | }
17 |
--------------------------------------------------------------------------------
/estream/sub.go:
--------------------------------------------------------------------------------
1 | package estream
2 |
3 | import (
4 | "errors"
5 | "time"
6 |
7 | "github.com/freeconf/yang/node"
8 | )
9 |
10 | type SubEventType int
11 |
12 | const (
13 | SubEventSuspended SubEventType = iota
14 | SubEventTerminated
15 | SubEventCompleted
16 | SubEventModified
17 | SubEventResumed
18 | SubEventStarted
19 | )
20 |
21 | type SubEvent struct {
22 | EventId SubEventType
23 | Subscription *Subscription
24 | Reason string
25 | }
26 |
27 | type SubState int
28 |
29 | const (
30 | SubStateValid = iota
31 | SubStateInvalid
32 | SubStateConcluded
33 | )
34 |
35 | type SubscriptionOptions struct {
36 | Filter Filter
37 | Stream Stream
38 | ReplayStartTime time.Time
39 | // configured-replay
40 | ReplayStartTimeRevision time.Time
41 | StopTime time.Time
42 | // transport
43 | // encoding
44 | Purpose string
45 | SourceAddress string
46 | }
47 |
48 | type subService interface {
49 | updateListeners(e SubEvent)
50 | }
51 |
52 | type Subscription struct {
53 | Id string
54 | closer node.NotifyCloser
55 | opts SubscriptionOptions
56 | service subService
57 |
58 | ConfiguredSubscriptionState SubState
59 | Recievers map[string]*receiverEntry
60 | }
61 |
62 | func NewSubscription(id string, service subService) *Subscription {
63 | return &Subscription{
64 | Id: id,
65 | service: service,
66 | Recievers: make(map[string]*receiverEntry),
67 | }
68 | }
69 |
70 | func (s *Subscription) AddReceiver(name string, receiver Receiver) error {
71 | if _, exists := s.Recievers[name]; exists {
72 | return errors.New("receiver already exists")
73 | }
74 | s.Recievers[name] = &receiverEntry{
75 | sub: s,
76 | Name: name,
77 | receiver: receiver,
78 | State: RecvStateActive,
79 | }
80 | return nil
81 | }
82 |
83 | func (s *Subscription) RemoveReceiver(name string) error {
84 | delete(s.Recievers, name)
85 | return nil
86 | }
87 |
88 | func (s *Subscription) Options() SubscriptionOptions {
89 | return s.opts
90 | }
91 |
92 | func (s *Subscription) activateReceiver(r *receiverEntry, active bool, reason string) {
93 | e := SubEvent{
94 | EventId: SubEventStarted,
95 | Subscription: s,
96 | }
97 | if active {
98 | r.State = RecvStateActive
99 | e.EventId = SubEventResumed
100 | } else {
101 | r.State = RecvStateSuspended
102 | e.EventId = SubEventSuspended
103 | e.Reason = reason
104 | }
105 | s.service.updateListeners(e)
106 | }
107 |
108 | func (s *Subscription) Apply(opts SubscriptionOptions) error {
109 | if s.closer != nil {
110 | s.closer()
111 | }
112 | notifySel, err := opts.Stream.Open()
113 | if err != nil {
114 | return err
115 | }
116 | s.opts = opts
117 | s.closer, err = notifySel.Notifications(func(n node.Notification) {
118 | eventSel := n.Event
119 | // TODO: Compare event time to stop time and filter accordingly
120 | if !s.opts.Filter.Empty() {
121 | eventSel = s.opts.Filter.Filter(eventSel)
122 | }
123 | for _, r := range s.Recievers {
124 | if eventSel != nil && r.State == RecvStateActive {
125 | err = r.receiver(ReceiverEvent{
126 | Name: r.Name,
127 | EventTime: n.EventTime,
128 | Event: eventSel,
129 | })
130 | if err == nil {
131 | r.SentEventRecords++
132 | continue
133 | } else {
134 | s.activateReceiver(r, false, err.Error())
135 | }
136 | }
137 | r.ExcludedEventRecords++
138 |
139 | }
140 | })
141 | return err
142 | }
143 |
--------------------------------------------------------------------------------
/estream/sub_test.go:
--------------------------------------------------------------------------------
1 | package estream
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/freeconf/yang/fc"
7 | "github.com/freeconf/yang/node"
8 | "github.com/freeconf/yang/nodeutil"
9 | "github.com/freeconf/yang/parser"
10 | )
11 |
12 | var mstr = `
13 | module x {
14 | notification msgs {
15 | leaf msg {
16 | type string;
17 | }
18 | }
19 | }
20 | `
21 |
22 | func TestSubReceiver(t *testing.T) {
23 | type msg struct {
24 | Msg string
25 | }
26 | msgs := make(chan msg)
27 | n := &nodeutil.Basic{
28 | OnNotify: func(r node.NotifyRequest) (node.NotifyCloser, error) {
29 | closer := func() error {
30 | close(msgs)
31 | return nil
32 | }
33 | go func() {
34 | for m := range msgs {
35 | r.Send(&nodeutil.Node{Object: &m})
36 | }
37 | }()
38 | return closer, nil
39 | },
40 | }
41 | m, err := parser.LoadModuleFromString(nil, mstr)
42 | fc.RequireEqual(t, nil, err)
43 | b := node.NewBrowser(m, n)
44 |
45 | s := NewSubscription("X", nil)
46 | opts := SubscriptionOptions{
47 | Stream: Stream{
48 | Name: "foo",
49 | Open: func() (*node.Selection, error) {
50 | return b.Root().Find("msgs")
51 | },
52 | },
53 | }
54 | err = s.Apply(opts)
55 | fc.RequireEqual(t, nil, err)
56 | recvr := make(chan ReceiverEvent)
57 | err = s.AddReceiver("foo", func(e ReceiverEvent) error {
58 | recvr <- e
59 | return nil
60 | })
61 | fc.RequireEqual(t, nil, err)
62 | msgs <- msg{Msg: "hello"}
63 | actual, err := nodeutil.WriteJSON((<-recvr).Event)
64 | fc.AssertEqual(t, nil, err)
65 | fc.AssertEqual(t, `{"msg":"hello"}`, actual)
66 | s.RemoveReceiver("foo")
67 | }
68 |
--------------------------------------------------------------------------------
/form.go:
--------------------------------------------------------------------------------
1 | package restconf
2 |
3 | import (
4 | "errors"
5 | "io"
6 | "mime/multipart"
7 | "net/http"
8 | "strings"
9 |
10 | "github.com/freeconf/yang/meta"
11 | "github.com/freeconf/yang/node"
12 | "github.com/freeconf/yang/nodeutil"
13 | "github.com/freeconf/yang/val"
14 | )
15 |
16 | func isMultiPartForm(hdrs http.Header) bool {
17 | return strings.HasPrefix(hdrs.Get("Content-Type"), "multipart/form-data")
18 | }
19 |
20 | // AnyDataReader is field value for anydata types that are io.Reader, but receiver might
21 | // also want the name submitted with reader. Think file upload or plain old os.File as
22 | // underlying type
23 | type AnyDataReader interface {
24 | io.Reader
25 | Name() string
26 | }
27 |
28 | type formFile struct {
29 | rdr io.Reader
30 | name string
31 | }
32 |
33 | func (ff formFile) Read(p []byte) (n int, err error) {
34 | return ff.rdr.Read(p)
35 | }
36 |
37 | func (ff formFile) Name() string {
38 | return ff.name
39 | }
40 |
41 | func formNode(req *http.Request) (node.Node, error) {
42 | err := req.ParseMultipartForm(10000)
43 | if err != nil {
44 | return nil, err
45 | }
46 | return &nodeutil.Basic{
47 | OnChild: func(r node.ChildRequest) (node.Node, error) {
48 | entry, found := req.MultipartForm.File[r.Meta.Ident()]
49 | if !found || len(entry) == 0 {
50 | return nil, nil
51 | }
52 | if meta.IsList(r.Meta) {
53 | return formListNode(entry), nil
54 | }
55 | if len(entry) != 1 {
56 | return nil, errors.New("invalid number of form files for structure, expected 0 or 1")
57 | }
58 |
59 | return nil, nil
60 | },
61 | OnField: func(r node.FieldRequest, hnd *node.ValueHandle) error {
62 | sval := req.FormValue(r.Meta.Ident())
63 | if sval != "" {
64 | var err error
65 | hnd.Val, err = node.NewValue(r.Meta.Type(), sval)
66 | return err
67 | }
68 | entry, found := req.MultipartForm.File[r.Meta.Ident()]
69 | if found {
70 | if len(entry) == 0 {
71 | return nil
72 | }
73 |
74 | // Can type any be a leaf-list? spec says yes
75 | // if r.Meta.Type().Format().IsList()
76 |
77 | if len(entry) != 1 {
78 | return errors.New("invalid number of form files for field, expected 0 or 1")
79 | }
80 | f, err := entry[0].Open()
81 | if err != nil {
82 | return err
83 | }
84 | // wrapping will make uploaded file name available to implementor
85 | // should they be interested
86 | wrap := formFile{
87 | name: entry[0].Filename,
88 | rdr: f,
89 | }
90 | hnd.Val = val.Any{Thing: wrap}
91 | return nil
92 | }
93 |
94 | return nil
95 | },
96 | }, nil
97 | }
98 |
99 | func formChildNode(f *multipart.FileHeader) (node.Node, error) {
100 | rdr, err := f.Open()
101 | if err != nil {
102 | return nil, err
103 | }
104 | defer rdr.Close()
105 | return nodeutil.ReadJSONIO(rdr)
106 | }
107 |
108 | func formListNode(files []*multipart.FileHeader) node.Node {
109 | return &nodeutil.Basic{
110 | OnNext: func(r node.ListRequest) (node.Node, []val.Value, error) {
111 | if r.Row >= len(files) {
112 | return nil, nil, nil
113 | }
114 | n, err := formChildNode(files[r.Row])
115 | return n, nil, err
116 | },
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/form_test.go:
--------------------------------------------------------------------------------
1 | package restconf
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "fmt"
7 | "io"
8 | "io/ioutil"
9 | "mime/multipart"
10 | "net/http"
11 | "os"
12 | "testing"
13 | "time"
14 |
15 | "github.com/freeconf/yang/node"
16 | "github.com/freeconf/yang/nodeutil"
17 | "github.com/freeconf/yang/parser"
18 | )
19 |
20 | type handlerImpl http.HandlerFunc
21 |
22 | func (impl handlerImpl) ServeHTTP(w http.ResponseWriter, r *http.Request) {
23 | impl(w, r)
24 | }
25 |
26 | func TestForm(t *testing.T) {
27 | if os.Getenv("TRAVIS") == "true" {
28 | // no web servers allowed in CI
29 | t.Skip()
30 | return
31 | }
32 |
33 | m, err := parser.LoadModuleFromString(nil, `
34 | module test {
35 | rpc x {
36 | input {
37 | leaf a {
38 | type string;
39 | }
40 | anydata b;
41 | }
42 | }
43 | }
44 | `)
45 | if err != nil {
46 | t.Fatal(err)
47 | }
48 | done := make(chan bool, 1)
49 | handler := func(w http.ResponseWriter, r *http.Request) {
50 | b := node.NewBrowser(m, formDummyNode(t))
51 | x := m.Actions()["x"]
52 | input, err := readInput(Strict, YangDataJsonMimeType1, r, x)
53 | chkErr(t, err)
54 | xsel, err := b.Root().Find("x")
55 | chkErr(t, err)
56 | _, err = xsel.Action(input)
57 | chkErr(t, err)
58 | w.Write([]byte("ok"))
59 | t.Log("form received")
60 | done <- true
61 | }
62 | srv := &http.Server{Addr: "127.0.0.1:9999", Handler: handlerImpl(handler)}
63 | go srv.ListenAndServe()
64 | defer srv.Shutdown(context.TODO())
65 | // wait for server to start
66 | <-time.After(10 * time.Millisecond)
67 |
68 | var buf bytes.Buffer
69 | form := multipart.NewWriter(&buf)
70 | dataPart, err := form.CreateFormField("a")
71 | chkErr(t, err)
72 | fmt.Fprint(dataPart, "hello")
73 | filePart, err := form.CreateFormFile("b", "b")
74 | chkErr(t, err)
75 | fmt.Fprint(filePart, "hello world")
76 | chkErr(t, form.Close())
77 | req, err := http.NewRequest("POST", "http://"+srv.Addr, &buf)
78 | chkErr(t, err)
79 | req.Header.Set("Content-Type", form.FormDataContentType())
80 | _, err = http.DefaultClient.Do(req)
81 | // If you get an error here, make sure something else isn't running on same port
82 | chkErr(t, err)
83 | <-done
84 | }
85 |
86 | func chkErr(t *testing.T, err error) {
87 | t.Helper()
88 | if err != nil {
89 | t.Fatal(err)
90 | }
91 | }
92 |
93 | func formDummyNode(t *testing.T) node.Node {
94 | return &nodeutil.Basic{
95 | OnAction: func(r node.ActionRequest) (node.Node, error) {
96 | sel, err := r.Input.Find("a")
97 | chkErr(t, err)
98 | v, err := sel.Get()
99 | chkErr(t, err)
100 | if v.String() != "hello" {
101 | t.Error(v.String())
102 | }
103 |
104 | sel, err = r.Input.Find("b")
105 | chkErr(t, err)
106 | v, err = sel.Get()
107 | chkErr(t, err)
108 | rdr, valid := v.Value().(io.Reader)
109 | if !valid {
110 | panic("invalid")
111 | }
112 | actual, err := ioutil.ReadAll(rdr)
113 | chkErr(t, err)
114 | if string(actual) != "hello world" {
115 | t.Error(actual)
116 | }
117 | //defer rdr.Close()
118 | fmt.Print(string(actual))
119 | return nil, nil
120 | },
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/freeconf/restconf
2 |
3 | go 1.20
4 |
5 | require github.com/freeconf/yang v0.0.0-20240126135339-ef92ddeb9f99
6 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/freeconf/yang v0.0.0-20230820141610-ae98639510d8 h1:rmTtIlQQGN4HwcMbzaWgoeFlScagn4eunheWZXWwnLg=
2 | github.com/freeconf/yang v0.0.0-20230820141610-ae98639510d8/go.mod h1:mWEJ47bQKL2+1uMaHsA6VjRtoO+svJ2LcIiN+StJqNY=
3 | github.com/freeconf/yang v0.0.0-20230821105535-d155c2806a88 h1:Ke/hC3iThdqQ+nc/URe8W+VU/e9COKMQyPOta+9DaqA=
4 | github.com/freeconf/yang v0.0.0-20230821105535-d155c2806a88/go.mod h1:mWEJ47bQKL2+1uMaHsA6VjRtoO+svJ2LcIiN+StJqNY=
5 | github.com/freeconf/yang v0.0.0-20230825115246-c5cd81534e2c h1:NY+XI5xxlHRbX5KaSca4JXL6CeMZwQJpk7nYFcVn7/Q=
6 | github.com/freeconf/yang v0.0.0-20230825115246-c5cd81534e2c/go.mod h1:mWEJ47bQKL2+1uMaHsA6VjRtoO+svJ2LcIiN+StJqNY=
7 | github.com/freeconf/yang v0.0.0-20230831100447-20decdb7a190 h1:LGYpXeh6AE1TMuGusfVuJ7zDaRL6U5cgZ1gj00m/ZqI=
8 | github.com/freeconf/yang v0.0.0-20230831100447-20decdb7a190/go.mod h1:mWEJ47bQKL2+1uMaHsA6VjRtoO+svJ2LcIiN+StJqNY=
9 | github.com/freeconf/yang v0.0.0-20231107135057-5f971eab6dd7 h1:IknGHRp4yLV76AaYoLqmQN0GNsiYdF1qvVvly0IGbHs=
10 | github.com/freeconf/yang v0.0.0-20231107135057-5f971eab6dd7/go.mod h1:mWEJ47bQKL2+1uMaHsA6VjRtoO+svJ2LcIiN+StJqNY=
11 | github.com/freeconf/yang v0.0.0-20231119210536-9dc8fe344ebf h1:urgzRsimROVY/EQTlUcH/O/jCEShKKAlOGyu9LjL/r4=
12 | github.com/freeconf/yang v0.0.0-20231119210536-9dc8fe344ebf/go.mod h1:xg9dIHu/2tw/pdxf2xscOXCZTfAwjdbG5S7sg5VqDd8=
13 | github.com/freeconf/yang v0.0.0-20231123103235-3be8d985c9cb h1:/CFxjh7MGAgo0PMOFmdznLvcCbkj1VimX7pu9sZ6+0U=
14 | github.com/freeconf/yang v0.0.0-20231123103235-3be8d985c9cb/go.mod h1:xg9dIHu/2tw/pdxf2xscOXCZTfAwjdbG5S7sg5VqDd8=
15 | github.com/freeconf/yang v0.0.0-20231127125511-7277a0718e49 h1:vS0/BB3FsF6HkXQTalZ4+bN2vWpqmwLO9CgDqnmhxX8=
16 | github.com/freeconf/yang v0.0.0-20231127125511-7277a0718e49/go.mod h1:mWEJ47bQKL2+1uMaHsA6VjRtoO+svJ2LcIiN+StJqNY=
17 | github.com/freeconf/yang v0.0.0-20231206120455-129436190487 h1:nIcof7cZqFJhXm767iSLVrpBlDVDf2G5C/pplVGymjI=
18 | github.com/freeconf/yang v0.0.0-20231206120455-129436190487/go.mod h1:mWEJ47bQKL2+1uMaHsA6VjRtoO+svJ2LcIiN+StJqNY=
19 | github.com/freeconf/yang v0.0.0-20240113214104-fe20e073cbc5 h1:rq6J03f/b5YCdS8ZwD6S/IZqjyzpxRksbSk2aOBzzPo=
20 | github.com/freeconf/yang v0.0.0-20240113214104-fe20e073cbc5/go.mod h1:mWEJ47bQKL2+1uMaHsA6VjRtoO+svJ2LcIiN+StJqNY=
21 | github.com/freeconf/yang v0.0.0-20240126135339-ef92ddeb9f99 h1:CzgpQ/Y6Lqpsx8oDLGSrSp4f4WggqBLkUq4IOrGrLPk=
22 | github.com/freeconf/yang v0.0.0-20240126135339-ef92ddeb9f99/go.mod h1:mWEJ47bQKL2+1uMaHsA6VjRtoO+svJ2LcIiN+StJqNY=
23 | github.com/freeconf/yang v0.1.0-alpha h1:rvKmBgcTjZM2Fyt5g7xe/yL+Og9fQaBe489o/QRpJR8=
24 | github.com/freeconf/yang v0.1.0-alpha/go.mod h1:mWEJ47bQKL2+1uMaHsA6VjRtoO+svJ2LcIiN+StJqNY=
25 |
--------------------------------------------------------------------------------
/node.go:
--------------------------------------------------------------------------------
1 | package restconf
2 |
3 | import (
4 | "github.com/freeconf/restconf/stock"
5 | "github.com/freeconf/yang/fc"
6 | "github.com/freeconf/yang/node"
7 | "github.com/freeconf/yang/nodeutil"
8 | "github.com/freeconf/yang/source"
9 | "github.com/freeconf/yang/val"
10 | )
11 |
12 | func Node(mgmt *Server, ypath source.Opener) node.Node {
13 | return &nodeutil.Extend{
14 | Base: nodeutil.ReflectChild(mgmt),
15 | OnChild: func(p node.Node, r node.ChildRequest) (node.Node, error) {
16 | switch r.Meta.Ident() {
17 | case "web":
18 | if r.New {
19 | mgmt.Web = stock.NewHttpServer(mgmt)
20 | }
21 | if mgmt.Web != nil {
22 | return stock.WebServerNode(mgmt.Web), nil
23 | }
24 | default:
25 | return p.Child(r)
26 | }
27 | return nil, nil
28 | },
29 | OnField: func(p node.Node, r node.FieldRequest, hnd *node.ValueHandle) error {
30 | switch r.Meta.Ident() {
31 | case "debug":
32 | if r.Write {
33 | fc.DebugLog(hnd.Val.Value().(bool))
34 | } else {
35 | hnd.Val = val.Bool(fc.DebugLogEnabled())
36 | }
37 | case "streamCount":
38 | hnd.Val = val.Int32(mgmt.notifiers.Len())
39 | case "subscriptionCount":
40 | hnd.Val = val.Int32(subscribeCount)
41 | default:
42 | return p.Field(r, hnd)
43 | }
44 | return nil
45 | },
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/secure/authenticate.go:
--------------------------------------------------------------------------------
1 | package secure
2 |
3 | import (
4 | "crypto/x509"
5 | "fmt"
6 |
7 | "github.com/freeconf/restconf/stock"
8 | )
9 |
10 | type CertHandler struct {
11 | Authority *stock.Tls
12 | }
13 |
14 | func (self *CertHandler) VerifyRequest(certs []*x509.Certificate) error {
15 | // certs := r.TLS.PeerCertificates
16 | opts := x509.VerifyOptions{
17 | Roots: self.Authority.Config.RootCAs,
18 | }
19 | var err error
20 | var valid *x509.Certificate
21 | for _, cert := range certs {
22 | if _, invalid := cert.Verify(opts); invalid != nil {
23 | err = invalid
24 | } else {
25 | valid = cert
26 | }
27 | }
28 |
29 | if err != nil {
30 | return err
31 | }
32 | fmt.Printf("Valid! %v", valid)
33 |
34 | return nil
35 | }
36 |
--------------------------------------------------------------------------------
/secure/authenticate_test.go:
--------------------------------------------------------------------------------
1 | package secure
2 |
3 | import "testing"
4 |
5 | func TestAuthenticate(t *testing.T) {
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/secure/authorize.go:
--------------------------------------------------------------------------------
1 | package secure
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/freeconf/yang/fc"
7 | "github.com/freeconf/yang/meta"
8 | "github.com/freeconf/yang/node"
9 | )
10 |
11 | var noAccess = &Role{}
12 |
13 | type Role struct {
14 | Id string
15 | Access map[string]*AccessControl
16 | }
17 |
18 | func NewRole() *Role {
19 | return &Role{
20 | Access: make(map[string]*AccessControl),
21 | }
22 | }
23 |
24 | type AccessControl struct {
25 | Path string
26 | Permissions Permission
27 | }
28 |
29 | type Permission int
30 |
31 | const (
32 | None Permission = iota
33 | Read
34 | Full
35 | )
36 |
37 | func (role *Role) CheckListPreConstraints(r *node.ListRequest) (bool, error) {
38 | requested := Read
39 | if r.New {
40 | requested = Full
41 | }
42 | return role.check(r.Meta, r.Selection.Context, requested)
43 | }
44 |
45 | func (role *Role) CheckContainerPreConstraints(r *node.ChildRequest) (bool, error) {
46 | requested := Read
47 | if r.New {
48 | requested = Full
49 | }
50 | return role.check(r.Meta, r.Selection.Context, requested)
51 | }
52 |
53 | func (role *Role) CheckFieldPreConstraints(r *node.FieldRequest, hnd *node.ValueHandle) (bool, error) {
54 | requested := Read
55 | if r.Write {
56 | requested = Full
57 | }
58 | return role.check(r.Meta, r.Selection.Context, requested)
59 | }
60 |
61 | func (role *Role) CheckNotifyFilterConstraints(msg *node.Selection) (bool, error) {
62 | return role.check(msg.Meta(), msg.Context, Full)
63 | }
64 |
65 | type contextKey int
66 |
67 | var permKey contextKey = 0
68 |
69 | func (role *Role) CheckActionPreConstraints(r *node.ActionRequest) (bool, error) {
70 | return role.check(r.Meta, r.Selection.Context, Full)
71 | }
72 |
73 | func (role *Role) ContextConstraint(s *node.Selection) context.Context {
74 | if acl, found := role.Access[meta.SchemaPath(s.Meta())]; found {
75 | return context.WithValue(s.Context, permKey, acl.Permissions)
76 | }
77 | return s.Context
78 | }
79 |
80 | func (role *Role) check(m meta.Meta, c context.Context, requested Permission) (bool, error) {
81 | allowed := None
82 | path := meta.SchemaPath(m)
83 | if acl, found := role.Access[path]; found {
84 | allowed = acl.Permissions
85 | } else if x := c.Value(permKey); x != nil {
86 | allowed = x.(Permission)
87 | }
88 | if requested == Read {
89 | return allowed >= Read, nil
90 | }
91 | if allowed >= requested {
92 | return true, nil
93 | }
94 | return false, fc.UnauthorizedError
95 | }
96 |
--------------------------------------------------------------------------------
/secure/authorize_test.go:
--------------------------------------------------------------------------------
1 | package secure
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "strings"
7 | "testing"
8 |
9 | "github.com/freeconf/yang/val"
10 |
11 | "github.com/freeconf/yang/fc"
12 | "github.com/freeconf/yang/node"
13 | "github.com/freeconf/yang/nodeutil"
14 | "github.com/freeconf/yang/parser"
15 | )
16 |
17 | type testAc struct {
18 | path string
19 | perm Permission
20 | }
21 |
22 | const (
23 | xAllowed string = "allowed"
24 | xHidden = "hidden"
25 | xUnauth = "unauthorized"
26 | )
27 |
28 | func TestAuthConstraints(t *testing.T) {
29 | fc.DebugLog(true)
30 | m, err := parser.LoadModuleFromString(nil, `module birding { revision 0;
31 | leaf count {
32 | type int32;
33 | }
34 | container owner {
35 | leaf name {
36 | type string;
37 | }
38 | }
39 | action fieldtrip {
40 | input {}
41 | }
42 | notification identified {}
43 | }`)
44 | if err != nil {
45 | t.Fatal(err)
46 | }
47 | dataStr := `{
48 | "count" : 10,
49 | "owner": {"name":"ethel"}
50 | }`
51 | var data map[string]interface{}
52 | if err := json.NewDecoder(strings.NewReader(dataStr)).Decode(&data); err != nil {
53 | panic(err)
54 | }
55 | n := &nodeutil.Extend{
56 | Base: nodeutil.ReflectChild(data),
57 | OnNotify: func(p node.Node, r node.NotifyRequest) (node.NotifyCloser, error) {
58 | r.Send(&nodeutil.Basic{})
59 | closer := func() error { return nil }
60 | return closer, nil
61 | },
62 | OnAction: func(p node.Node, r node.ActionRequest) (node.Node, error) {
63 | return nil, nil
64 | },
65 | }
66 | b := node.NewBrowser(m, n)
67 | tests := []struct {
68 | desc string
69 | acls []*AccessControl
70 | read string
71 | readPath string
72 | write string
73 | writePath string
74 | notify string
75 | action string
76 | }{
77 | {
78 | desc: "default",
79 | acls: []*AccessControl{
80 | /* empty */
81 | },
82 | read: xHidden,
83 | readPath: xHidden,
84 | write: xUnauth,
85 | writePath: xHidden,
86 | notify: xUnauth,
87 | action: xUnauth,
88 | },
89 | {
90 | desc: "none",
91 | acls: []*AccessControl{
92 | {
93 | Path: "birding",
94 | Permissions: Read,
95 | },
96 | },
97 | read: xAllowed,
98 | readPath: xAllowed,
99 | write: xUnauth,
100 | writePath: xUnauth,
101 | notify: xUnauth,
102 | action: xUnauth,
103 | },
104 | {
105 | desc: "full",
106 | acls: []*AccessControl{
107 | {
108 | Path: "birding",
109 | Permissions: Full,
110 | },
111 | },
112 | read: xAllowed,
113 | readPath: xAllowed,
114 | write: xAllowed,
115 | writePath: xAllowed,
116 | notify: xAllowed,
117 | action: xAllowed,
118 | },
119 | {
120 | desc: "mixed",
121 | acls: []*AccessControl{
122 | {
123 | Path: "birding",
124 | Permissions: Full,
125 | },
126 | {
127 | Path: "birding/owner",
128 | Permissions: None,
129 | },
130 | },
131 | read: xAllowed,
132 | readPath: xHidden,
133 | write: xAllowed,
134 | writePath: xHidden,
135 | notify: xAllowed,
136 | action: xAllowed,
137 | },
138 | }
139 | for _, test := range tests {
140 | acl := NewRole()
141 | for _, testAcDef := range test.acls {
142 | acl.Access[testAcDef.Path] = testAcDef
143 | }
144 |
145 | s := b.Root()
146 | s.Constraints.AddConstraint("auth", 0, 0, acl)
147 | s.Context = s.Constraints.ContextConstraint(s)
148 |
149 | t.Log(test.desc + " read")
150 | fc.AssertEqual(t, test.read, val2auth(sel(s.Find("count")).Get()))
151 |
152 | t.Logf(test.desc + " read path")
153 | pathSel := sel(s.Find("owner"))
154 | fc.AssertEqual(t, test.readPath, sel2auth(pathSel))
155 |
156 | t.Log(test.desc + " write")
157 | writeErr := sel(s.Find("count")).SetValue(100)
158 | fc.AssertEqual(t, test.write, err2auth(writeErr))
159 |
160 | t.Log(test.desc + " write path")
161 | if pathSel == nil {
162 | fc.AssertEqual(t, test.writePath, xHidden)
163 | } else {
164 | writePathErr := sel(pathSel.Find("name")).SetValue("Harvey")
165 | fc.AssertEqual(t, test.writePath, err2auth(writePathErr))
166 | }
167 |
168 | t.Log(test.desc + " execute")
169 | _, actionErr := sel(s.Find("fieldtrip")).Action(nil)
170 | fc.AssertEqual(t, test.action, err2auth(actionErr))
171 |
172 | t.Log(test.desc + " notify")
173 | var notifyErr error
174 | sel(s.Find("identified")).Notifications(func(n node.Notification) {
175 | if errNode, isErr := (n.Event.Node).(node.ErrorNode); isErr {
176 | notifyErr = errNode.Err
177 | }
178 | })
179 | fc.AssertEqual(t, test.notify, err2auth(notifyErr))
180 | }
181 | }
182 |
183 | func val2auth(v val.Value, err error) string {
184 | if v == nil {
185 | return xHidden
186 | }
187 | if errors.Is(err, fc.UnauthorizedError) {
188 | return xUnauth
189 | }
190 | return xAllowed
191 | }
192 |
193 | func sel2auth(s *node.Selection) string {
194 | if s == nil {
195 | return xHidden
196 | }
197 | return xAllowed
198 | }
199 |
200 | func err2auth(err error) string {
201 | if err == nil {
202 | return xAllowed
203 | } else if errors.Is(err, fc.UnauthorizedError) {
204 | return xUnauth
205 | }
206 | panic(err.Error())
207 | }
208 |
209 | func sel(sel *node.Selection, err error) *node.Selection {
210 | if err != nil {
211 | panic(err)
212 | }
213 | return sel
214 | }
215 |
--------------------------------------------------------------------------------
/secure/cred_gen.go:
--------------------------------------------------------------------------------
1 | package secure
2 |
3 | import (
4 | "crypto/rand"
5 | "crypto/rsa"
6 | "crypto/x509"
7 | "crypto/x509/pkix"
8 | "encoding/pem"
9 | "io"
10 | "math/big"
11 | "time"
12 | )
13 |
14 | type Generator struct {
15 | Country string
16 | Organization string
17 | }
18 |
19 | type Cert struct {
20 | PrivateKey *rsa.PrivateKey
21 | Cert *x509.Certificate
22 | Raw []byte
23 | }
24 |
25 | func Decode(inKey io.Reader, inCert io.Reader) (*Cert, error) {
26 | return nil, nil
27 | }
28 |
29 | func (self *Cert) EncodeCert(out io.Writer) error {
30 | return pem.Encode(out, &pem.Block{Type: "CERTIFICATE", Bytes: self.Raw})
31 | }
32 |
33 | func (self *Cert) EncodeKey(out io.Writer) error {
34 | raw := x509.MarshalPKCS1PrivateKey(self.PrivateKey)
35 | return pem.Encode(out, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: raw})
36 | }
37 |
38 | func (self *Generator) CA() (*Cert, error) {
39 | pvtKey, err := rsa.GenerateKey(rand.Reader, 2048)
40 | if err != nil {
41 | return nil, err
42 | }
43 | pubKey := &pvtKey.PublicKey
44 |
45 | template := self.template()
46 | template.IsCA = true
47 | template.KeyUsage |= x509.KeyUsageCertSign
48 |
49 | // self-signed
50 | parent := template
51 |
52 | raw, err := x509.CreateCertificate(rand.Reader, template, parent, pubKey, pvtKey)
53 | if err != nil {
54 | return nil, err
55 | }
56 | return &Cert{
57 | PrivateKey: pvtKey,
58 | Cert: template,
59 | Raw: raw,
60 | }, nil
61 | }
62 |
63 | func (self *Generator) template() *x509.Certificate {
64 | return &x509.Certificate{
65 | BasicConstraintsValid: true,
66 | SubjectKeyId: []byte{1, 2, 3},
67 | SerialNumber: big.NewInt(1234),
68 | Subject: pkix.Name{
69 | Country: []string{self.Country},
70 | Organization: []string{self.Organization},
71 | },
72 | NotBefore: time.Now(),
73 | NotAfter: time.Now().AddDate(5, 0, 0),
74 | KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
75 | ExtKeyUsage: []x509.ExtKeyUsage{
76 | x509.ExtKeyUsageServerAuth,
77 | },
78 | }
79 | }
80 |
81 | func (self *Generator) Cert(parent *Cert) (*Cert, error) {
82 | pvtKey, err := rsa.GenerateKey(rand.Reader, 2048)
83 | if err != nil {
84 | return nil, err
85 | }
86 | pubKey := &pvtKey.PublicKey
87 | template := self.template()
88 | template.ExtKeyUsage = append(template.ExtKeyUsage, x509.ExtKeyUsageClientAuth)
89 |
90 | raw, err := x509.CreateCertificate(rand.Reader, template, parent.Cert, pubKey, pvtKey)
91 | if err != nil {
92 | return nil, err
93 | }
94 | return &Cert{
95 | PrivateKey: pvtKey,
96 | Cert: template,
97 | Raw: raw,
98 | }, nil
99 | }
100 |
--------------------------------------------------------------------------------
/secure/cred_gen_test.go:
--------------------------------------------------------------------------------
1 | package secure
2 |
3 | import "testing"
4 |
5 | func TestGenCa(t *testing.T) {
6 | g := &Generator{
7 | Country: "US",
8 | Organization: "Engineering",
9 | }
10 | ca, err := g.CA()
11 | if err != nil {
12 | t.Fatal(err)
13 | }
14 | if len(ca.Raw) == 0 {
15 | t.Error(len(ca.Raw))
16 | }
17 |
18 | c, err := g.Cert(ca)
19 | if err != nil {
20 | t.Fatal(err)
21 | }
22 | if len(c.Raw) == 0 {
23 | t.Error(len(c.Raw))
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/secure/mgmt.go:
--------------------------------------------------------------------------------
1 | package secure
2 |
3 | import (
4 | "github.com/freeconf/yang/val"
5 |
6 | "github.com/freeconf/yang/node"
7 | "github.com/freeconf/yang/nodeutil"
8 | )
9 |
10 | func Manage(rbac *Rbac) node.Node {
11 | return &nodeutil.Node{
12 | Object: rbac,
13 | Options: nodeutil.NodeOptions{
14 | TryPluralOnLists: true,
15 | },
16 | OnChild: func(n *nodeutil.Node, r node.ChildRequest) (node.Node, error) {
17 | switch r.Meta.Ident() {
18 | case "authentication":
19 | return nil, nil
20 | case "authorization":
21 | return n, nil
22 | }
23 | return n.DoChild(r)
24 | },
25 | OnField: func(n *nodeutil.Node, r node.FieldRequest, hnd *node.ValueHandle) error {
26 | switch r.Meta.Ident() {
27 | case "perm":
28 | ac := n.Object.(*AccessControl)
29 | if r.Write {
30 | ac.Permissions = Permission(hnd.Val.Value().(val.Enum).Id)
31 | } else {
32 | var err error
33 | hnd.Val, err = node.NewValue(r.Meta.Type(), ac.Permissions)
34 | return err
35 | }
36 | return nil
37 | }
38 | return n.DoField(r, hnd)
39 | },
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/secure/mgmt_test.go:
--------------------------------------------------------------------------------
1 | package secure
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/freeconf/yang/fc"
7 | "github.com/freeconf/yang/source"
8 |
9 | "github.com/freeconf/yang/node"
10 | "github.com/freeconf/yang/nodeutil"
11 | "github.com/freeconf/yang/parser"
12 | )
13 |
14 | func TestManage(t *testing.T) {
15 | a := NewRbac()
16 | ypath := source.Dir("../yang")
17 | b := node.NewBrowser(parser.RequireModule(ypath, "fc-secure"), Manage(a))
18 | err := b.Root().UpsertFrom(readJson(`{
19 | "authorization" : {
20 | "role" : [{
21 | "id" : "sales",
22 | "access" : [{
23 | "path" : "m",
24 | "perm" : "read"
25 | },{
26 | "path" : "m/x",
27 | "perm" : "none"
28 | },{
29 | "path" : "m/z",
30 | "perm" : "full"
31 | }]
32 | }]
33 | }
34 | }`))
35 | if err != nil {
36 | t.Fatal(err)
37 | }
38 | fc.AssertEqual(t, 1, len(a.Roles))
39 | //fc.AssertEqual(t, 3, len(a.Roles["sales"].Access))
40 | }
41 |
42 | func readJson(s string) node.Node {
43 | n, err := nodeutil.ReadJSON(s)
44 | if err != nil {
45 | panic(err)
46 | }
47 | return n
48 | }
49 |
--------------------------------------------------------------------------------
/secure/rbac.go:
--------------------------------------------------------------------------------
1 | package secure
2 |
3 | import "github.com/freeconf/yang/node"
4 |
5 | type Auth interface {
6 | ConstrainRoot(role string, c *node.Constraints)
7 | }
8 |
9 | // This does not implement NETMOD ACLs, but rather a simplistic implementation
10 | // to be both useful and example of more complex implementations
11 | type Rbac struct {
12 | Roles map[string]*Role
13 | }
14 |
15 | func NewRbac() *Rbac {
16 | return &Rbac{
17 | Roles: make(map[string]*Role),
18 | }
19 | }
20 |
21 | func (self *Rbac) ConstrainRoot(role string, c *node.Constraints) {
22 | r, found := self.Roles[role]
23 | if !found {
24 | r = noAccess
25 | }
26 | c.AddConstraint("auth", 0, 0, r)
27 | }
28 |
--------------------------------------------------------------------------------
/server_test.go:
--------------------------------------------------------------------------------
1 | package restconf
2 |
3 | import (
4 | "flag"
5 | "io"
6 | "io/ioutil"
7 | "net/http"
8 | "strings"
9 | "testing"
10 | "time"
11 |
12 | "github.com/freeconf/yang/fc"
13 |
14 | "github.com/freeconf/restconf/device"
15 | "github.com/freeconf/restconf/testdata"
16 | "github.com/freeconf/yang/node"
17 | "github.com/freeconf/yang/parser"
18 | "github.com/freeconf/yang/source"
19 | )
20 |
21 | var updateFlag = flag.Bool("update", false, "update golden files instead of verifying against them")
22 |
23 | func TestServer(t *testing.T) {
24 | ypath := source.Path("./testdata:./yang")
25 | m := parser.RequireModule(ypath, "car")
26 | car := testdata.New()
27 | bServer := node.NewBrowser(m, testdata.Manage(car))
28 | d := device.New(ypath)
29 | d.AddBrowser(bServer)
30 | s := NewServer(d)
31 | defer s.Close()
32 | err := d.ApplyStartupConfig(strings.NewReader(`
33 | {
34 | "fc-restconf" : {
35 | "web": {
36 | "port" : ":9080"
37 | },
38 | "debug" : true
39 | }
40 | }`))
41 | if err != nil {
42 | t.Fatal(err)
43 | }
44 | <-time.After(2 * time.Second)
45 |
46 | client := http.DefaultClient
47 | addr := "http://localhost:9080"
48 |
49 | r, err := client.Get(addr + "/.well-known/host-meta")
50 | goldResponse(t, "testdata/gold/well-known", r, err)
51 |
52 | r, err = client.Get(addr + "/restconf/schema/car.yang")
53 | goldResponse(t, "testdata/gold/car.yang", r, err)
54 |
55 | r, _ = client.Get(addr + "/restconf/schema/bogus")
56 | if r.StatusCode != 404 {
57 | t.Errorf("expected 404 got %d", r.StatusCode)
58 | }
59 |
60 | rfcMimes := []MimeType{
61 | YangDataJsonMimeType1, YangDataJsonMimeType2,
62 | YangDataXmlMimeType1, YangDataXmlMimeType2,
63 | }
64 |
65 | t.Run("rpc", func(t *testing.T) {
66 | // weird + strict RFC Complianiance = OK
67 | for _, c := range rfcMimes {
68 | r, _ = client.Post(addr+"/restconf/operations/car:rotateTires", string(c), nil)
69 | fc.AssertEqual(t, 204, r.StatusCode, string(c))
70 | }
71 |
72 | // weird + relaxed RFC Complianiance = OK
73 | r, _ = client.Post(addr+"/restconf/operations/car:rotateTires", string(PlainJsonMimeType), nil)
74 | fc.AssertEqual(t, 204, r.StatusCode)
75 |
76 | // intuative + strict RFC Complianiance = NOT OK
77 | for _, c := range rfcMimes {
78 | r, _ = client.Post(addr+"/restconf/data/car:rotateTires", string(c), nil)
79 | fc.AssertEqual(t, 400, r.StatusCode, string(c))
80 | }
81 |
82 | // intuative + relaxed RFC Complianiance = OK
83 | r, _ = client.Post(addr+"/restconf/data/car:rotateTires", string(PlainJsonMimeType), nil)
84 | fc.AssertEqual(t, 204, r.StatusCode)
85 | })
86 |
87 | t.Run("rpc-io", func(t *testing.T) {
88 | tests := []struct {
89 | format MimeType
90 | input string
91 | output string
92 | }{
93 | {
94 | format: PlainJsonMimeType,
95 | input: `{"source":"tripa"}`,
96 | output: `{"miles":0}`,
97 | },
98 | {
99 | format: YangDataJsonMimeType1,
100 | input: `{"car:input":{"source":"tripa"}}`,
101 | output: `{"car:output":{"miles":0}}`,
102 | },
103 | {
104 | format: YangDataXmlMimeType1,
105 | input: `tripa`,
106 | output: ``,
107 | },
108 | }
109 | for _, test := range tests {
110 | payload := strings.NewReader(test.input)
111 | req, err := http.NewRequest("POST", addr+"/restconf/operations/car:getMiles", payload)
112 | fc.RequireEqual(t, nil, err)
113 | req.Header.Set("Content-Type", string(test.format))
114 | req.Header.Set("Accept", string(test.format))
115 | resp, err := client.Do(req)
116 | fc.RequireEqual(t, nil, err)
117 | fc.AssertEqual(t, 200, resp.StatusCode)
118 | actual, err := io.ReadAll(resp.Body)
119 | fc.RequireEqual(t, nil, err)
120 | fc.AssertEqual(t, test.output, string(actual))
121 | }
122 | })
123 |
124 | s.Close()
125 | }
126 |
127 | func goldResponse(t *testing.T, goldFile string, r *http.Response, err error) {
128 | t.Helper()
129 | if err != nil {
130 | t.Error(err)
131 | return
132 | }
133 | data, err := ioutil.ReadAll(r.Body)
134 | if err != nil {
135 | t.Error(err)
136 | return
137 | }
138 | fc.Gold(t, *updateFlag, data, goldFile)
139 | if r.StatusCode != 200 {
140 | t.Errorf("gave status code %d", r.StatusCode)
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/stock/testdata/test.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDmzCCAoOgAwIBAgIUR9L++H2o6Cv+Ug1EBD1cYPZ1l94wDQYJKoZIhvcNAQEL
3 | BQAwXTELMAkGA1UEBhMCVVMxDTALBgNVBAgMBFNlbGYxDTALBgNVBAcMBFNlbGYx
4 | DTALBgNVBAoMBFNlbGYxDTALBgNVBAsMBFNlbGYxEjAQBgNVBAMMCWxvY2FsaG9z
5 | dDAeFw0yMzA4MTkxMTQxMzNaFw0yNjA1MTQxMTQxMzNaMF0xCzAJBgNVBAYTAlVT
6 | MQ0wCwYDVQQIDARTZWxmMQ0wCwYDVQQHDARTZWxmMQ0wCwYDVQQKDARTZWxmMQ0w
7 | CwYDVQQLDARTZWxmMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB
8 | AQUAA4IBDwAwggEKAoIBAQCjFKjbaL+GSGrsqLBmKQKDrgL+f6+35cLUCbbJWfE2
9 | dw1yaIZDMFmcbpisfJcf7jjEH3eqSvijf9QiUO4qsK6HV7q8ASDv94s1YLtr97aZ
10 | /4zIe+MW3SSpJ/LTj0FCQd/kqOiU8RCRF6Kd0U9AyOyjxXs3eFHfO9tTZ97k+6iu
11 | 9e8YJQ9F2u7sE4ubUphUBHm1eyCsyH5LaDboHBWECt1B4hNfMOqWCQLc6+0DRnC4
12 | QzuVK2dr4dTEshozKIJQ6LYmPPjd5qVJ1OPAaLgiOdcT1EteJ+0MWAKIrt2lwfdE
13 | v+oo7JmWhKfsEuaMIxYd9EcnbnTcwC/vPKBLvW93zZkrAgMBAAGjUzBRMB0GA1Ud
14 | DgQWBBTUP2Owvrno9g+r3tWaZ8cPXxEHITAfBgNVHSMEGDAWgBTUP2Owvrno9g+r
15 | 3tWaZ8cPXxEHITAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCf
16 | PxzbTPiLBS+rqFSdFtJEEiBvSVw43a7FAnmx9aSc7lo7Q9+uWS/p6jhRcav884Pk
17 | eXaT5/A/hQ1gRsQyvTx6GSbITZngjpfjJ1nVF/V1VlPnbpHM0f6gUP7HjUmRjOCG
18 | mYGsJLPv/w+AaS1S5NAl7xWLqnSyIcIn4nw3DihvrzjDblChvJevPXSFxilrwgdI
19 | cZrDzzjf0WmCoRj+OgFWu6tA61A8EEclO/4u6qCmGz7tHZ+1POpKxxF6AXv4FIR2
20 | lAe42Um+wqEcOVyc1llWiwDa6256jGXZ4LXVELfgqeBnTVjG3P7TgvZvXW6FVqMD
21 | T4NpnbR/fzgjtJCWD2k0
22 | -----END CERTIFICATE-----
23 |
--------------------------------------------------------------------------------
/stock/testdata/test.key:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCjFKjbaL+GSGrs
3 | qLBmKQKDrgL+f6+35cLUCbbJWfE2dw1yaIZDMFmcbpisfJcf7jjEH3eqSvijf9Qi
4 | UO4qsK6HV7q8ASDv94s1YLtr97aZ/4zIe+MW3SSpJ/LTj0FCQd/kqOiU8RCRF6Kd
5 | 0U9AyOyjxXs3eFHfO9tTZ97k+6iu9e8YJQ9F2u7sE4ubUphUBHm1eyCsyH5LaDbo
6 | HBWECt1B4hNfMOqWCQLc6+0DRnC4QzuVK2dr4dTEshozKIJQ6LYmPPjd5qVJ1OPA
7 | aLgiOdcT1EteJ+0MWAKIrt2lwfdEv+oo7JmWhKfsEuaMIxYd9EcnbnTcwC/vPKBL
8 | vW93zZkrAgMBAAECggEAEs9ENqJbTNq3cnp2diIOxy9f6wjBNL3MffZs3caA+t46
9 | b9K8ga1m+JMNCwAH0kgAxiY3AC5LhVguGJMrFUNxreyhfiPHL8ZmiQ6SL37P0e1W
10 | yJz5cV6qZtRTvrEMPt8oOtB8mwk+9u059Wf58yf5m14guyIml0/pGTKm09Ru/Vbz
11 | ONCAr6hNDYSUMP9DEQq91HNlSJaxIiz5JAr6cVt1bYr4PgVdRgHVot6jCkeDew8W
12 | yl9SRQEVHPTWxEjcSWxjsRT5lvFc6uzt9xP0+2eYpAOmqt2NNgV8kX9ilINU5vda
13 | jpX3DhKf1hniYNnR4F3vLleEv9oAyBHiaLVtrlNBiQKBgQDYpyP/Q6ik1mCHetCm
14 | bw9ViS0456Cptzbl0X6oNfDWVcypx76l+4xQzIUsx+X8hhWSWCx32g8E8vsu3Xyu
15 | UI0veCdSr40s8qDZe8a+M0UyMdBjA+y0PSqi4kGS5Bkte1iVQmJLZx3JvIP2kMlg
16 | 9VUBpne6XFZtL1fBSnhwkUP/RQKBgQDAssh0NyGAT9PPI75T08J7Ea1imyCo9niH
17 | NQX91nQEyd1WDPpV1EKo5Wo+BDkBztx8uLU4PKuOWmcsDNYKcyS5vECR1G83pSLf
18 | j8k53KXeO5VXyn4j7YhAoOh/SZqm03QU7ZEf9J6mWTlflRDNKfu1qWCPPbQlGLz+
19 | WHt5cHrFrwKBgB4wTAfJuboeVR0Ls/89Ann8v1xqiiyb5v5nDqsJuSFkmzwdJSej
20 | wY2Qsg3nXNivJlpaBvg8XlNmODHzkugHOWUf7PrMk3Nr65XidXMTWdUAs/TYGyeS
21 | TlqcBF8fgZKg2VsTAPdYywHNTuYQjr7/8HRmadCCBVj5e9TnN5kX5M2xAoGATaE4
22 | xCjZpDmEmPRLXrX2qWHLuefI1ojEsaOY0FQu1ScFtf7wctdaK3SQwn8tAB94D/7R
23 | cuBuNKES41ogeIE+kFzYnmc4jvBGBk6EwHy1jbvvRDjdbFfrBi5Mu8rJNOAG6c9z
24 | Ia6Io3dad7kz1ZNORjcAIXS4OCGpMcYvqBK6Qx0CgYBkVDXSPlpDByN3aNKx8dsY
25 | OknnzmxL4FPzgV0LGHOuU/YdujTB3Dx1O+5dpYdNGTHKICnttXLR09tgn1Zdx86o
26 | oXFGwkIhd60M7IT8T9CDfFkWE2tQ0kJFCwmhXfT50Qkzzh2nHeLaOVNqx9uTJT0H
27 | kMgDGRB2LltAJTz4TtDK+A==
28 | -----END PRIVATE KEY-----
29 |
--------------------------------------------------------------------------------
/stock/tls.go:
--------------------------------------------------------------------------------
1 | package stock
2 |
3 | import (
4 | "crypto/tls"
5 | "crypto/x509"
6 | "os"
7 |
8 | "github.com/freeconf/yang/node"
9 | "github.com/freeconf/yang/nodeutil"
10 | "github.com/freeconf/yang/val"
11 | )
12 |
13 | type Tls struct {
14 | Config tls.Config
15 | CertFile string
16 | KeyFile string
17 | CaCertFile string
18 | }
19 |
20 | func TlsNode(config *Tls) node.Node {
21 | return &nodeutil.Extend{
22 | Base: nodeutil.ReflectChild(&config.Config),
23 | OnChild: func(p node.Node, r node.ChildRequest) (node.Node, error) {
24 | switch r.Meta.Ident() {
25 | case "ca":
26 | if r.New {
27 | config.Config.RootCAs = x509.NewCertPool()
28 |
29 | // assertion - harmless if not used, but useful if is used.
30 | config.Config.ClientCAs = config.Config.RootCAs
31 | config.Config.ClientAuth = tls.VerifyClientCertIfGiven
32 | }
33 | if config.Config.RootCAs != nil {
34 | return CertificateAuthorityNode(config), nil
35 | }
36 | case "cert":
37 | if r.New {
38 | config.Config.Certificates = make([]tls.Certificate, 1)
39 | }
40 | if len(config.Config.Certificates) > 0 {
41 | return CertificateNode(config), nil
42 | }
43 | }
44 | return p.Child(r)
45 | },
46 | }
47 | }
48 |
49 | func CertificateAuthorityNode(config *Tls) node.Node {
50 | n := &nodeutil.Basic{}
51 | n.OnField = func(r node.FieldRequest, hnd *node.ValueHandle) error {
52 | switch r.Meta.Ident() {
53 | case "certFile":
54 | if r.Write {
55 | config.CaCertFile = hnd.Val.String()
56 | pemData, err := os.ReadFile(hnd.Val.String())
57 | if err != nil {
58 | return err
59 | }
60 | config.Config.RootCAs.AppendCertsFromPEM(pemData)
61 | } else {
62 | hnd.Val = val.String(config.CaCertFile)
63 | }
64 | }
65 | return nil
66 | }
67 | return n
68 | }
69 |
70 | func CertificateNode(config *Tls) node.Node {
71 | n := &nodeutil.Basic{}
72 | n.OnField = func(r node.FieldRequest, hnd *node.ValueHandle) (err error) {
73 | switch r.Meta.Ident() {
74 | case "certFile":
75 | if r.Write {
76 | config.CertFile = hnd.Val.String()
77 | } else {
78 | hnd.Val = val.String(config.CertFile)
79 | }
80 | case "keyFile":
81 | if r.Write {
82 | config.KeyFile = hnd.Val.String()
83 | } else {
84 | hnd.Val = val.String(config.KeyFile)
85 | }
86 | }
87 | return nil
88 | }
89 | n.OnEndEdit = func(r node.NodeRequest) error {
90 | var err error
91 | if r.New {
92 | config.Config.Certificates[0], err = tls.LoadX509KeyPair(config.CertFile, config.KeyFile)
93 | }
94 | return err
95 | }
96 | return n
97 | }
98 |
--------------------------------------------------------------------------------
/stock/tls_test.go:
--------------------------------------------------------------------------------
1 | package stock
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/freeconf/yang/fc"
7 | "github.com/freeconf/yang/node"
8 | "github.com/freeconf/yang/nodeutil"
9 | "github.com/freeconf/yang/parser"
10 | "github.com/freeconf/yang/source"
11 | )
12 |
13 | func TestTlsNode(t *testing.T) {
14 |
15 | // where car.yang is stored
16 | ypath := source.Dir("../yang")
17 |
18 | // Define new YANG module on the fly that references the application
19 | // YANG file but we pull in just what we want
20 | m, err := parser.LoadModuleFromString(ypath, `
21 | module x {
22 | import fc-stocklib {
23 | prefix "x";
24 | }
25 | uses x:tls;
26 | }
27 | `)
28 | fc.RequireEqual(t, nil, err)
29 | scfg := `{
30 | "cert":{
31 | "certFile": "testdata/test.crt",
32 | "keyFile": "testdata/test.key"
33 | }
34 | }`
35 | cfg := &Tls{}
36 | b := node.NewBrowser(m, TlsNode(cfg))
37 | fc.AssertEqual(t, nil, b.Root().UpsertFrom(readJson(scfg)))
38 | actual, err := nodeutil.WriteJSON(b.Root())
39 | fc.RequireEqual(t, nil, err)
40 | fc.RequireEqual(t, `{"cert":{"certFile":"testdata/test.crt","keyFile":"testdata/test.key"}}`, actual)
41 | }
42 |
43 | func readJson(s string) node.Node {
44 | n, err := nodeutil.ReadJSON(s)
45 | if err != nil {
46 | panic(err)
47 | }
48 | return n
49 | }
50 |
--------------------------------------------------------------------------------
/stock/web.go:
--------------------------------------------------------------------------------
1 | package stock
2 |
3 | import (
4 | "context"
5 | "crypto/tls"
6 | "io"
7 | "mime"
8 | "net"
9 | "net/http"
10 | "path/filepath"
11 | "time"
12 |
13 | "github.com/freeconf/yang/fc"
14 | "github.com/freeconf/yang/node"
15 | "github.com/freeconf/yang/nodeutil"
16 | "github.com/freeconf/yang/source"
17 | )
18 |
19 | type HttpServerOptions struct {
20 | Addr string
21 | Port string
22 | ReadTimeout int
23 | WriteTimeout int
24 | Tls *Tls
25 | Iface string
26 | CallbackAddress string
27 | NotifyKeepaliveTimeoutMs int
28 | }
29 |
30 | type HttpServer struct {
31 | options HttpServerOptions
32 | Server *http.Server
33 | handler http.Handler
34 | Metrics WebMetrics
35 | }
36 |
37 | func (service *HttpServer) Options() HttpServerOptions {
38 | return service.options
39 | }
40 |
41 | func (service *HttpServer) ApplyOptions(options HttpServerOptions) {
42 | if options == service.options {
43 | return
44 | }
45 | service.options = options
46 | service.Server = &http.Server{
47 | Addr: options.Port,
48 | Handler: service.handler,
49 | ReadTimeout: time.Duration(options.ReadTimeout) * time.Millisecond,
50 | WriteTimeout: time.Duration(options.WriteTimeout) * time.Millisecond,
51 | MaxHeaderBytes: 1 << 20,
52 | ConnState: service.connectionUpdate,
53 | }
54 | chkStartErr := func(err error) {
55 | if err != nil && err != http.ErrServerClosed {
56 | fc.Err.Fatal(err)
57 | }
58 | }
59 | if options.Tls != nil {
60 | service.Server.TLSConfig = &options.Tls.Config
61 | go func() {
62 | // Using "tcp" listener allowed for greater config flexibility for cert
63 | // data but disabled HTTP/2
64 | chkStartErr(service.Server.ListenAndServeTLS(options.Tls.CertFile, options.Tls.KeyFile))
65 | }()
66 | } else {
67 | go func() {
68 | chkStartErr(service.Server.ListenAndServe())
69 | }()
70 | }
71 | }
72 |
73 | type WebMetrics struct {
74 | New int64
75 | Active int64
76 | Idle int64
77 | Hijacked int64
78 | Closed int64
79 | }
80 |
81 | func (service *HttpServer) connectionUpdate(conn net.Conn, state http.ConnState) {
82 | switch state {
83 | case http.StateNew:
84 | service.Metrics.New++
85 | case http.StateActive:
86 | service.Metrics.Active++
87 | case http.StateIdle:
88 | service.Metrics.Idle++
89 | case http.StateHijacked:
90 | service.Metrics.Hijacked++
91 | case http.StateClosed:
92 | service.Metrics.Closed++
93 | }
94 | }
95 |
96 | func (service *HttpServer) Stop() {
97 | service.Server.Shutdown(context.Background())
98 | }
99 |
100 | func NewHttpServer(handler http.Handler) *HttpServer {
101 | return &HttpServer{
102 | handler: handler,
103 | }
104 | }
105 |
106 | func (service *HttpServer) GetHttpClient() *http.Client {
107 | var client *http.Client
108 | if service.options.Tls != nil {
109 | tlsConfig := &tls.Config{
110 | Certificates: service.options.Tls.Config.Certificates,
111 | RootCAs: service.options.Tls.Config.RootCAs,
112 | }
113 | transport := &http.Transport{TLSClientConfig: tlsConfig}
114 | client = &http.Client{Transport: transport}
115 | } else {
116 | client = http.DefaultClient
117 | }
118 | return client
119 | }
120 |
121 | type StreamSourceWebHandler struct {
122 | Source source.Opener
123 | }
124 |
125 | func (service StreamSourceWebHandler) ServeHTTP(wtr http.ResponseWriter, req *http.Request) {
126 | path := req.URL.Path
127 | if path == "" {
128 | path = "index.html"
129 | }
130 | if rdr, err := service.Source(path, ""); err != nil {
131 | http.Error(wtr, err.Error(), 404)
132 | } else {
133 | if closer, ok := rdr.(io.Closer); ok {
134 | defer closer.Close()
135 | }
136 | ext := filepath.Ext(path)
137 | ctype := mime.TypeByExtension(ext)
138 | wtr.Header().Set("Content-Type", ctype)
139 | if _, err = io.Copy(wtr, rdr); err != nil {
140 | http.Error(wtr, err.Error(), http.StatusInternalServerError)
141 | }
142 | // Eventually support this but need file seeker to do that.
143 | // http.ServeContent(wtr, req, path, time.Now(), &ReaderPeeker{rdr})
144 | }
145 | }
146 |
147 | func WebServerNode(service *HttpServer) node.Node {
148 | options := service.Options()
149 | return &nodeutil.Extend{
150 | Base: nodeutil.ReflectChild(&options),
151 | OnChild: func(p node.Node, r node.ChildRequest) (node.Node, error) {
152 | switch r.Meta.Ident() {
153 | case "tls":
154 | if r.New {
155 | options.Tls = &Tls{}
156 | }
157 | if options.Tls != nil {
158 | return TlsNode(options.Tls), nil
159 | }
160 | case "metrics":
161 | return nodeutil.ReflectChild(&service.Metrics), nil
162 | }
163 | return nil, nil
164 | },
165 | OnEndEdit: func(p node.Node, r node.NodeRequest) error {
166 | if err := p.EndEdit(r); err != nil {
167 | return err
168 | }
169 | service.ApplyOptions(options)
170 | return nil
171 | },
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/testdata/bird.go:
--------------------------------------------------------------------------------
1 | package testdata
2 |
3 | import (
4 | "github.com/freeconf/restconf/device"
5 | "github.com/freeconf/yang/meta"
6 | "github.com/freeconf/yang/node"
7 | "github.com/freeconf/yang/nodeutil"
8 | "github.com/freeconf/yang/parser"
9 | "github.com/freeconf/yang/source"
10 | )
11 |
12 | type Bird struct {
13 | Name string
14 | Wingspan int
15 | Species *Species
16 | }
17 |
18 | type Species struct {
19 | Name string
20 | Class string
21 | }
22 |
23 | var YangPath = source.Path("../testdata:../yang")
24 |
25 | func BirdDevice(json string) (*device.Local, map[string]*Bird) {
26 | d := device.New(YangPath)
27 | b, birds := BirdBrowser(json)
28 | d.AddBrowser(b)
29 | if json != "" {
30 | if err := b.Root().UpsertFrom(readJson(json)); err != nil {
31 | panic(err)
32 | }
33 | }
34 | return d, birds
35 | }
36 |
37 | // not useful in prod
38 | func readJson(s string) node.Node {
39 | n, err := nodeutil.ReadJSON(s)
40 | if err != nil {
41 | panic(err)
42 | }
43 | return n
44 | }
45 |
46 | func BirdBrowser(json string) (*node.Browser, map[string]*Bird) {
47 | data := make(map[string]*Bird)
48 | b := node.NewBrowser(BirdModule(), BirdNode(data))
49 | if json != "" {
50 | if err := b.Root().UpsertFrom(readJson(json)); err != nil {
51 | panic(err)
52 | }
53 | }
54 | return b, data
55 | }
56 |
57 | func BirdModule() *meta.Module {
58 | return parser.RequireModule(YangPath, "bird")
59 | }
60 |
61 | func BirdNode(birds map[string]*Bird) node.Node {
62 | return &nodeutil.Basic{
63 | OnChild: func(r node.ChildRequest) (node.Node, error) {
64 | switch r.Meta.Ident() {
65 | case "bird":
66 | return nodeutil.ReflectList(birds), nil
67 | }
68 | return nil, nil
69 | },
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/testdata/bird.yang:
--------------------------------------------------------------------------------
1 | module bird {
2 | prefix "";
3 | namespace "";
4 | revision 0;
5 |
6 | list bird {
7 | key "name";
8 | leaf name {
9 | type string;
10 | }
11 | leaf wingspan {
12 | type int32;
13 | }
14 | container species {
15 | leaf name {
16 | type string;
17 | }
18 | leaf class {
19 | type string;
20 | }
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/testdata/car.yang:
--------------------------------------------------------------------------------
1 | module car {
2 | prefix "";
3 | namespace "c";
4 | description "Vehicle of sorts";
5 | revision 0;
6 |
7 | uses car;
8 |
9 | notification update {
10 | description "important state information about your car";
11 | uses car;
12 | }
13 |
14 | rpc rotateTires {
15 | description "rotate tires for optimal wear";
16 | }
17 |
18 | rpc replaceTires {
19 | description "replace all tires";
20 | }
21 |
22 | rpc getMiles {
23 | input {
24 | leaf source {
25 | type enumeration {
26 | enum odometer;
27 | enum tripa;
28 | enum tripb;
29 | }
30 | }
31 | }
32 | output {
33 | leaf miles {
34 | type int64;
35 | }
36 | }
37 | }
38 |
39 | grouping car {
40 | list tire {
41 | description "rubber circular part that makes contact with road";
42 | key "pos";
43 | uses tire;
44 | }
45 |
46 | leaf miles {
47 | config false;
48 | type int64;
49 | }
50 |
51 | leaf lastRotation {
52 | type int64;
53 | config false;
54 | }
55 |
56 | leaf running {
57 | type boolean;
58 | config false;
59 | }
60 |
61 | leaf speed {
62 | description "number of millisecs it takes to travel one mile";
63 | type int32;
64 | default 1000;
65 | }
66 | }
67 |
68 | grouping tire {
69 | leaf pos {
70 | type int32;
71 | }
72 | leaf size {
73 | type string;
74 | default 15;
75 | }
76 | leaf worn {
77 | config false;
78 | type boolean;
79 | }
80 | leaf wear {
81 | config false;
82 | type decimal64;
83 | }
84 | leaf flat {
85 | config false;
86 | type boolean;
87 | }
88 | }
89 |
90 | container engine {
91 | container specs {
92 | leaf horsepower {
93 | type int32;
94 | }
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/testdata/gold/car.json:
--------------------------------------------------------------------------------
1 | module car {
2 | prefix "";
3 | namespace "";
4 | description "Vehicle of sorts";
5 | revision 0;
6 |
7 | uses car;
8 |
9 | notification update {
10 | description "important state information about your car";
11 | uses car;
12 | }
13 |
14 | rpc rotateTires {
15 | description "rotate tires for optimal wear";
16 | }
17 |
18 | rpc replaceTires {
19 | description "replace all tires";
20 | }
21 |
22 | rpc getMiles {
23 | input {
24 | leaf source {
25 | type enumeration {
26 | enum odometer;
27 | enum tripa;
28 | enum tripb;
29 | }
30 | }
31 | }
32 | output {
33 | leaf miles {
34 | type int64;
35 | }
36 | }
37 | }
38 |
39 | grouping car {
40 | list tire {
41 | description "rubber circular part that makes contact with road";
42 | key "pos";
43 | uses tire;
44 | }
45 |
46 | leaf miles {
47 | config false;
48 | type int64;
49 | }
50 |
51 | leaf lastRotation {
52 | type int64;
53 | config false;
54 | }
55 |
56 | leaf running {
57 | type boolean;
58 | config false;
59 | }
60 |
61 | leaf speed {
62 | description "number of millisecs it takes to travel one mile";
63 | type int32;
64 | default 1000;
65 | }
66 | }
67 |
68 | grouping tire {
69 | leaf pos {
70 | type int32;
71 | }
72 | leaf size {
73 | type string;
74 | default 15;
75 | }
76 | leaf worn {
77 | config false;
78 | type boolean;
79 | }
80 | leaf wear {
81 | config false;
82 | type decimal64;
83 | }
84 | leaf flat {
85 | config false;
86 | type boolean;
87 | }
88 | }
89 |
90 | container engine {
91 | container specs {
92 | leaf horsepower {
93 | type int32;
94 | }
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/testdata/gold/car.yang:
--------------------------------------------------------------------------------
1 | module car {
2 | prefix "";
3 | namespace "c";
4 | description "Vehicle of sorts";
5 | revision 0;
6 |
7 | uses car;
8 |
9 | notification update {
10 | description "important state information about your car";
11 | uses car;
12 | }
13 |
14 | rpc rotateTires {
15 | description "rotate tires for optimal wear";
16 | }
17 |
18 | rpc replaceTires {
19 | description "replace all tires";
20 | }
21 |
22 | rpc getMiles {
23 | input {
24 | leaf source {
25 | type enumeration {
26 | enum odometer;
27 | enum tripa;
28 | enum tripb;
29 | }
30 | }
31 | }
32 | output {
33 | leaf miles {
34 | type int64;
35 | }
36 | }
37 | }
38 |
39 | grouping car {
40 | list tire {
41 | description "rubber circular part that makes contact with road";
42 | key "pos";
43 | uses tire;
44 | }
45 |
46 | leaf miles {
47 | config false;
48 | type int64;
49 | }
50 |
51 | leaf lastRotation {
52 | type int64;
53 | config false;
54 | }
55 |
56 | leaf running {
57 | type boolean;
58 | config false;
59 | }
60 |
61 | leaf speed {
62 | description "number of millisecs it takes to travel one mile";
63 | type int32;
64 | default 1000;
65 | }
66 | }
67 |
68 | grouping tire {
69 | leaf pos {
70 | type int32;
71 | }
72 | leaf size {
73 | type string;
74 | default 15;
75 | }
76 | leaf worn {
77 | config false;
78 | type boolean;
79 | }
80 | leaf wear {
81 | config false;
82 | type decimal64;
83 | }
84 | leaf flat {
85 | config false;
86 | type boolean;
87 | }
88 | }
89 |
90 | container engine {
91 | container specs {
92 | leaf horsepower {
93 | type int32;
94 | }
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/testdata/gold/error.json:
--------------------------------------------------------------------------------
1 | {"ietf-restconf:errors":{"error":[{"error-type":"protocol","error-tag":"operation-failed","error-path":"","error-message":"some error"}]}}
2 |
3 |
--------------------------------------------------------------------------------
/testdata/gold/error.xml:
--------------------------------------------------------------------------------
1 | protocoloperation-failedsome error
2 |
--------------------------------------------------------------------------------
/testdata/gold/well-known:
--------------------------------------------------------------------------------
1 | { "subject": "/", "links" : [ { "rel" : "restconf", "href" : "/restconf" } ] }
--------------------------------------------------------------------------------
/testdata/main.go:
--------------------------------------------------------------------------------
1 | //go:build ignore
2 | // +build ignore
3 |
4 | package main
5 |
6 | import (
7 | "strings"
8 |
9 | "github.com/freeconf/restconf"
10 | "github.com/freeconf/restconf/device"
11 | "github.com/freeconf/restconf/testdata"
12 | "github.com/freeconf/yang/source"
13 | )
14 |
15 | func main() {
16 | app := testdata.New()
17 | ypath := source.Any(restconf.InternalYPath, source.Path("."))
18 | d := device.New(ypath)
19 | if err := d.Add("car", testdata.Manage(app)); err != nil {
20 | panic(err)
21 | }
22 | restconf.NewServer(d)
23 | config := `{
24 | "fc-restconf":{
25 | "debug": true,
26 | "web":{
27 | "port":":8090"
28 | }
29 | },
30 | "car":{"speed":100}
31 | }`
32 |
33 | // apply start-up config normally stored in a config file on disk
34 | if err := d.ApplyStartupConfig(strings.NewReader(config)); err != nil {
35 | panic(err)
36 | }
37 | select {}
38 | }
39 |
--------------------------------------------------------------------------------
/testdata/x.yang:
--------------------------------------------------------------------------------
1 | module x {
2 | revision 0000-00-00;
3 | notification y {
4 | leaf z {
5 | type string;
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/wire_format.go:
--------------------------------------------------------------------------------
1 | package restconf
2 |
3 | import (
4 | "fmt"
5 | "io"
6 |
7 | "github.com/freeconf/yang/meta"
8 | )
9 |
10 | func getWireFormatter(accept MimeType) wireFormat {
11 | if accept.IsXml() {
12 | return xmlWireFormat(0)
13 | }
14 | return jsonWireFormat(0)
15 | }
16 |
17 | type wireFormat interface {
18 | writeNotificationStart(w io.Writer, module *meta.Module, etime string) (int, error)
19 | writeNotificationEnd(w io.Writer) (int, error)
20 | writeRpcOutputStart(w io.Writer, module *meta.Module) (int, error)
21 | writeRpcOutputEnd(w io.Writer) (int, error)
22 | }
23 |
24 | type jsonWireFormat int
25 |
26 | func (jsonWireFormat) writeNotificationStart(w io.Writer, module *meta.Module, etime string) (int, error) {
27 | return fmt.Fprintf(w, `{"ietf-restconf:notification":{"eventTime":"%s","event":`, etime)
28 | }
29 |
30 | func (jsonWireFormat) writeNotificationEnd(w io.Writer) (int, error) {
31 | return fmt.Fprint(w, "}}")
32 | }
33 |
34 | func (jsonWireFormat) writeRpcOutputStart(w io.Writer, module *meta.Module) (int, error) {
35 | return fmt.Fprintf(w, `{"%s:output":`, module.Ident())
36 | }
37 |
38 | func (jsonWireFormat) writeRpcOutputEnd(w io.Writer) (int, error) {
39 | return fmt.Fprint(w, "}")
40 | }
41 |
42 | type xmlWireFormat int
43 |
44 | func (xmlWireFormat) writeNotificationStart(w io.Writer, module *meta.Module, etime string) (int, error) {
45 | return fmt.Fprintf(w, `%s`, etime, module.Namespace())
47 | }
48 |
49 | func (xmlWireFormat) writeNotificationEnd(w io.Writer) (int, error) {
50 | return fmt.Fprint(w, "")
51 | }
52 |
53 | func (xmlWireFormat) writeRpcOutputStart(w io.Writer, module *meta.Module) (int, error) {
54 | return 0, nil
55 | }
56 |
57 | func (xmlWireFormat) writeRpcOutputEnd(w io.Writer) (int, error) {
58 | return 0, nil
59 | }
60 |
--------------------------------------------------------------------------------
/yang/fc-call-home-client.yang:
--------------------------------------------------------------------------------
1 | module fc-call-home-client {
2 | prefix "home";
3 | namespace "freeconf.org/fc-call-home";
4 | description "Configuring connection to a call-home service. Inspired by call-home RFC draft
5 | but should be replaced with official implementation eventually.";
6 | revision 0000-00-00;
7 |
8 | rpc register {
9 | input {}
10 | }
11 |
12 | rpc unregister {
13 | input {}
14 | }
15 |
16 | notification update {
17 | description "Change in registration status.";
18 | leaf registered {
19 | type boolean;
20 | }
21 | leaf err {
22 | description "Last registration error if there was one";
23 | type string;
24 | }
25 | }
26 |
27 | leaf registered {
28 | description "Success registration";
29 | config false;
30 | type boolean;
31 | }
32 |
33 | leaf deviceId {
34 | description "Unique device id within your infrastructure for this device. Uses DEVICE_ID environment variableb by default";
35 | type string;
36 | }
37 |
38 | leaf address {
39 | description "Hostname or IP address of application management system. Uses CALLHOME_ADDR environment variable by default";
40 | type string;
41 | }
42 |
43 | leaf localAddress {
44 | description "When client is initiating connection to a registration server, this is the network address";
45 | type string;
46 | }
47 |
48 | leaf retryRateMs {
49 | description "If registration fails, try again after given ms.";
50 | type int32;
51 | default 10000;
52 | }
53 | }
--------------------------------------------------------------------------------
/yang/fc-call-home-server.yang:
--------------------------------------------------------------------------------
1 | module fc-call-home-server {
2 | revision 0;
3 |
4 | rpc register {
5 | input {
6 | leaf deviceId {
7 | description "Id that is unique to this device in the infrastructure pool";
8 | type string;
9 | mandatory true;
10 | }
11 | leaf address {
12 | description "Optional. Will use incoming address of request. Hint: If you use the text
13 | phrase '{REG_ADD}' anywhere in the address, it will be replaced by the IP address found
14 | in the registration request. This does not include the port number because often that
15 | is not typically the port used when registering. Example https://{REG_ADDR}:8090/restconf";
16 |
17 | type string;
18 | mandatory true;
19 | }
20 | }
21 | }
22 |
23 | rpc unregister {
24 | description "Your registration will eventually timeout, but this is faster way to commmunicate you are unavailable.";
25 | }
26 | }
--------------------------------------------------------------------------------
/yang/fc-doc.yang:
--------------------------------------------------------------------------------
1 | module fc-doc {
2 | namespace "freeconf.org/fc-doc";
3 | prefix "doc";
4 | description "YANG parse in form used for documentation generation";
5 | revision 2018-03-03;
6 |
7 | leaf title {
8 | config false;
9 | type string;
10 | }
11 |
12 | list def {
13 | config false;
14 | key title;
15 |
16 | uses dataDef;
17 |
18 | list action {
19 | key title;
20 |
21 | uses metaDef;
22 |
23 | list input {
24 | key title;
25 | uses fieldsDef;
26 | }
27 |
28 | list output {
29 | key title;
30 | uses fieldsDef;
31 | }
32 | }
33 |
34 | list event {
35 | key title;
36 | uses dataDef;
37 | }
38 | }
39 |
40 | grouping metaDef {
41 |
42 | leaf title {
43 | config false;
44 | type string;
45 | }
46 |
47 | leaf description {
48 | config false;
49 | type string;
50 | }
51 | }
52 |
53 | grouping fieldsDef {
54 | uses metaDef;
55 |
56 | leaf details {
57 | type string;
58 | }
59 |
60 | leaf level {
61 | type int32;
62 | }
63 |
64 | leaf type {
65 | type string;
66 | }
67 | }
68 |
69 | grouping dataDef {
70 |
71 | leaf parent {
72 | config false;
73 | // should this be leafref?
74 | type string;
75 | }
76 |
77 | uses metaDef;
78 |
79 | list field {
80 | config false;
81 | key title;
82 |
83 | uses fieldsDef;
84 | }
85 | }
86 | }
--------------------------------------------------------------------------------
/yang/fc-map.yang:
--------------------------------------------------------------------------------
1 | module map {
2 | prefix "";
3 | namespace "";
4 | description "";
5 | revision 0;
6 |
7 | grouping deviceItem {
8 | leaf deviceId {
9 | type string;
10 | }
11 |
12 | list module {
13 | key "name";
14 |
15 | leaf name {
16 | type string;
17 | }
18 |
19 | leaf revision {
20 | type string;
21 | }
22 | }
23 | }
24 |
25 | list device {
26 | key "deviceId";
27 | config false;
28 | uses deviceItem;
29 | }
30 |
31 | rpc register {
32 | input {
33 | leaf deviceId {
34 | description "Id that is unique to this device in the infrastructure pool";
35 | type string;
36 | mandatory true;
37 | }
38 | }
39 | }
40 |
41 | notification update {
42 | uses deviceItem;
43 |
44 | leaf change {
45 | type enumeration {
46 | enum added;
47 | enum removed;
48 | }
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/yang/fc-restconf.yang:
--------------------------------------------------------------------------------
1 | module fc-restconf {
2 | namespace "org.freeconf/restconf";
3 | prefix "restconf";
4 | import fc-stocklib {
5 | prefix "stock";
6 | }
7 |
8 | description "service that implements RESTCONF RFC8040 device protocol";
9 | revision 0;
10 |
11 | leaf notifyKeepaliveTimeoutMs {
12 | description "close the connection after N milliseconds of no pings or activity";
13 | type int32;
14 | default 30000;
15 | }
16 |
17 | leaf debug {
18 | description "enable debug log messages";
19 | type boolean;
20 | default "false";
21 | }
22 |
23 | leaf streamCount {
24 | description "number of open sessions. each session have have many subscriptions";
25 | type int32;
26 | config false;
27 | }
28 |
29 | leaf subscriptionCount {
30 | description "number of subscriptions across all sessions";
31 | type int32;
32 | config false;
33 | }
34 |
35 | container web {
36 | description "web service used by restconf server";
37 |
38 | leaf port {
39 | description "required port number. Examples :8010 192.168.1.10:8080";
40 | type string;
41 | }
42 |
43 | leaf readTimeout {
44 | description "timeout in milliseconds to wait for reading data from client";
45 | type int32;
46 | default 10000;
47 | }
48 |
49 | leaf writeTimeout {
50 | description "timeout in milliseconds for sending data from client";
51 | type int32;
52 | default 10000;
53 | }
54 |
55 | container tls {
56 | description "required for secure transport";
57 | uses stock:tls;
58 | }
59 |
60 | container metrics {
61 | description "Details for connection metrics";
62 | config false;
63 |
64 | leaf new {
65 | description "Count of all new connections";
66 | type int64;
67 | }
68 |
69 | leaf active {
70 | description "Count of all active connections";
71 | type int64;
72 | }
73 |
74 | leaf idle {
75 | description "Count of all idle connections";
76 | type int64;
77 | }
78 |
79 | leaf hijacked {
80 | description "Count of all hijacked connections";
81 | type int64;
82 | }
83 |
84 | leaf closed {
85 | description "Count of all closed connections";
86 | type int64;
87 | }
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/yang/fc-secure.yang:
--------------------------------------------------------------------------------
1 | module fc-secure {
2 | prefix "";
3 | namespace "";
4 | description "Authentication details";
5 | revision 0;
6 |
7 | container authentication {
8 |
9 | }
10 |
11 | container authorization {
12 | list role {
13 |
14 | key "id";
15 |
16 | leaf id {
17 | type string;
18 | }
19 |
20 | list access {
21 |
22 | key "path";
23 |
24 | leaf path {
25 | type string;
26 | }
27 |
28 | leaf perm {
29 | type enumeration {
30 | enum none;
31 | enum read;
32 | enum full;
33 | }
34 | }
35 | }
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/yang/fc-stocklib.yang:
--------------------------------------------------------------------------------
1 | module fc-stocklib {
2 | namespace "org.freeconf/stock";
3 | prefix "stock";
4 | description "management of various objects on C2 library";
5 | revision 0000-00-00;
6 |
7 | grouping tls {
8 |
9 | leaf serverName {
10 | description "Name identified in certificate for this server";
11 | type string;
12 | }
13 |
14 | container cert {
15 | leaf certFile {
16 | description "PEM encoded certification";
17 | type string;
18 | }
19 | leaf keyFile {
20 | description "PEM encoded private key used to build certificate";
21 | type string;
22 | }
23 | }
24 |
25 | container ca {
26 | leaf certFile {
27 | description "PEM encoded certificate of certificate authority used to sign certificate";
28 | type string;
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/yang/ietf-datastores.yang:
--------------------------------------------------------------------------------
1 | module ietf-datastores {
2 | yang-version 1.1;
3 | namespace "urn:ietf:params:xml:ns:yang:ietf-datastores";
4 | prefix ds;
5 |
6 | organization
7 | "IETF Network Modeling (NETMOD) Working Group";
8 |
9 | contact
10 | "WG Web:
11 |
12 | WG List:
13 |
14 | Author: Martin Bjorklund
15 |
16 |
17 | Author: Juergen Schoenwaelder
18 |
19 |
20 | Author: Phil Shafer
21 |
22 |
23 | Author: Kent Watsen
24 |
25 |
26 | Author: Rob Wilton
27 | ";
28 |
29 | description
30 | "This YANG module defines a set of identities for identifying
31 | datastores.
32 |
33 | Copyright (c) 2018 IETF Trust and the persons identified as
34 | authors of the code. All rights reserved.
35 |
36 | Redistribution and use in source and binary forms, with or
37 | without modification, is permitted pursuant to, and subject to
38 | the license terms contained in, the Simplified BSD License set
39 | forth in Section 4.c of the IETF Trust's Legal Provisions
40 | Relating to IETF Documents
41 | (https://trustee.ietf.org/license-info).
42 |
43 | This version of this YANG module is part of RFC 8342
44 | (https://www.rfc-editor.org/info/rfc8342); see the RFC itself
45 | for full legal notices.";
46 |
47 | revision 2018-02-14 {
48 | description
49 | "Initial revision.";
50 | reference
51 | "RFC 8342: Network Management Datastore Architecture (NMDA)";
52 | }
53 |
54 | /*
55 | * Identities
56 | */
57 |
58 | identity datastore {
59 | description
60 | "Abstract base identity for datastore identities.";
61 | }
62 |
63 | identity conventional {
64 | base datastore;
65 | description
66 | "Abstract base identity for conventional configuration
67 | datastores.";
68 | }
69 |
70 | identity running {
71 | base conventional;
72 | description
73 | "The running configuration datastore.";
74 | }
75 |
76 | identity candidate {
77 | base conventional;
78 | description
79 | "The candidate configuration datastore.";
80 | }
81 |
82 | identity startup {
83 | base conventional;
84 | description
85 | "The startup configuration datastore.";
86 | }
87 |
88 | identity intended {
89 | base conventional;
90 | description
91 | "The intended configuration datastore.";
92 | }
93 |
94 | identity dynamic {
95 | base datastore;
96 | description
97 | "Abstract base identity for dynamic configuration datastores.";
98 | }
99 |
100 | identity operational {
101 | base datastore;
102 | description
103 | "The operational state datastore.";
104 | }
105 |
106 | /*
107 | * Type definitions
108 | */
109 |
110 | typedef datastore-ref {
111 | type identityref {
112 | base datastore;
113 | }
114 | description
115 | "A datastore identity reference.";
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/yang/ietf-restconf-monitoring.yang:
--------------------------------------------------------------------------------
1 | module ietf-restconf-monitoring {
2 | namespace "urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring";
3 | prefix "rcmon";
4 |
5 | import ietf-yang-types { prefix yang; }
6 | import ietf-inet-types { prefix inet; }
7 |
8 | organization
9 | "IETF NETCONF (Network Configuration) Working Group";
10 |
11 | contact
12 | "WG Web:
13 | WG List:
14 |
15 | Author: Andy Bierman
16 |
17 |
18 | Author: Martin Bjorklund
19 |
20 |
21 | Author: Kent Watsen
22 | ";
23 |
24 | description
25 | "This module contains monitoring information for the
26 | RESTCONF protocol.
27 |
28 | Copyright (c) 2017 IETF Trust and the persons identified as
29 | authors of the code. All rights reserved.
30 |
31 | Redistribution and use in source and binary forms, with or
32 | without modification, is permitted pursuant to, and subject
33 | to the license terms contained in, the Simplified BSD License
34 | set forth in Section 4.c of the IETF Trust's Legal Provisions
35 | Relating to IETF Documents
36 | (http://trustee.ietf.org/license-info).
37 |
38 | This version of this YANG module is part of RFC 8040; see
39 | the RFC itself for full legal notices.";
40 |
41 | revision 2017-01-26 {
42 | description
43 | "Initial revision.";
44 | reference
45 | "RFC 8040: RESTCONF Protocol.";
46 | }
47 |
48 | container restconf-state {
49 | config false;
50 | description
51 | "Contains RESTCONF protocol monitoring information.";
52 |
53 | container capabilities {
54 | description
55 | "Contains a list of protocol capability URIs.";
56 |
57 | leaf-list capability {
58 | type inet:uri;
59 | description
60 | "A RESTCONF protocol capability URI.";
61 | }
62 | }
63 |
64 | container streams {
65 | description
66 | "Container representing the notification event streams
67 | supported by the server.";
68 | reference
69 | "RFC 5277, Section 3.4, element.";
70 |
71 | list stream {
72 | key name;
73 | description
74 | "Each entry describes an event stream supported by
75 | the server.";
76 |
77 | leaf name {
78 | type string;
79 | description
80 | "The stream name.";
81 | reference
82 | "RFC 5277, Section 3.4, element.";
83 | }
84 |
85 | leaf description {
86 | type string;
87 | description
88 | "Description of stream content.";
89 | reference
90 | "RFC 5277, Section 3.4, element.";
91 | }
92 |
93 | leaf replay-support {
94 | type boolean;
95 | default false;
96 | description
97 | "Indicates if replay buffer is supported for this stream.
98 | If 'true', then the server MUST support the 'start-time'
99 | and 'stop-time' query parameters for this stream.";
100 | reference
101 | "RFC 5277, Section 3.4, element.";
102 | }
103 |
104 | leaf replay-log-creation-time {
105 | when "../replay-support" {
106 | description
107 | "Only present if notification replay is supported.";
108 | }
109 | type yang:date-and-time;
110 | description
111 | "Indicates the time the replay log for this stream
112 | was created.";
113 | reference
114 | "RFC 5277, Section 3.4,
115 | element.";
116 | }
117 |
118 | list access {
119 | key encoding;
120 | min-elements 1;
121 | description
122 | "The server will create an entry in this list for each
123 | encoding format that is supported for this stream.
124 | The media type 'text/event-stream' is expected
125 | for all event streams. This list identifies the
126 | subtypes supported for this stream.";
127 |
128 | leaf encoding {
129 | type string;
130 | description
131 | "This is the secondary encoding format within the
132 | 'text/event-stream' encoding used by all streams.
133 | The type 'xml' is supported for XML encoding.
134 | The type 'json' is supported for JSON encoding.";
135 | }
136 |
137 | leaf location {
138 | type inet:uri;
139 | mandatory true;
140 | description
141 | "Contains a URL that represents the entry point
142 | for establishing notification delivery via
143 | server-sent events.";
144 | }
145 | }
146 | }
147 | }
148 | }
149 |
150 | }
151 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/iana-bfd-types.yang:
--------------------------------------------------------------------------------
1 | module iana-bfd-types {
2 | yang-version 1.1;
3 | namespace "urn:ietf:params:xml:ns:yang:iana-bfd-types";
4 | prefix iana-bfd-types;
5 |
6 | organization
7 | "IANA";
8 | contact
9 | "Internet Assigned Numbers Authority
10 |
11 | Postal: ICANN
12 | 12025 Waterfront Drive, Suite 300
13 | Los Angeles, CA 90094-2536
14 | United States of America
15 | Tel: +1 310 301 5800
16 | ";
17 | description
18 | "This module defines YANG data types for IANA-registered
19 | BFD parameters.
20 |
21 | This YANG module is maintained by IANA and reflects the
22 | 'BFD Diagnostic Codes' and 'BFD Authentication Types'
23 | registries.
24 |
25 | Copyright (c) 2021 IETF Trust and the persons identified as
26 | authors of the code. All rights reserved.
27 |
28 | Redistribution and use in source and binary forms, with or
29 | without modification, is permitted pursuant to, and subject to
30 | the license terms contained in, the Simplified BSD License set
31 | forth in Section 4.c of the IETF Trust's Legal Provisions
32 | Relating to IETF Documents
33 | (https://trustee.ietf.org/license-info).
34 |
35 | This version of this YANG module is part of RFC 9127; see the
36 | RFC itself for full legal notices.";
37 | reference
38 | "RFC 9127: YANG Data Model for Bidirectional Forwarding
39 | Detection (BFD)";
40 |
41 | revision 2021-10-21 {
42 | description
43 | "Initial revision.";
44 | reference
45 | "RFC 9127: YANG Data Model for Bidirectional Forwarding
46 | Detection (BFD)";
47 | }
48 |
49 | /*
50 | * Type definitions
51 | */
52 |
53 | typedef diagnostic {
54 | type enumeration {
55 | enum none {
56 | value 0;
57 | description
58 | "No Diagnostic.";
59 | }
60 | enum control-expiry {
61 | value 1;
62 | description
63 | "Control Detection Time Expired.";
64 | }
65 | enum echo-failed {
66 | value 2;
67 | description
68 | "Echo Function Failed.";
69 | }
70 | enum neighbor-down {
71 | value 3;
72 | description
73 | "Neighbor Signaled Session Down.";
74 | }
75 | enum forwarding-reset {
76 | value 4;
77 | description
78 | "Forwarding Plane Reset.";
79 | }
80 | enum path-down {
81 | value 5;
82 | description
83 | "Path Down.";
84 | }
85 | enum concatenated-path-down {
86 | value 6;
87 | description
88 | "Concatenated Path Down.";
89 | }
90 | enum admin-down {
91 | value 7;
92 | description
93 | "Administratively Down.";
94 | }
95 | enum reverse-concatenated-path-down {
96 | value 8;
97 | description
98 | "Reverse Concatenated Path Down.";
99 | }
100 | enum mis-connectivity-defect {
101 | value 9;
102 | description
103 | "Mis-connectivity defect.";
104 | reference
105 | "RFC 5880: Bidirectional Forwarding Detection (BFD)
106 | RFC 6428: Proactive Connectivity Verification, Continuity
107 | Check, and Remote Defect Indication for the MPLS Transport
108 | Profile";
109 | }
110 | }
111 | description
112 | "BFD diagnostic codes as defined in RFC 5880. Values are
113 | maintained in the 'BFD Diagnostic Codes' IANA registry.
114 | Range is 0 to 31.";
115 | reference
116 | "RFC 5880: Bidirectional Forwarding Detection (BFD)";
117 | }
118 |
119 | typedef auth-type {
120 | type enumeration {
121 | enum reserved {
122 | value 0;
123 | description
124 | "Reserved.";
125 | }
126 | enum simple-password {
127 | value 1;
128 | description
129 | "Simple Password.";
130 | }
131 | enum keyed-md5 {
132 | value 2;
133 | description
134 | "Keyed MD5.";
135 | }
136 | enum meticulous-keyed-md5 {
137 | value 3;
138 | description
139 | "Meticulous Keyed MD5.";
140 | }
141 | enum keyed-sha1 {
142 | value 4;
143 | description
144 | "Keyed SHA1.";
145 | }
146 | enum meticulous-keyed-sha1 {
147 | value 5;
148 | description
149 | "Meticulous Keyed SHA1.";
150 | }
151 | }
152 | description
153 | "BFD authentication type as defined in RFC 5880. Values are
154 | maintained in the 'BFD Authentication Types' IANA registry.
155 | Range is 0 to 255.";
156 | reference
157 | "RFC 5880: Bidirectional Forwarding Detection (BFD)";
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/iana-crypt-hash.yang:
--------------------------------------------------------------------------------
1 | module iana-crypt-hash {
2 | namespace "urn:ietf:params:xml:ns:yang:iana-crypt-hash";
3 | prefix ianach;
4 |
5 | organization "IANA";
6 | contact
7 | " Internet Assigned Numbers Authority
8 |
9 | Postal: ICANN
10 | 12025 Waterfront Drive, Suite 300
11 | Los Angeles, CA 90094-2536
12 | United States
13 |
14 | Tel: +1 310 301 5800
15 | E-Mail: iana@iana.org>";
16 | description
17 | "This YANG module defines a type for storing passwords
18 | using a hash function and features to indicate which hash
19 | functions are supported by an implementation.
20 |
21 | The latest revision of this YANG module can be obtained from
22 | the IANA web site.
23 |
24 | Requests for new values should be made to IANA via
25 | email (iana@iana.org).
26 |
27 | Copyright (c) 2014 IETF Trust and the persons identified as
28 | authors of the code. All rights reserved.
29 |
30 | Redistribution and use in source and binary forms, with or
31 | without modification, is permitted pursuant to, and subject
32 | to the license terms contained in, the Simplified BSD License
33 | set forth in Section 4.c of the IETF Trust's Legal Provisions
34 | Relating to IETF Documents
35 | (http://trustee.ietf.org/license-info).
36 |
37 | The initial version of this YANG module is part of RFC 7317;
38 | see the RFC itself for full legal notices.";
39 |
40 | revision 2014-08-06 {
41 | description
42 | "Initial revision.";
43 | reference
44 | "RFC 7317: A YANG Data Model for System Management";
45 | }
46 |
47 | typedef crypt-hash {
48 | type string {
49 | pattern
50 | '$0$.*'
51 | + '|$1$[a-zA-Z0-9./]{1,8}$[a-zA-Z0-9./]{22}'
52 | + '|$5$(rounds=\d+$)?[a-zA-Z0-9./]{1,16}$[a-zA-Z0-9./]{43}'
53 | + '|$6$(rounds=\d+$)?[a-zA-Z0-9./]{1,16}$[a-zA-Z0-9./]{86}';
54 | }
55 | description
56 | "The crypt-hash type is used to store passwords using
57 | a hash function. The algorithms for applying the hash
58 | function and encoding the result are implemented in
59 | various UNIX systems as the function crypt(3).
60 |
61 | A value of this type matches one of the forms:
62 |
63 | $0$
64 | $$$
65 | $$$$
66 |
67 | The '$0$' prefix signals that the value is clear text. When
68 | such a value is received by the server, a hash value is
69 | calculated, and the string '$$$' or
70 | $$$$ is prepended to the result. This
71 | value is stored in the configuration data store.
72 | If a value starting with '$$', where is not '0', is
73 | received, the server knows that the value already represents a
74 | hashed value and stores it 'as is' in the data store.
75 |
76 | When a server needs to verify a password given by a user, it
77 | finds the stored password hash string for that user, extracts
78 | the salt, and calculates the hash with the salt and given
79 | password as input. If the calculated hash value is the same
80 | as the stored value, the password given by the client is
81 | accepted.
82 |
83 | This type defines the following hash functions:
84 |
85 | id | hash function | feature
86 | ---+---------------+-------------------
87 | 1 | MD5 | crypt-hash-md5
88 | 5 | SHA-256 | crypt-hash-sha-256
89 | 6 | SHA-512 | crypt-hash-sha-512
90 |
91 | The server indicates support for the different hash functions
92 | by advertising the corresponding feature.";
93 | reference
94 | "IEEE Std 1003.1-2008 - crypt() function
95 | RFC 1321: The MD5 Message-Digest Algorithm
96 | FIPS.180-4.2012: Secure Hash Standard (SHS)";
97 | }
98 |
99 | feature crypt-hash-md5 {
100 | description
101 | "Indicates that the device supports the MD5
102 | hash function in 'crypt-hash' values.";
103 | reference "RFC 1321: The MD5 Message-Digest Algorithm";
104 | }
105 |
106 | feature crypt-hash-sha-256 {
107 | description
108 | "Indicates that the device supports the SHA-256
109 | hash function in 'crypt-hash' values.";
110 | reference "FIPS.180-4.2012: Secure Hash Standard (SHS)";
111 | }
112 |
113 | feature crypt-hash-sha-512 {
114 | description
115 | "Indicates that the device supports the SHA-512
116 | hash function in 'crypt-hash' values.";
117 | reference "FIPS.180-4.2012: Secure Hash Standard (SHS)";
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-acldns.yang:
--------------------------------------------------------------------------------
1 | module ietf-acldns {
2 | yang-version 1.1;
3 | namespace "urn:ietf:params:xml:ns:yang:ietf-acldns";
4 | prefix ietf-acldns;
5 |
6 | import ietf-access-control-list {
7 | prefix acl;
8 | }
9 | import ietf-inet-types {
10 | prefix inet;
11 | }
12 |
13 | organization
14 | "IETF OPSAWG (Operations and Management Area Working Group)";
15 | contact
16 | "WG Web:
17 | WG List: opsawg@ietf.org
18 |
19 | Author: Eliot Lear
20 | lear@cisco.com
21 |
22 | Author: Ralph Droms
23 | rdroms@gmail.com
24 |
25 | Author: Dan Romascanu
26 | dromasca@gmail.com
27 | ";
28 | description
29 | "This YANG module defines a component that augments the
30 | IETF description of an access list to allow DNS names
31 | as matching criteria.
32 |
33 | Copyright (c) 2019 IETF Trust and the persons identified as
34 | authors of the code. All rights reserved.
35 |
36 | Redistribution and use in source and binary forms, with or
37 | without modification, is permitted pursuant to, and subject
38 | to the license terms contained in, the Simplified BSD License
39 | set forth in Section 4.c of the IETF Trust's Legal Provisions
40 | Relating to IETF Documents
41 | (http://trustee.ietf.org/license-info).";
42 |
43 | revision 2019-01-28 {
44 | description
45 | "Base version of dnsname extension of the ACL model.";
46 | reference
47 | "RFC 8520: Manufacturer Usage Description
48 | Specification";
49 | }
50 |
51 | grouping dns-matches {
52 | description
53 | "Domain names for matching.";
54 | leaf src-dnsname {
55 | type inet:host;
56 | description
57 | "domain name to be matched against.";
58 | }
59 | leaf dst-dnsname {
60 | type inet:host;
61 | description
62 | "domain name to be matched against.";
63 | }
64 | }
65 |
66 | augment "/acl:acls/acl:acl/acl:aces/acl:ace/acl:matches"
67 | + "/acl:l3/acl:ipv4/acl:ipv4" {
68 | description
69 | "Adding domain names to matching.";
70 | uses dns-matches;
71 | }
72 | augment "/acl:acls/acl:acl/acl:aces/acl:ace/acl:matches"
73 | + "/acl:l3/acl:ipv6/acl:ipv6" {
74 | description
75 | "Adding domain names to matching.";
76 | uses dns-matches;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-bfd-ip-mh.yang:
--------------------------------------------------------------------------------
1 | module ietf-bfd-ip-mh {
2 | yang-version 1.1;
3 | namespace "urn:ietf:params:xml:ns:yang:ietf-bfd-ip-mh";
4 | prefix bfd-ip-mh;
5 |
6 | import ietf-bfd-types {
7 | prefix bfd-types;
8 | reference
9 | "RFC 9314: YANG Data Model for Bidirectional Forwarding
10 | Detection (BFD)";
11 | }
12 | import ietf-bfd {
13 | prefix bfd;
14 | reference
15 | "RFC 9314: YANG Data Model for Bidirectional Forwarding
16 | Detection (BFD)";
17 | }
18 | import ietf-inet-types {
19 | prefix inet;
20 | reference
21 | "RFC 6991: Common YANG Data Types";
22 | }
23 | import ietf-routing {
24 | prefix rt;
25 | reference
26 | "RFC 8349: A YANG Data Model for Routing Management
27 | (NMDA Version)";
28 | }
29 |
30 | organization
31 | "IETF BFD Working Group";
32 | contact
33 | "WG Web:
34 | WG List:
35 |
36 | Editor: Reshad Rahman
37 |
38 |
39 | Editor: Lianshu Zheng
40 |
41 |
42 | Editor: Mahesh Jethanandani
43 | ";
44 | description
45 | "This module contains the YANG definition for BFD IP multihop
46 | as per RFC 5883.
47 |
48 | Copyright (c) 2022 IETF Trust and the persons identified as
49 | authors of the code. All rights reserved.
50 |
51 | Redistribution and use in source and binary forms, with or
52 | without modification, is permitted pursuant to, and subject to
53 | the license terms contained in, the Revised BSD License set
54 | forth in Section 4.c of the IETF Trust's Legal Provisions
55 | Relating to IETF Documents
56 | (https://trustee.ietf.org/license-info).
57 |
58 | This version of this YANG module is part of RFC 9314; see the
59 | RFC itself for full legal notices.";
60 | reference
61 | "RFC 5883: Bidirectional Forwarding Detection (BFD) for
62 | Multihop Paths
63 | RFC 9314: YANG Data Model for Bidirectional Forwarding
64 | Detection (BFD)";
65 |
66 | revision 2022-09-22 {
67 | description
68 | "Updating reference to RFC 9314.";
69 | reference
70 | "RFC 9314: YANG Data Model for Bidirectional Forwarding
71 | Detection (BFD).";
72 | }
73 | revision 2021-10-21 {
74 | description
75 | "Initial revision.";
76 | reference
77 | "RFC 9127: YANG Data Model for Bidirectional Forwarding
78 | Detection (BFD)";
79 | }
80 |
81 | /*
82 | * Augments
83 | */
84 |
85 | augment "/rt:routing/rt:control-plane-protocols/"
86 | + "rt:control-plane-protocol/bfd:bfd" {
87 | description
88 | "BFD augmentation for IP multihop.";
89 | container ip-mh {
90 | description
91 | "BFD IP multihop top-level container.";
92 | uses bfd-types:session-statistics-summary;
93 | container session-groups {
94 | description
95 | "BFD IP multihop session groups.";
96 | list session-group {
97 | key "source-addr dest-addr";
98 | description
99 | "Group of BFD IP multihop sessions (for ECMP). A
100 | group of sessions is between one source and one
101 | destination. Each session has a different field
102 | in the UDP/IP header for ECMP.";
103 | leaf source-addr {
104 | type inet:ip-address;
105 | description
106 | "Local IP address.";
107 | }
108 | leaf dest-addr {
109 | type inet:ip-address;
110 | description
111 | "IP address of the peer.";
112 | }
113 | uses bfd-types:common-cfg-parms;
114 | leaf tx-ttl {
115 | type bfd-types:hops;
116 | default "255";
117 | description
118 | "Hop count of outgoing BFD control packets.";
119 | }
120 | leaf rx-ttl {
121 | type bfd-types:hops;
122 | mandatory true;
123 | description
124 | "Minimum allowed hop count value for incoming BFD
125 | control packets. Control packets whose hop count is
126 | lower than this value are dropped.";
127 | }
128 | list sessions {
129 | config false;
130 | description
131 | "The multiple BFD sessions between a source and a
132 | destination.";
133 | uses bfd-types:all-session;
134 | }
135 | }
136 | }
137 | }
138 | }
139 |
140 | /*
141 | * Notifications
142 | */
143 |
144 | notification multihop-notification {
145 | description
146 | "Notification for BFD multihop session state change. An
147 | implementation may rate-limit notifications, e.g., when a
148 | session is continuously changing state.";
149 | uses bfd-types:notification-parms;
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-bfd-ip-sh.yang:
--------------------------------------------------------------------------------
1 | module ietf-bfd-ip-sh {
2 | yang-version 1.1;
3 | namespace "urn:ietf:params:xml:ns:yang:ietf-bfd-ip-sh";
4 | prefix bfd-ip-sh;
5 |
6 | import ietf-bfd-types {
7 | prefix bfd-types;
8 | reference
9 | "RFC 9314: YANG Data Model for Bidirectional Forwarding
10 | Detection (BFD)";
11 | }
12 | import ietf-bfd {
13 | prefix bfd;
14 | reference
15 | "RFC 9314: YANG Data Model for Bidirectional Forwarding
16 | Detection (BFD)";
17 | }
18 | import ietf-interfaces {
19 | prefix if;
20 | reference
21 | "RFC 8343: A YANG Data Model for Interface Management";
22 | }
23 | import ietf-inet-types {
24 | prefix inet;
25 | reference
26 | "RFC 6991: Common YANG Data Types";
27 | }
28 | import ietf-routing {
29 | prefix rt;
30 | reference
31 | "RFC 8349: A YANG Data Model for Routing Management
32 | (NMDA Version)";
33 | }
34 |
35 | organization
36 | "IETF BFD Working Group";
37 | contact
38 | "WG Web:
39 | WG List:
40 |
41 | Editor: Reshad Rahman
42 |
43 |
44 | Editor: Lianshu Zheng
45 |
46 |
47 | Editor: Mahesh Jethanandani
48 | ";
49 | description
50 | "This module contains the YANG definition for BFD IP single-hop
51 | as per RFC 5881.
52 |
53 | Copyright (c) 2022 IETF Trust and the persons identified as
54 | authors of the code. All rights reserved.
55 |
56 | Redistribution and use in source and binary forms, with or
57 | without modification, is permitted pursuant to, and subject
58 | to the license terms contained in, the Revised BSD License
59 | set forth in Section 4.c of the IETF Trust's Legal Provisions
60 | Relating to IETF Documents
61 | (https://trustee.ietf.org/license-info).
62 |
63 | This version of this YANG module is part of RFC 9314; see the
64 | RFC itself for full legal notices.";
65 | reference
66 | "RFC 5881: Bidirectional Forwarding Detection (BFD)
67 | for IPv4 and IPv6 (Single Hop)
68 | RFC 9314: YANG Data Model for Bidirectional Forwarding
69 | Detection (BFD)";
70 |
71 | revision 2022-09-22 {
72 | description
73 | "Updating reference to RFC 9314.";
74 | reference
75 | "RFC 9314: YANG Data Model for Bidirectional Forwarding
76 | Detection (BFD).";
77 | }
78 | revision 2021-10-21 {
79 | description
80 | "Initial revision.";
81 | reference
82 | "RFC 9127: YANG Data Model for Bidirectional Forwarding
83 | Detection (BFD)";
84 | }
85 |
86 | /*
87 | * Augments
88 | */
89 |
90 | augment "/rt:routing/rt:control-plane-protocols/"
91 | + "rt:control-plane-protocol/bfd:bfd" {
92 | description
93 | "BFD augmentation for IP single-hop.";
94 | container ip-sh {
95 | description
96 | "BFD IP single-hop top-level container.";
97 | uses bfd-types:session-statistics-summary;
98 | container sessions {
99 | description
100 | "BFD IP single-hop sessions.";
101 | list session {
102 | key "interface dest-addr";
103 | description
104 | "List of IP single-hop sessions.";
105 | leaf interface {
106 | type if:interface-ref;
107 | description
108 | "Interface on which the BFD session is running.";
109 | }
110 | leaf dest-addr {
111 | type inet:ip-address;
112 | description
113 | "IP address of the peer.";
114 | }
115 | leaf source-addr {
116 | type inet:ip-address;
117 | description
118 | "Local IP address.";
119 | }
120 | uses bfd-types:common-cfg-parms;
121 | uses bfd-types:all-session;
122 | }
123 | }
124 | list interfaces {
125 | key "interface";
126 | description
127 | "List of interfaces.";
128 | leaf interface {
129 | type if:interface-ref;
130 | description
131 | "BFD information for this interface.";
132 | }
133 | uses bfd-types:auth-parms;
134 | }
135 | }
136 | }
137 |
138 | /*
139 | * Notifications
140 | */
141 |
142 | notification singlehop-notification {
143 | description
144 | "Notification for BFD single-hop session state change. An
145 | implementation may rate-limit notifications, e.g., when a
146 | session is continuously changing state.";
147 | uses bfd-types:notification-parms;
148 | leaf interface {
149 | type if:interface-ref;
150 | description
151 | "Interface to which this BFD session belongs.";
152 | }
153 | leaf echo-enabled {
154 | type boolean;
155 | description
156 | "Indicates whether Echo was enabled for BFD.";
157 | }
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-bfd.yang:
--------------------------------------------------------------------------------
1 | module ietf-bfd {
2 | yang-version 1.1;
3 | namespace "urn:ietf:params:xml:ns:yang:ietf-bfd";
4 | prefix bfd;
5 |
6 | import ietf-bfd-types {
7 | prefix bfd-types;
8 | reference
9 | "RFC 9314: YANG Data Model for Bidirectional Forwarding
10 | Detection (BFD)";
11 | }
12 | import ietf-routing {
13 | prefix rt;
14 | reference
15 | "RFC 8349: A YANG Data Model for Routing Management
16 | (NMDA Version)";
17 | }
18 |
19 | organization
20 | "IETF BFD Working Group";
21 | contact
22 | "WG Web:
23 | WG List:
24 |
25 | Editor: Reshad Rahman
26 |
27 |
28 | Editor: Lianshu Zheng
29 |
30 |
31 | Editor: Mahesh Jethanandani
32 | ";
33 | description
34 | "This module contains the YANG definition for BFD parameters as
35 | per RFC 5880.
36 |
37 | Copyright (c) 2022 IETF Trust and the persons identified as
38 | authors of the code. All rights reserved.
39 |
40 | Redistribution and use in source and binary forms, with or
41 | without modification, is permitted pursuant to, and subject
42 | to the license terms contained in, the Revised BSD License set
43 | forth in Section 4.c of the IETF Trust's Legal Provisions
44 | Relating to IETF Documents
45 | (https://trustee.ietf.org/license-info).
46 |
47 | This version of this YANG module is part of RFC 9314; see the
48 | RFC itself for full legal notices.";
49 | reference
50 | "RFC 5880: Bidirectional Forwarding Detection (BFD)
51 | RFC 9314: YANG Data Model for Bidirectional Forwarding
52 | Detection (BFD)";
53 |
54 | revision 2022-09-22 {
55 | description
56 | "Updating reference to RFC 9314.";
57 | reference
58 | "RFC 9314: YANG Data Model for Bidirectional Forwarding
59 | Detection (BFD).";
60 | }
61 | revision 2021-10-21 {
62 | description
63 | "Initial revision.";
64 | reference
65 | "RFC 9127: YANG Data Model for Bidirectional Forwarding
66 | Detection (BFD)";
67 | }
68 |
69 | augment "/rt:routing/rt:control-plane-protocols/"
70 | + "rt:control-plane-protocol" {
71 | when "derived-from-or-self(rt:type, 'bfd-types:bfdv1')" {
72 | description
73 | "This augmentation is only valid for a control plane protocol
74 | instance of BFD (type 'bfdv1').";
75 | }
76 | description
77 | "BFD augmentation.";
78 | container bfd {
79 | description
80 | "BFD top-level container.";
81 | uses bfd-types:session-statistics-summary;
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-complex-types.yang:
--------------------------------------------------------------------------------
1 | module ietf-complex-types {
2 |
3 | namespace "urn:ietf:params:xml:ns:yang:ietf-complex-types";
4 | prefix "ct";
5 |
6 | organization
7 | "NETMOD WG";
8 |
9 | contact
10 | "Editor: Bernd Linowski
11 |
12 | Editor: Mehmet Ersue
13 |
14 | Editor: Siarhei Kuryla
15 | ";
16 |
17 | description
18 | "YANG extensions for complex types and typed instance
19 | identifiers.
20 |
21 | Copyright (c) 2011 IETF Trust and the persons identified as
22 | authors of the code. All rights reserved.
23 |
24 | Redistribution and use in source and binary forms, with or
25 | without modification, is permitted pursuant to, and subject
26 | to the license terms contained in, the Simplified BSD License
27 | set forth in Section 4.c of the IETF Trust's Legal Provisions
28 | Relating to IETF Documents
29 | (http://trustee.ietf.org/license-info).
30 |
31 | This version of this YANG module is part of RFC 6095; see
32 | the RFC itself for full legal notices.";
33 |
34 | revision 2011-03-15 {
35 | description "Initial revision.";
36 | }
37 |
38 | extension complex-type {
39 | description "Defines a complex-type.";
40 | reference "Section 2.2, complex-type Extension Statement";
41 | argument type-identifier {
42 | yin-element true;
43 | }
44 | }
45 |
46 | extension extends {
47 | description "Defines the base type of a complex-type.";
48 | reference "Section 2.5, extends Extension Statement";
49 | argument base-type-identifier {
50 | yin-element true;
51 | }
52 | }
53 | extension abstract {
54 | description "Makes the complex-type abstract.";
55 | reference "Section 2.6, abstract Extension Statement";
56 | argument status;
57 | }
58 |
59 | extension instance {
60 | description "Declares an instance of the given
61 | complex type.";
62 | reference "Section 2.3, instance Extension Statement";
63 | argument ct-instance-identifier {
64 | yin-element true;
65 | }
66 | }
67 |
68 | extension instance-list {
69 | description "Declares a list of instances of the given
70 | complex type";
71 | reference "Section 2.4, instance-list Extension Statement";
72 | argument ct-instance-identifier {
73 | yin-element true;
74 | }
75 | }
76 |
77 | extension instance-type {
78 | description "Tells to which type instance the instance
79 | identifier refers.";
80 | reference "Section 3.2, instance-type Extension Statement";
81 | argument target-type-identifier {
82 | yin-element true;
83 | }
84 | }
85 |
86 | feature complex-types {
87 | description "Indicates that the server supports
88 | complex types and instance identifiers.";
89 | }
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-datastores.yang:
--------------------------------------------------------------------------------
1 | module ietf-datastores {
2 | yang-version 1.1;
3 | namespace "urn:ietf:params:xml:ns:yang:ietf-datastores";
4 | prefix ds;
5 |
6 | organization
7 | "IETF Network Modeling (NETMOD) Working Group";
8 |
9 | contact
10 | "WG Web:
11 |
12 | WG List:
13 |
14 | Author: Martin Bjorklund
15 |
16 |
17 | Author: Juergen Schoenwaelder
18 |
19 |
20 | Author: Phil Shafer
21 |
22 |
23 | Author: Kent Watsen
24 |
25 |
26 | Author: Rob Wilton
27 | ";
28 |
29 | description
30 | "This YANG module defines a set of identities for identifying
31 | datastores.
32 |
33 | Copyright (c) 2018 IETF Trust and the persons identified as
34 | authors of the code. All rights reserved.
35 |
36 | Redistribution and use in source and binary forms, with or
37 | without modification, is permitted pursuant to, and subject to
38 | the license terms contained in, the Simplified BSD License set
39 | forth in Section 4.c of the IETF Trust's Legal Provisions
40 | Relating to IETF Documents
41 | (https://trustee.ietf.org/license-info).
42 |
43 | This version of this YANG module is part of RFC 8342
44 | (https://www.rfc-editor.org/info/rfc8342); see the RFC itself
45 | for full legal notices.";
46 |
47 | revision 2018-02-14 {
48 | description
49 | "Initial revision.";
50 | reference
51 | "RFC 8342: Network Management Datastore Architecture (NMDA)";
52 | }
53 |
54 | /*
55 | * Identities
56 | */
57 |
58 | identity datastore {
59 | description
60 | "Abstract base identity for datastore identities.";
61 | }
62 |
63 | identity conventional {
64 | base datastore;
65 | description
66 | "Abstract base identity for conventional configuration
67 | datastores.";
68 | }
69 |
70 | identity running {
71 | base conventional;
72 | description
73 | "The running configuration datastore.";
74 | }
75 |
76 | identity candidate {
77 | base conventional;
78 | description
79 | "The candidate configuration datastore.";
80 | }
81 |
82 | identity startup {
83 | base conventional;
84 | description
85 | "The startup configuration datastore.";
86 | }
87 |
88 | identity intended {
89 | base conventional;
90 | description
91 | "The intended configuration datastore.";
92 | }
93 |
94 | identity dynamic {
95 | base datastore;
96 | description
97 | "Abstract base identity for dynamic configuration datastores.";
98 | }
99 |
100 | identity operational {
101 | base datastore;
102 | description
103 | "The operational state datastore.";
104 | }
105 |
106 | /*
107 | * Type definitions
108 | */
109 |
110 | typedef datastore-ref {
111 | type identityref {
112 | base datastore;
113 | }
114 | description
115 | "A datastore identity reference.";
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-factory-default.yang:
--------------------------------------------------------------------------------
1 | module ietf-factory-default {
2 | yang-version 1.1;
3 | namespace "urn:ietf:params:xml:ns:yang:ietf-factory-default";
4 | prefix fd;
5 |
6 | import ietf-datastores {
7 | prefix ds;
8 | reference
9 | "RFC 8342: Network Management Datastore Architecture
10 | (NMDA)";
11 | }
12 | import ietf-netconf-acm {
13 | prefix nacm;
14 | reference
15 | "RFC 8341: Network Configuration Access Control Model";
16 | }
17 |
18 | organization
19 | "IETF Network Modeling (netmod) Working Group";
20 | contact
21 | "WG Web:
22 | WG List:
23 |
24 | Editor: Qin Wu
25 |
26 |
27 | Editor: Balazs Lengyel
28 |
29 |
30 | Editor: Ye Niu
31 | ";
32 | description
33 | "This module provides functionality to reset a server to its
34 | factory default configuration and, when supported, to
35 | discover the factory default configuration contents
36 | independently of resetting the server.
37 |
38 | Copyright (c) 2020 IETF Trust and the persons identified as
39 | authors of the code. All rights reserved.
40 |
41 | Redistribution and use in source and binary forms, with or
42 | without modification, is permitted pursuant to, and subject
43 | to the license terms contained in, the Simplified BSD License
44 | set forth in Section 4.c of the IETF Trust's Legal Provisions
45 | Relating to IETF Documents
46 | (https://trustee.ietf.org/license-info).
47 |
48 | This version of this YANG module is part of RFC 8808; see the
49 | RFC itself for full legal notices.";
50 |
51 | revision 2020-08-31 {
52 | description
53 | "Initial revision.";
54 | reference
55 | "RFC 8808: A YANG Data Model for Factory Default Settings";
56 | }
57 |
58 | feature factory-default-datastore {
59 | description
60 | "Indicates that the factory default configuration is
61 | available as a datastore.";
62 | }
63 |
64 | rpc factory-reset {
65 | nacm:default-deny-all;
66 | description
67 | "The server resets all datastores to their factory
68 | default contents and any nonvolatile storage back to
69 | factory condition, deleting all dynamically
70 | generated files, including those containing keys,
71 | certificates, logs, and other temporary files.
72 |
73 | Depending on the factory default configuration, after
74 | being reset, the device may become unreachable on the
75 | network.";
76 | }
77 |
78 | identity factory-default {
79 | if-feature "factory-default-datastore";
80 | base ds:datastore;
81 | description
82 | "This read-only datastore contains the factory default
83 | configuration for the device that will be used to replace
84 | the contents of the read-write conventional configuration
85 | datastores during a 'factory-reset' RPC operation.";
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-foo.yang:
--------------------------------------------------------------------------------
1 | module ietf-foo {
2 | namespace "urn:ietf:params:xml:ns:yang:ietf-foo";
3 | prefix "foo";
4 | organization "...";
5 | contact "...";
6 | description "...";
7 | revision 2016-03-20 {
8 | description "Latest revision";
9 | reference "RFC XXXX: Foo Protocol";
10 | }
11 | // ... more statements
12 | }
13 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-lime-time-types.yang:
--------------------------------------------------------------------------------
1 | module ietf-lime-time-types {
2 | yang-version 1.1;
3 | namespace "urn:ietf:params:xml:ns:yang:ietf-lime-time-types";
4 | prefix lime;
5 |
6 | organization
7 | "IETF LIME Working Group";
8 | contact
9 | "WG Web:
10 | WG List:
11 |
12 | Editor: Qin Wu
13 | ";
14 | description
15 | "This module provides time-related definitions used by the data
16 | models written for Layer Independent OAM Management in the
17 | Multi-Layer Environment (LIME). This module defines
18 | identities but no schema tree elements.
19 |
20 | Copyright (c) 2019 IETF Trust and the persons identified as
21 | authors of the code. All rights reserved.
22 |
23 | Redistribution and use in source and binary forms, with or
24 | without modification, is permitted pursuant to, and subject
25 | to the license terms contained in, the Simplified BSD License
26 | set forth in Section 4.c of the IETF Trust's Legal Provisions
27 | Relating to IETF Documents
28 | (http://trustee.ietf.org/license-info).
29 |
30 | This version of this YANG module is part of RFC 8532; see
31 | the RFC itself for full legal notices.";
32 |
33 | revision 2019-04-16 {
34 | description
35 | "Initial version.";
36 | reference
37 | "RFC 8532: Generic YANG Data Model for the Management of
38 | Operations, Administration, and Maintenance (OAM) Protocols
39 | That Use Connectionless Communications";
40 | }
41 |
42 | /*** Collection of common types related to time ***/
43 | /*** Time unit identity ***/
44 |
45 | identity time-unit-type {
46 | description
47 | "Time unit type.";
48 | }
49 |
50 | identity hours {
51 | base time-unit-type;
52 | description
53 | "Time unit in hours.";
54 | }
55 |
56 | identity minutes {
57 | base time-unit-type;
58 | description
59 | "Time unit in minutes.";
60 | }
61 |
62 | identity seconds {
63 | base time-unit-type;
64 | description
65 | "Time unit in seconds.";
66 | }
67 |
68 | identity milliseconds {
69 | base time-unit-type;
70 | description
71 | "Time unit in milliseconds.";
72 | }
73 |
74 | identity microseconds {
75 | base time-unit-type;
76 | description
77 | "Time unit in microseconds.";
78 | }
79 |
80 | identity nanoseconds {
81 | base time-unit-type;
82 | description
83 | "Time unit in nanoseconds.";
84 | }
85 |
86 | /*** Timestamp format Identity ***/
87 |
88 | identity timestamp-type {
89 | description
90 | "Base identity for Timestamp Type.";
91 | }
92 |
93 | identity truncated-ptp {
94 | base timestamp-type;
95 | description
96 | "Identity for 64-bit short-format PTP timestamp.";
97 | }
98 |
99 | identity truncated-ntp {
100 | base timestamp-type;
101 | description
102 | "Identity for 32-bit short-format NTP timestamp.";
103 | }
104 |
105 | identity ntp64 {
106 | base timestamp-type;
107 | description
108 | "Identity for 64-bit NTP timestamp.";
109 | }
110 |
111 | identity icmp {
112 | base timestamp-type;
113 | description
114 | "Identity for 32-bit ICMP timestamp.";
115 | }
116 | identity ptp80 {
117 | base timestamp-type;
118 | description
119 | "Identity for 80-bit PTP timestamp.";
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-module-tags-state.yang:
--------------------------------------------------------------------------------
1 | module ietf-module-tags-state {
2 | yang-version 1.1;
3 | namespace "urn:ietf:params:xml:ns:yang:ietf-module-tags-state";
4 | prefix tags-s;
5 |
6 | import ietf-yang-types {
7 | prefix yang;
8 | }
9 | import ietf-module-tags {
10 | prefix tags;
11 | }
12 |
13 | organization
14 | "IETF NetMod Working Group (NetMod)";
15 | contact
16 | "WG Web:
17 | WG List:
18 |
19 | Author: Christian Hopps
20 |
21 |
22 | Author: Lou Berger
23 |
24 |
25 | Author: Dean Bogdanovic
26 | ";
27 |
28 | description
29 | "This module describes a mechanism associating tags with YANG
30 | modules. Tags may be IANA assigned or privately defined.
31 |
32 | This is a temporary non-NMDA module that is for use by
33 | implementations that don't yet support NMDA.
34 |
35 | Copyright (c) 2021 IETF Trust and the persons identified as
36 | authors of the code. All rights reserved.
37 |
38 | Redistribution and use in source and binary forms, with or
39 | without modification, is permitted pursuant to, and subject to
40 | the license terms contained in, the Simplified BSD License set
41 | forth in Section 4.c of the IETF Trust's Legal Provisions
42 | Relating to IETF Documents
43 | (https://trustee.ietf.org/license-info).
44 |
45 | This version of this YANG module is part of RFC 8819
46 | (https://www.rfc-editor.org/info/rfc8819); see the RFC itself
47 | for full legal notices.";
48 |
49 | revision 2021-01-04 {
50 | description
51 | "Initial revision.";
52 | reference
53 | "RFC 8819: YANG Module Tags";
54 | }
55 |
56 | container module-tags-state {
57 | config false;
58 | status deprecated;
59 | description
60 | "Contains the list of modules and their associated tags.";
61 | list module {
62 | key "name";
63 | status deprecated;
64 | description
65 | "A list of modules and their associated tags.";
66 | leaf name {
67 | type yang:yang-identifier;
68 | mandatory true;
69 | status deprecated;
70 | description
71 | "The YANG module name.";
72 | }
73 | leaf-list tag {
74 | type tags:tag;
75 | status deprecated;
76 | description
77 | "Tags associated with the module. See the IANA 'YANG
78 | Module Tag Prefixes' registry for reserved prefixes and
79 | the IANA 'IETF YANG Module Tags' registry for IETF tags.
80 |
81 | The contents of this list is constructed using the
82 | following steps:
83 |
84 | 1) System tags (i.e., tags of added by the system) are
85 | added.
86 | 2) User-configured tags (i.e., tags added by
87 | configuration) are added.
88 | 3) Any tag that is equal to a masked-tag present in the
89 | corresponding ietf-module-tags:module-tags:module-tag leaf
90 | list for this module is removed.";
91 | }
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-module-tags.yang:
--------------------------------------------------------------------------------
1 | module ietf-module-tags {
2 | yang-version 1.1;
3 | namespace "urn:ietf:params:xml:ns:yang:ietf-module-tags";
4 | prefix tags;
5 |
6 | import ietf-yang-types {
7 | prefix yang;
8 | }
9 |
10 | organization
11 | "IETF NetMod Working Group (NetMod)";
12 | contact
13 | "WG Web:
14 | WG List:
15 |
16 | Author: Christian Hopps
17 |
18 |
19 | Author: Lou Berger
20 |
21 |
22 | Author: Dean Bogdanovic
23 | ";
24 |
25 | description
26 | "This module describes a mechanism associating tags with YANG
27 | modules. Tags may be IANA assigned or privately defined.
28 |
29 | Copyright (c) 2021 IETF Trust and the persons identified as
30 | authors of the code. All rights reserved.
31 |
32 | Redistribution and use in source and binary forms, with or
33 | without modification, is permitted pursuant to, and subject to
34 | the license terms contained in, the Simplified BSD License set
35 | forth in Section 4.c of the IETF Trust's Legal Provisions
36 | Relating to IETF Documents
37 | (https://trustee.ietf.org/license-info).
38 |
39 | This version of this YANG module is part of RFC 8819
40 | (https://www.rfc-editor.org/info/rfc8819); see the RFC itself
41 | for full legal notices.
42 |
43 | The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL
44 | NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED',
45 | 'MAY', and 'OPTIONAL' in this document are to be interpreted as
46 | described in BCP 14 (RFC 2119) (RFC 8174) when, and only when,
47 | they appear in all capitals, as shown here.";
48 |
49 | revision 2021-01-04 {
50 | description
51 | "Initial revision.";
52 | reference
53 | "RFC 8819: YANG Module Tags";
54 | }
55 |
56 | typedef tag {
57 | type string {
58 | length "1..max";
59 | pattern '[\S ]+';
60 | }
61 | description
62 | "A tag is a type of 'string' value that does not include
63 | carriage return, newline, or tab characters. It SHOULD begin
64 | with a registered prefix; however, tags without a registered
65 | prefix SHOULD NOT be treated as invalid.";
66 | }
67 |
68 | extension module-tag {
69 | argument tag;
70 | description
71 | "The argument 'tag' is of type 'tag'. This extension statement
72 | is used by module authors to indicate the tags that SHOULD be
73 | added automatically by the system. As such, the origin of the
74 | value for the predefined tags should be set to 'system'
75 | [RFC8342].";
76 | }
77 |
78 | container module-tags {
79 | description
80 | "Contains the list of modules and their associated tags.";
81 | list module {
82 | key "name";
83 | description
84 | "A list of modules and their associated tags.";
85 | leaf name {
86 | type yang:yang-identifier;
87 | mandatory true;
88 | description
89 | "The YANG module name.";
90 | }
91 | leaf-list tag {
92 | type tag;
93 | description
94 | "Tags associated with the module. See the IANA 'YANG
95 | Module Tag Prefixes' registry for reserved prefixes and
96 | the IANA 'IETF YANG Module Tags' registry for IETF tags.
97 |
98 | The 'operational' state [RFC8342] view of this list is
99 | constructed using the following steps:
100 |
101 | 1) System tags (i.e., tags of 'system' origin) are added.
102 | 2) User-configured tags (i.e., tags of 'intended' origin)
103 | are added.
104 | 3) Any tag that is equal to a masked-tag is removed.";
105 | }
106 | leaf-list masked-tag {
107 | type tag;
108 | description
109 | "The list of tags that should not be associated with this
110 | module. The user can remove (mask) tags from the
111 | operational state datastore [RFC8342] by adding them to
112 | this list. It is not an error to add tags to this list
113 | that are not associated with the module, but they have no
114 | operational effect.";
115 | }
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-mud-detext-example.yang:
--------------------------------------------------------------------------------
1 | module ietf-mud-detext-example {
2 | yang-version 1.1;
3 | namespace "urn:ietf:params:xml:ns:yang:ietf-mud-detext-example";
4 | prefix ietf-mud-detext-example;
5 |
6 | import ietf-mud {
7 | prefix ietf-mud;
8 | }
9 |
10 | organization
11 | "IETF OPSAWG (Operations and Management Area Working Group)";
12 | contact
13 | "WG Web:
14 | WG List: opsawg@ietf.org
15 |
16 | Author: Eliot Lear
17 | lear@cisco.com
18 |
19 | Author: Ralph Droms
20 | rdroms@gmail.com
21 |
22 | Author: Dan Romascanu
23 | dromasca@gmail.com
24 | ";
25 | description
26 | "Sample extension to a MUD module to indicate a need
27 | for DETNET support.";
28 |
29 | revision 2019-01-28 {
30 | description
31 | "Initial revision.";
32 | reference
33 | "RFC 8520: Manufacturer Usage Description
34 | Specification";
35 | }
36 |
37 | augment "/ietf-mud:mud" {
38 | description
39 | "This adds a simple extension for a manufacturer
40 | to indicate whether DETNET is required by a
41 | device.";
42 | leaf is-detnet-required {
43 | type boolean;
44 | description
45 | "This value will equal 'true' if a device requires
46 | DETNET to properly function.";
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-netconf-partial-lock.yang:
--------------------------------------------------------------------------------
1 | module ietf-netconf-partial-lock {
2 |
3 | namespace urn:ietf:params:xml:ns:netconf:partial-lock:1.0;
4 | prefix pl;
5 |
6 | organization "IETF Network Configuration (netconf) Working Group";
7 |
8 | contact
9 | "Netconf Working Group
10 | Mailing list: netconf@ietf.org
11 | Web: http://www.ietf.org/html.charters/netconf-charter.html
12 |
13 | Balazs Lengyel
14 | Ericsson
15 | balazs.lengyel@ericsson.com";
16 |
17 | description
18 | "This YANG module defines the and
19 | operations.";
20 |
21 | revision 2009-10-19 {
22 | description
23 | "Initial version, published as RFC 5717.";
24 | }
25 |
26 | typedef lock-id-type {
27 | type uint32;
28 | description
29 | "A number identifying a specific partial-lock granted to a session.
30 | It is allocated by the system, and SHOULD be used in the
31 | partial-unlock operation.";
32 | }
33 |
34 | rpc partial-lock {
35 | description
36 | "A NETCONF operation that locks parts of the running datastore.";
37 | input {
38 | leaf-list select {
39 | type string;
40 | min-elements 1;
41 | description
42 | "XPath expression that specifies the scope of the lock.
43 | An Instance Identifier expression MUST be used unless the
44 | :xpath capability is supported, in which case any XPath 1.0
45 | expression is allowed.";
46 | }
47 | }
48 | output {
49 | leaf lock-id {
50 | type lock-id-type;
51 | description
52 | "Identifies the lock, if granted. The lock-id SHOULD be
53 | used in the partial-unlock rpc.";
54 | }
55 | leaf-list locked-node {
56 | type instance-identifier;
57 | min-elements 1;
58 | description
59 | "List of locked nodes in the running datastore";
60 | }
61 | }
62 | }
63 |
64 | rpc partial-unlock {
65 | description
66 | "A NETCONF operation that releases a previously acquired
67 | partial-lock.";
68 | input {
69 | leaf lock-id {
70 | type lock-id-type;
71 | description
72 | "Identifies the lock to be released. MUST be the value
73 | received in the response to a partial-lock operation.";
74 | }
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-netconf-with-defaults.yang:
--------------------------------------------------------------------------------
1 | module ietf-netconf-with-defaults {
2 |
3 | namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults";
4 |
5 | prefix ncwd;
6 |
7 | import ietf-netconf { prefix nc; }
8 |
9 | organization
10 | "IETF NETCONF (Network Configuration Protocol) Working Group";
11 |
12 | contact
13 | "WG Web:
14 |
15 | WG List:
16 |
17 | WG Chair: Bert Wijnen
18 |
19 |
20 | WG Chair: Mehmet Ersue
21 |
22 |
23 | Editor: Andy Bierman
24 |
25 |
26 | Editor: Balazs Lengyel
27 | ";
28 |
29 | description
30 | "This module defines an extension to the NETCONF protocol
31 | that allows the NETCONF client to control how default
32 | values are handled by the server in particular NETCONF
33 | operations.
34 |
35 | Copyright (c) 2011 IETF Trust and the persons identified as
36 | the document authors. All rights reserved.
37 |
38 | Redistribution and use in source and binary forms, with or
39 | without modification, is permitted pursuant to, and subject
40 | to the license terms contained in, the Simplified BSD License
41 | set forth in Section 4.c of the IETF Trust's Legal Provisions
42 | Relating to IETF Documents
43 | (http://trustee.ietf.org/license-info).
44 |
45 | This version of this YANG module is part of RFC 6243; see
46 | the RFC itself for full legal notices.";
47 | revision 2011-06-01 {
48 | description
49 | "Initial version.";
50 | reference
51 | "RFC 6243: With-defaults Capability for NETCONF";
52 | }
53 |
54 | typedef with-defaults-mode {
55 | description
56 | "Possible modes to report default data.";
57 | reference
58 | "RFC 6243; Section 3.";
59 | type enumeration {
60 | enum report-all {
61 | description
62 | "All default data is reported.";
63 | reference
64 | "RFC 6243; Section 3.1";
65 | }
66 | enum report-all-tagged {
67 | description
68 | "All default data is reported.
69 | Any nodes considered to be default data
70 | will contain a 'default' XML attribute,
71 | set to 'true' or '1'.";
72 | reference
73 | "RFC 6243; Section 3.4";
74 | }
75 | enum trim {
76 | description
77 | "Values are not reported if they contain the default.";
78 | reference
79 | "RFC 6243; Section 3.2";
80 | }
81 | enum explicit {
82 | description
83 | "Report values that contain the definition of
84 | explicitly set data.";
85 | reference
86 | "RFC 6243; Section 3.3";
87 | }
88 | }
89 | }
90 |
91 | grouping with-defaults-parameters {
92 | description
93 | "Contains the parameter for control
94 | of defaults in NETCONF retrieval operations.";
95 | leaf with-defaults {
96 | description
97 | "The explicit defaults processing mode requested.";
98 | reference
99 | "RFC 6243; Section 4.5.1";
100 |
101 | type with-defaults-mode;
102 | }
103 | }
104 |
105 | // extending the get-config operation
106 | augment /nc:get-config/nc:input {
107 | description
108 | "Adds the parameter to the
109 | input of the NETCONF operation.";
110 | reference
111 | "RFC 6243; Section 4.5.1";
112 |
113 | uses with-defaults-parameters;
114 | }
115 |
116 | // extending the get operation
117 | augment /nc:get/nc:input {
118 | description
119 | "Adds the parameter to
120 | the input of the NETCONF operation.";
121 | reference
122 | "RFC 6243; Section 4.5.1";
123 |
124 | uses with-defaults-parameters;
125 | }
126 |
127 | // extending the copy-config operation
128 | augment /nc:copy-config/nc:input {
129 | description
130 | "Adds the parameter to
131 | the input of the NETCONF operation.";
132 | reference
133 | "RFC 6243; Section 4.5.1";
134 |
135 | uses with-defaults-parameters;
136 | }
137 |
138 | }
139 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-origin.yang:
--------------------------------------------------------------------------------
1 | module ietf-origin {
2 | yang-version 1.1;
3 | namespace "urn:ietf:params:xml:ns:yang:ietf-origin";
4 | prefix or;
5 |
6 | import ietf-yang-metadata {
7 | prefix md;
8 | }
9 |
10 | organization
11 | "IETF Network Modeling (NETMOD) Working Group";
12 |
13 | contact
14 | "WG Web:
15 |
16 | WG List:
17 |
18 | Author: Martin Bjorklund
19 |
20 |
21 | Author: Juergen Schoenwaelder
22 |
23 |
24 | Author: Phil Shafer
25 |
26 |
27 | Author: Kent Watsen
28 |
29 |
30 | Author: Rob Wilton
31 | ";
32 |
33 | description
34 | "This YANG module defines an 'origin' metadata annotation and a
35 | set of identities for the origin value.
36 |
37 | Copyright (c) 2018 IETF Trust and the persons identified as
38 | authors of the code. All rights reserved.
39 |
40 | Redistribution and use in source and binary forms, with or
41 | without modification, is permitted pursuant to, and subject to
42 | the license terms contained in, the Simplified BSD License set
43 | forth in Section 4.c of the IETF Trust's Legal Provisions
44 | Relating to IETF Documents
45 | (https://trustee.ietf.org/license-info).
46 |
47 | This version of this YANG module is part of RFC 8342
48 | (https://www.rfc-editor.org/info/rfc8342); see the RFC itself
49 | for full legal notices.";
50 |
51 | revision 2018-02-14 {
52 | description
53 | "Initial revision.";
54 | reference
55 | "RFC 8342: Network Management Datastore Architecture (NMDA)";
56 | }
57 |
58 | /*
59 | * Identities
60 | */
61 |
62 | identity origin {
63 | description
64 | "Abstract base identity for the origin annotation.";
65 | }
66 |
67 | identity intended {
68 | base origin;
69 | description
70 | "Denotes configuration from the intended configuration
71 | datastore.";
72 | }
73 |
74 | identity dynamic {
75 | base origin;
76 | description
77 | "Denotes configuration from a dynamic configuration
78 | datastore.";
79 | }
80 |
81 | identity system {
82 | base origin;
83 | description
84 | "Denotes configuration originated by the system itself.
85 |
86 | Examples of system configuration include applied configuration
87 | for an always-existing loopback interface, or interface
88 | configuration that is auto-created due to the hardware
89 | currently present in the device.";
90 | }
91 |
92 | identity learned {
93 | base origin;
94 | description
95 | "Denotes configuration learned from protocol interactions with
96 | other devices, instead of via either the intended
97 | configuration datastore or any dynamic configuration
98 | datastore.
99 |
100 | Examples of protocols that provide learned configuration
101 | include link-layer negotiations, routing protocols, and
102 | DHCP.";
103 | }
104 |
105 | identity default {
106 | base origin;
107 | description
108 | "Denotes configuration that does not have a configured or
109 | learned value but has a default value in use. Covers both
110 | values defined in a 'default' statement and values defined
111 | via an explanation in a 'description' statement.";
112 | }
113 |
114 | identity unknown {
115 | base origin;
116 | description
117 | "Denotes configuration for which the system cannot identify the
118 | origin.";
119 | }
120 |
121 | /*
122 | * Type definitions
123 | */
124 |
125 | typedef origin-ref {
126 | type identityref {
127 | base origin;
128 | }
129 | description
130 | "An origin identity reference.";
131 | }
132 |
133 | /*
134 | * Metadata annotations
135 | */
136 |
137 | md:annotation origin {
138 | type origin-ref;
139 | description
140 | "The 'origin' annotation can be present on any configuration
141 | data node in the operational state datastore. It specifies
142 | from where the node originated. If not specified for a given
143 | configuration data node, then the origin is the same as the
144 | origin of its parent node in the data tree. The origin for
145 | any top-level configuration data nodes must be specified.";
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-pim-dm.yang:
--------------------------------------------------------------------------------
1 | module ietf-pim-dm {
2 | yang-version 1.1;
3 | namespace "urn:ietf:params:xml:ns:yang:ietf-pim-dm";
4 | prefix pim-dm;
5 |
6 | import ietf-routing {
7 | prefix rt;
8 | reference
9 | "RFC 8349: A YANG Data Model for Routing Management (NMDA
10 | Version)";
11 | }
12 | import ietf-pim-base {
13 | prefix pim-base;
14 | reference
15 | "RFC 9128: A YANG Data Model for Protocol Independent
16 | Multicast (PIM)";
17 | }
18 |
19 | organization
20 | "IETF PIM Working Group";
21 | contact
22 | "WG Web:
23 | WG List:
24 |
25 | Editor: Xufeng Liu
26 |
27 |
28 | Editor: Pete McAllister
29 |
30 |
31 | Editor: Anish Peter
32 |
33 |
34 | Editor: Mahesh Sivakumar
35 |
36 |
37 | Editor: Yisong Liu
38 |
39 |
40 | Editor: Fangwei Hu
41 | ";
42 | description
43 | "This YANG module defines a PIM (Protocol Independent Multicast)
44 | DM (Dense Mode) model.
45 |
46 | Copyright (c) 2022 IETF Trust and the persons identified as
47 | authors of the code. All rights reserved.
48 |
49 | Redistribution and use in source and binary forms, with or
50 | without modification, is permitted pursuant to, and subject to
51 | the license terms contained in, the Revised BSD License set
52 | forth in Section 4.c of the IETF Trust's Legal Provisions
53 | Relating to IETF Documents
54 | (https://trustee.ietf.org/license-info).
55 |
56 | This version of this YANG module is part of RFC 9128; see the
57 | RFC itself for full legal notices.";
58 |
59 | revision 2022-10-19 {
60 | description
61 | "Initial revision.";
62 | reference
63 | "RFC 9128: A YANG Data Model for Protocol Independent
64 | Multicast (PIM)";
65 | }
66 |
67 | /*
68 | * Configuration data nodes
69 | */
70 |
71 | augment "/rt:routing/rt:control-plane-protocols/"
72 | + "pim-base:pim/pim-base:address-family" {
73 | description
74 | "PIM-DM augmentation.";
75 | container dm {
76 | presence "Present to enable PIM-DM.";
77 | description
78 | "PIM-DM configuration data.";
79 | } // dm
80 | } // augment
81 |
82 | augment "/rt:routing/rt:control-plane-protocols/"
83 | + "pim-base:pim/pim-base:interfaces/pim-base:interface/"
84 | + "pim-base:address-family" {
85 | description
86 | "PIM-DM augmentation to 'pim-base:interface'.";
87 | container dm {
88 | presence "Present to enable PIM-DM.";
89 | description
90 | "PIM-DM configuration data.";
91 | } // dm
92 | } // augment
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-restconf-monitoring.yang:
--------------------------------------------------------------------------------
1 | module ietf-restconf-monitoring {
2 | namespace "urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring";
3 | prefix "rcmon";
4 |
5 | import ietf-yang-types { prefix yang; }
6 | import ietf-inet-types { prefix inet; }
7 |
8 | organization
9 | "IETF NETCONF (Network Configuration) Working Group";
10 |
11 | contact
12 | "WG Web:
13 | WG List:
14 |
15 | Author: Andy Bierman
16 |
17 |
18 | Author: Martin Bjorklund
19 |
20 |
21 | Author: Kent Watsen
22 | ";
23 |
24 | description
25 | "This module contains monitoring information for the
26 | RESTCONF protocol.
27 |
28 | Copyright (c) 2017 IETF Trust and the persons identified as
29 | authors of the code. All rights reserved.
30 |
31 | Redistribution and use in source and binary forms, with or
32 | without modification, is permitted pursuant to, and subject
33 | to the license terms contained in, the Simplified BSD License
34 | set forth in Section 4.c of the IETF Trust's Legal Provisions
35 | Relating to IETF Documents
36 | (http://trustee.ietf.org/license-info).
37 |
38 | This version of this YANG module is part of RFC 8040; see
39 | the RFC itself for full legal notices.";
40 |
41 | revision 2017-01-26 {
42 | description
43 | "Initial revision.";
44 | reference
45 | "RFC 8040: RESTCONF Protocol.";
46 | }
47 |
48 | container restconf-state {
49 | config false;
50 | description
51 | "Contains RESTCONF protocol monitoring information.";
52 |
53 | container capabilities {
54 | description
55 | "Contains a list of protocol capability URIs.";
56 |
57 | leaf-list capability {
58 | type inet:uri;
59 | description
60 | "A RESTCONF protocol capability URI.";
61 | }
62 | }
63 |
64 | container streams {
65 | description
66 | "Container representing the notification event streams
67 | supported by the server.";
68 | reference
69 | "RFC 5277, Section 3.4, element.";
70 |
71 | list stream {
72 | key name;
73 | description
74 | "Each entry describes an event stream supported by
75 | the server.";
76 |
77 | leaf name {
78 | type string;
79 | description
80 | "The stream name.";
81 | reference
82 | "RFC 5277, Section 3.4, element.";
83 | }
84 |
85 | leaf description {
86 | type string;
87 | description
88 | "Description of stream content.";
89 | reference
90 | "RFC 5277, Section 3.4, element.";
91 | }
92 |
93 | leaf replay-support {
94 | type boolean;
95 | default false;
96 | description
97 | "Indicates if replay buffer is supported for this stream.
98 | If 'true', then the server MUST support the 'start-time'
99 | and 'stop-time' query parameters for this stream.";
100 | reference
101 | "RFC 5277, Section 3.4, element.";
102 | }
103 |
104 | leaf replay-log-creation-time {
105 | when "../replay-support" {
106 | description
107 | "Only present if notification replay is supported.";
108 | }
109 | type yang:date-and-time;
110 | description
111 | "Indicates the time the replay log for this stream
112 | was created.";
113 | reference
114 | "RFC 5277, Section 3.4,
115 | element.";
116 | }
117 |
118 | list access {
119 | key encoding;
120 | min-elements 1;
121 | description
122 | "The server will create an entry in this list for each
123 | encoding format that is supported for this stream.
124 | The media type 'text/event-stream' is expected
125 | for all event streams. This list identifies the
126 | subtypes supported for this stream.";
127 |
128 | leaf encoding {
129 | type string;
130 | description
131 | "This is the secondary encoding format within the
132 | 'text/event-stream' encoding used by all streams.
133 | The type 'xml' is supported for XML encoding.
134 | The type 'json' is supported for JSON encoding.";
135 | }
136 |
137 | leaf location {
138 | type inet:uri;
139 | mandatory true;
140 | description
141 | "Contains a URL that represents the entry point
142 | for establishing notification delivery via
143 | server-sent events.";
144 | }
145 | }
146 | }
147 | }
148 | }
149 |
150 | }
151 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-restconf-subscribed-notifications.yang:
--------------------------------------------------------------------------------
1 | module ietf-restconf-subscribed-notifications {
2 | yang-version 1.1;
3 | namespace "urn:ietf:params:xml:ns:yang:"
4 | + "ietf-restconf-subscribed-notifications";
5 | prefix rsn;
6 |
7 | import ietf-subscribed-notifications {
8 | prefix sn;
9 | }
10 | import ietf-inet-types {
11 | prefix inet;
12 | }
13 |
14 | organization
15 | "IETF NETCONF (Network Configuration) Working Group";
16 | contact
17 | "WG Web:
18 | WG List:
19 |
20 | Editor: Eric Voit
21 |
22 |
23 | Editor: Alexander Clemm
24 |
25 |
26 | Editor: Reshad Rahman
27 | ";
28 | description
29 | "Defines RESTCONF as a supported transport for subscribed
30 | event notifications.
31 |
32 | Copyright (c) 2019 IETF Trust and the persons identified
33 | as authors of the code. All rights reserved.
34 |
35 | Redistribution and use in source and binary forms, with or
36 | without modification, is permitted pursuant to, and subject to
37 | the license terms contained in, the Simplified BSD License set
38 | forth in Section 4.c of the IETF Trust's Legal Provisions
39 | Relating to IETF Documents
40 | (https://trustee.ietf.org/license-info).
41 |
42 | This version of this YANG module is part of RFC 8650; see the
43 | RFC itself for full legal notices.";
44 |
45 | revision 2019-11-17 {
46 | description
47 | "Initial version";
48 | reference
49 | "RFC 8650: Dynamic Subscription to YANG Events and Datastores
50 | over RESTCONF";
51 | }
52 |
53 | grouping uri {
54 | description
55 | "Provides a reusable description of a URI.";
56 | leaf uri {
57 | type inet:uri;
58 | config false;
59 | description
60 | "Location of a subscription-specific URI on the publisher.";
61 | }
62 | }
63 |
64 | augment "/sn:establish-subscription/sn:output" {
65 | description
66 | "This augmentation allows RESTCONF-specific parameters for a
67 | response to a publisher's subscription request.";
68 | uses uri;
69 | }
70 |
71 | augment "/sn:subscriptions/sn:subscription" {
72 | description
73 | "This augmentation allows RESTCONF-specific parameters to be
74 | exposed for a subscription.";
75 | uses uri;
76 | }
77 |
78 | augment "/sn:subscription-modified" {
79 | description
80 | "This augmentation allows RESTCONF-specific parameters to be
81 | included as part of the notification that a subscription has
82 | been modified.";
83 | uses uri;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-segment-routing.yang:
--------------------------------------------------------------------------------
1 | module ietf-segment-routing {
2 | yang-version 1.1;
3 | namespace "urn:ietf:params:xml:ns:yang:ietf-segment-routing";
4 | prefix sr;
5 |
6 | import ietf-routing {
7 | prefix rt;
8 | reference "RFC 8349: A YANG Data Model for Routing
9 | Management (NMDA Version)";
10 | }
11 |
12 | organization
13 | "IETF SPRING - SPRING Working Group";
14 | contact
15 | "WG Web:
16 | WG List:
17 |
18 | Author: Stephane Litkowski
19 |
20 | Author: Yingzhen Qu
21 |
22 | Author: Acee Lindem
23 |
24 | Author: Pushpasis Sarkar
25 |
26 | Author: Jeff Tantsura
27 |
28 |
29 | ";
30 | description
31 | "This YANG module defines a generic framework for Segment
32 | Routing (SR). It is to be augmented by models for different
33 | SR data planes.
34 |
35 | This YANG module conforms to the Network Management
36 | Datastore Architecture (NMDA), as described in RFC 8242.
37 |
38 | The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL
39 | NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED',
40 | 'MAY', and 'OPTIONAL' in this document are to be interpreted as
41 | described in BCP 14 (RFC 2119) (RFC 8174) when, and only when,
42 | they appear in all capitals, as shown here.
43 |
44 | Copyright (c) 2021 IETF Trust and the persons identified as
45 | authors of the code. All rights reserved.
46 |
47 | Redistribution and use in source and binary forms, with or
48 | without modification, is permitted pursuant to, and subject
49 | to the license terms contained in, the Simplified BSD License
50 | set forth in Section 4.c of the IETF Trust's Legal Provisions
51 | Relating to IETF Documents
52 | (https://trustee.ietf.org/license-info).
53 |
54 | This version of this YANG module is part of RFC 9020;
55 | see the RFC itself for full legal notices.";
56 |
57 | reference
58 | "RFC 9020: YANG Data Model for Segment Routing.";
59 |
60 | revision 2021-05-26 {
61 | description
62 | "Initial version";
63 | reference
64 | "RFC 9020: YANG Data Model for Segment Routing.";
65 | }
66 |
67 | augment "/rt:routing" {
68 | description
69 | "This module augments the routing data model (RFC 8349)
70 | with Segment Routing (SR).";
71 | container segment-routing {
72 | description
73 | "Segment Routing configuration. This container
74 | is to be augmented by models for different SR
75 | data planes.";
76 | reference
77 | "RFC 8402: Segment Routing Architecture.";
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-snmp-engine.yang:
--------------------------------------------------------------------------------
1 | submodule ietf-snmp-engine {
2 |
3 | belongs-to ietf-snmp {
4 | prefix snmp;
5 | }
6 |
7 | import ietf-inet-types {
8 | prefix inet;
9 | }
10 |
11 | include ietf-snmp-common;
12 |
13 | organization
14 | "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
15 |
16 | contact
17 | "WG Web:
18 | WG List:
19 |
20 | WG Chair: Thomas Nadeau
21 |
22 |
23 | WG Chair: Juergen Schoenwaelder
24 |
25 |
26 | Editor: Martin Bjorklund
27 |
28 |
29 | Editor: Juergen Schoenwaelder
30 | ";
31 |
32 | description
33 | "This submodule contains a collection of YANG definitions
34 | for configuring SNMP engines.
35 |
36 | Copyright (c) 2014 IETF Trust and the persons identified as
37 | authors of the code. All rights reserved.
38 |
39 | Redistribution and use in source and binary forms, with or
40 | without modification, is permitted pursuant to, and subject
41 | to the license terms contained in, the Simplified BSD License
42 | set forth in Section 4.c of the IETF Trust's Legal Provisions
43 | Relating to IETF Documents
44 | (http://trustee.ietf.org/license-info).
45 | This version of this YANG module is part of RFC 7407; see
46 | the RFC itself for full legal notices.";
47 |
48 | revision 2014-12-10 {
49 | description
50 | "Initial revision.";
51 | reference
52 | "RFC 7407: A YANG Data Model for SNMP Configuration";
53 | }
54 |
55 | augment /snmp:snmp {
56 |
57 | container engine {
58 |
59 | description
60 | "Configuration of the SNMP engine.";
61 |
62 | leaf enabled {
63 | type boolean;
64 | default "false";
65 | description
66 | "Enables the SNMP engine.";
67 | }
68 |
69 | list listen {
70 | key "name";
71 | description
72 | "Configuration of the transport endpoints on which the
73 | engine listens.";
74 |
75 | leaf name {
76 | type snmp:identifier;
77 | description
78 | "An arbitrary name for the list entry.";
79 | }
80 |
81 | choice transport {
82 | mandatory true;
83 | description
84 | "The transport-protocol-specific parameters for this
85 | endpoint. Submodules providing configuration for
86 | additional transports are expected to augment this
87 | choice.";
88 | case udp {
89 | container udp {
90 | leaf ip {
91 | type inet:ip-address;
92 | mandatory true;
93 | description
94 | "The IPv4 or IPv6 address on which the engine
95 | listens.";
96 | }
97 | leaf port {
98 | type inet:port-number;
99 | description
100 | "The UDP port on which the engine listens.
101 |
102 | If the port is not configured, an engine that
103 | acts as a Command Responder uses port 161, and
104 | an engine that acts as a Notification Receiver
105 | uses port 162.";
106 | }
107 | }
108 | }
109 | }
110 | }
111 |
112 | container version {
113 | description
114 | "SNMP version used by the engine.";
115 | leaf v1 {
116 | type empty;
117 | }
118 | leaf v2c {
119 | type empty;
120 | }
121 | leaf v3 {
122 | type empty;
123 | }
124 | }
125 |
126 | leaf engine-id {
127 | type snmp:engine-id;
128 | description
129 | "The local SNMP engine's administratively assigned unique
130 | identifier.
131 |
132 | If this leaf is not set, the device automatically
133 | calculates an engine ID, as described in RFC 3411. A
134 | server MAY initialize this leaf with the automatically
135 | created value.";
136 | reference
137 | "RFC 3411: An Architecture for Describing Simple Network
138 | Management Protocol (SNMP) Management
139 | Frameworks.
140 | SNMP-FRAMEWORK-MIB.snmpEngineID";
141 | }
142 |
143 | leaf enable-authen-traps {
144 | type boolean;
145 | description
146 | "Indicates whether the SNMP entity is permitted to
147 | generate authenticationFailure traps.";
148 | reference
149 | "RFC 3418: Management Information Base (MIB) for the
150 | Simple Network Management Protocol (SNMP)
151 | SNMPv2-MIB.snmpEnableAuthenTraps";
152 | }
153 | }
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-snmp-proxy.yang:
--------------------------------------------------------------------------------
1 | submodule ietf-snmp-proxy {
2 |
3 | belongs-to ietf-snmp {
4 | prefix snmp;
5 | }
6 |
7 | include ietf-snmp-common;
8 | include ietf-snmp-target;
9 |
10 | organization
11 | "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
12 |
13 | contact
14 | "WG Web:
15 | WG List:
16 |
17 | WG Chair: Thomas Nadeau
18 |
19 |
20 | WG Chair: Juergen Schoenwaelder
21 |
22 |
23 | Editor: Martin Bjorklund
24 |
25 |
26 | Editor: Juergen Schoenwaelder
27 | ";
28 |
29 | description
30 | "This submodule contains a collection of YANG definitions
31 | for configuring SNMP proxies.
32 |
33 | Copyright (c) 2014 IETF Trust and the persons identified as
34 | authors of the code. All rights reserved.
35 |
36 | Redistribution and use in source and binary forms, with or
37 | without modification, is permitted pursuant to, and subject
38 | to the license terms contained in, the Simplified BSD License
39 | set forth in Section 4.c of the IETF Trust's Legal Provisions
40 | Relating to IETF Documents
41 | (http://trustee.ietf.org/license-info).
42 |
43 | This version of this YANG module is part of RFC 7407; see
44 | the RFC itself for full legal notices.";
45 | reference
46 | "RFC 3413: Simple Network Management Protocol (SNMP)
47 | Applications";
48 |
49 | revision 2014-12-10 {
50 | description
51 | "Initial revision.";
52 | reference
53 | "RFC 7407: A YANG Data Model for SNMP Configuration";
54 | }
55 |
56 | feature proxy {
57 | description
58 | "A server implements this feature if it can act as an
59 | SNMP proxy.";
60 | reference
61 | "RFC 3413: Simple Network Management Protocol (SNMP)
62 | Applications";
63 | }
64 |
65 | augment /snmp:snmp {
66 | if-feature snmp:proxy;
67 |
68 | list proxy {
69 | key name;
70 |
71 | description
72 | "List of proxy parameters.";
73 | reference
74 | "RFC 3413: Simple Network Management Protocol (SNMP).
75 | Applications.
76 | SNMP-PROXY-MIB.snmpProxyTable";
77 |
78 | leaf name {
79 | type snmp:identifier;
80 | description
81 | "Identifies the proxy parameter entry.";
82 | reference
83 | "RFC 3413: Simple Network Management Protocol (SNMP).
84 | Applications.
85 | SNMP-PROXY-MIB.snmpProxyName";
86 | }
87 | leaf type {
88 | type enumeration {
89 | enum read { value 1; }
90 | enum write { value 2; }
91 | enum trap { value 3; }
92 | enum inform { value 4; }
93 | }
94 | mandatory true;
95 | reference
96 | "RFC 3413: Simple Network Management Protocol (SNMP).
97 | Applications.
98 | SNMP-PROXY-MIB.snmpProxyType";
99 | }
100 | leaf context-engine-id {
101 | type snmp:engine-id;
102 | mandatory true;
103 | reference
104 | "RFC 3413: Simple Network Management Protocol (SNMP).
105 | Applications.
106 | SNMP-PROXY-MIB.snmpProxyContextEngineID";
107 | }
108 | leaf context-name {
109 | type snmp:context-name;
110 | reference
111 | "RFC 3413: Simple Network Management Protocol (SNMP).
112 | Applications.
113 | SNMP-PROXY-MIB.snmpProxyContextName";
114 | }
115 | leaf target-params-in {
116 | type snmp:identifier;
117 | description
118 | "The name of a target parameters list entry.
119 |
120 | Implementations MAY restrict the values of this
121 | leaf to be one of the available values of
122 | /snmp/target-params/name in a valid configuration.";
123 | reference
124 | "RFC 3413: Simple Network Management Protocol (SNMP).
125 | Applications.
126 | SNMP-PROXY-MIB.snmpProxyTargetParamsIn";
127 | }
128 | leaf single-target-out {
129 | when "../type = 'read' or ../type = 'write'";
130 | type snmp:identifier;
131 | description
132 | "Implementations MAY restrict the values of this leaf
133 | to be one of the available values of /snmp/target/name in
134 | a valid configuration.";
135 | reference
136 | "RFC 3413: Simple Network Management Protocol (SNMP).
137 | Applications.
138 | SNMP-PROXY-MIB.snmpProxySingleTargetOut";
139 | }
140 |
141 | leaf multiple-target-out {
142 | when "../type = 'trap' or ../type = 'inform'";
143 | type snmp:tag-value;
144 | description
145 | "Implementations MAY restrict the values of this leaf
146 | to be one of the available values of /snmp/target/tag in
147 | a valid configuration.";
148 | reference
149 | "RFC 3413: Simple Network Management Protocol (SNMP).
150 | Applications.
151 | SNMP-PROXY-MIB.snmpProxyMultipleTargetOut";
152 | }
153 | }
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-snmp-ssh.yang:
--------------------------------------------------------------------------------
1 | submodule ietf-snmp-ssh {
2 |
3 | belongs-to ietf-snmp {
4 | prefix snmp;
5 | }
6 |
7 | import ietf-inet-types {
8 | prefix inet;
9 | }
10 |
11 | include ietf-snmp-common;
12 | include ietf-snmp-engine;
13 | include ietf-snmp-target;
14 |
15 | organization
16 | "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
17 |
18 | contact
19 | "WG Web:
20 | WG List:
21 |
22 | WG Chair: Thomas Nadeau
23 |
24 |
25 | WG Chair: Juergen Schoenwaelder
26 |
27 |
28 | Editor: Martin Bjorklund
29 |
30 |
31 | Editor: Juergen Schoenwaelder
32 | ";
33 |
34 | description
35 | "This submodule contains a collection of YANG definitions for
36 | configuring the Secure Shell Transport Model (SSHTM)
37 | of SNMP.
38 |
39 | Copyright (c) 2014 IETF Trust and the persons identified as
40 | authors of the code. All rights reserved.
41 |
42 | Redistribution and use in source and binary forms, with or
43 | without modification, is permitted pursuant to, and subject
44 | to the license terms contained in, the Simplified BSD License
45 | set forth in Section 4.c of the IETF Trust's Legal Provisions
46 | Relating to IETF Documents
47 | (http://trustee.ietf.org/license-info).
48 |
49 | This version of this YANG module is part of RFC 7407; see
50 | the RFC itself for full legal notices.";
51 |
52 | reference
53 | "RFC 5592: Secure Shell Transport Model for the
54 | Simple Network Management Protocol (SNMP)";
55 |
56 | revision 2014-12-10 {
57 | description
58 | "Initial revision.";
59 | reference
60 | "RFC 7407: A YANG Data Model for SNMP Configuration";
61 | }
62 |
63 | feature sshtm {
64 | description
65 | "A server implements this feature if it supports the
66 | Secure Shell Transport Model for SNMP.";
67 | reference
68 | "RFC 5592: Secure Shell Transport Model for the
69 | Simple Network Management Protocol (SNMP)";
70 | }
71 |
72 | augment /snmp:snmp/snmp:engine/snmp:listen/snmp:transport {
73 | if-feature sshtm;
74 | case ssh {
75 | container ssh {
76 | description
77 | "The IPv4 or IPv6 address and port to which the
78 | engine listens for SNMP messages over SSH.";
79 |
80 | leaf ip {
81 | type inet:ip-address;
82 | mandatory true;
83 | description
84 | "The IPv4 or IPv6 address on which the engine listens
85 | for SNMP messages over SSH.";
86 | }
87 | leaf port {
88 | type inet:port-number;
89 | description
90 | "The TCP port on which the engine listens for SNMP
91 | messages over SSH.
92 |
93 | If the port is not configured, an engine that
94 | acts as a Command Responder uses port 5161, and
95 | an engine that acts as a Notification Receiver
96 | uses port 5162.";
97 | }
98 | }
99 | }
100 | }
101 |
102 | augment /snmp:snmp/snmp:target/snmp:transport {
103 | if-feature sshtm;
104 | case ssh {
105 | reference
106 | "RFC 5592: Secure Shell Transport Model for the
107 | Simple Network Management Protocol (SNMP).
108 | SNMP-SSH-TM-MIB.snmpSSHDomain";
109 | container ssh {
110 | leaf ip {
111 | type inet:host;
112 | mandatory true;
113 | reference
114 | "RFC 3413: Simple Network Management Protocol (SNMP).
115 | Applications.
116 | SNMP-TARGET-MIB.snmpTargetAddrTAddress
117 | RFC 5592: Secure Shell Transport Model for the
118 | Simple Network Management Protocol (SNMP).
119 | SNMP-SSH-TM-MIB.SnmpSSHAddress";
120 | }
121 | leaf port {
122 | type inet:port-number;
123 | default 5161;
124 | reference
125 | "RFC 3413: Simple Network Management Protocol (SNMP).
126 | Applications.
127 | SNMP-TARGET-MIB.snmpTargetAddrTAddress
128 | RFC 5592: Secure Shell Transport Model for the
129 | Simple Network Management Protocol (SNMP).
130 | SNMP-SSH-TM-MIB.SnmpSSHAddress";
131 | }
132 | leaf username {
133 | type string;
134 | reference
135 | "RFC 3413: Simple Network Management Protocol (SNMP).
136 | Applications.
137 | SNMP-TARGET-MIB.snmpTargetAddrTAddress
138 | RFC 5592: Secure Shell Transport Model for the
139 | Simple Network Management Protocol (SNMP).
140 | SNMP-SSH-TM-MIB.SnmpSSHAddress";
141 | }
142 | }
143 | }
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-snmp-tsm.yang:
--------------------------------------------------------------------------------
1 | submodule ietf-snmp-tsm {
2 |
3 | belongs-to ietf-snmp {
4 | prefix snmp;
5 | }
6 |
7 | include ietf-snmp-common;
8 | include ietf-snmp-target;
9 | include ietf-snmp-proxy;
10 |
11 | organization
12 | "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
13 |
14 | contact
15 | "WG Web:
16 | WG List:
17 |
18 | WG Chair: Thomas Nadeau
19 |
20 |
21 | WG Chair: Juergen Schoenwaelder
22 |
23 |
24 | Editor: Martin Bjorklund
25 |
26 |
27 | Editor: Juergen Schoenwaelder
28 | ";
29 |
30 | description
31 | "This submodule contains a collection of YANG definitions for
32 | configuring the Transport Security Model (TSM) of SNMP.
33 |
34 | Copyright (c) 2014 IETF Trust and the persons identified as
35 | authors of the code. All rights reserved.
36 |
37 | Redistribution and use in source and binary forms, with or
38 | without modification, is permitted pursuant to, and subject
39 | to the license terms contained in, the Simplified BSD License
40 | set forth in Section 4.c of the IETF Trust's Legal Provisions
41 | Relating to IETF Documents
42 | (http://trustee.ietf.org/license-info).
43 |
44 | This version of this YANG module is part of RFC 7407; see
45 | the RFC itself for full legal notices.";
46 |
47 | reference
48 | "RFC 5591: Transport Security Model for the
49 | Simple Network Management Protocol (SNMP)";
50 |
51 | revision 2014-12-10 {
52 | description
53 | "Initial revision.";
54 | reference
55 | "RFC 7407: A YANG Data Model for SNMP Configuration";
56 | }
57 |
58 | feature tsm {
59 | description
60 | "A server implements this feature if it supports the
61 | Transport Security Model for SNMP.";
62 | reference
63 | "RFC 5591: Transport Security Model for the
64 | Simple Network Management Protocol (SNMP)";
65 | }
66 | augment /snmp:snmp {
67 | if-feature tsm;
68 | container tsm {
69 | description
70 | "Configuration of the Transport Security Model.";
71 |
72 | leaf use-prefix {
73 | type boolean;
74 | default false;
75 | reference
76 | "RFC 5591: Transport Security Model for the Simple
77 | Network Management Protocol (SNMP).
78 | SNMP-TSM-MIB.snmpTsmConfigurationUsePrefix";
79 | }
80 | }
81 | }
82 |
83 | grouping tsm-target-params {
84 | container tsm {
85 | description
86 | "Transport-based security SNMPv3 parameters type.
87 |
88 | Represents snmpTargetParamsMPModel '3' and
89 | snmpTargetParamsSecurityModel '4'.";
90 | leaf security-name {
91 | type snmp:security-name;
92 | mandatory true;
93 | reference
94 | "RFC 3413: Simple Network Management Protocol (SNMP).
95 | Applications.
96 | SNMP-TARGET-MIB.snmpTargetParamsSecurityName";
97 | }
98 | leaf security-level {
99 | type snmp:security-level;
100 | mandatory true;
101 | reference
102 | "RFC 3413: Simple Network Management Protocol (SNMP).
103 | Applications.
104 | SNMP-TARGET-MIB.snmpTargetParamsSecurityLevel";
105 | }
106 | }
107 | }
108 |
109 | augment /snmp:snmp/snmp:target-params/snmp:params {
110 | if-feature tsm;
111 | case tsm {
112 | uses tsm-target-params;
113 | }
114 | }
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-snmp.yang:
--------------------------------------------------------------------------------
1 | module ietf-snmp {
2 |
3 | namespace "urn:ietf:params:xml:ns:yang:ietf-snmp";
4 | prefix snmp;
5 |
6 | include ietf-snmp-common {
7 | revision-date 2014-12-10;
8 | }
9 | include ietf-snmp-engine {
10 | revision-date 2014-12-10;
11 | }
12 | include ietf-snmp-target {
13 | revision-date 2014-12-10;
14 | }
15 | include ietf-snmp-notification {
16 | revision-date 2014-12-10;
17 | }
18 | include ietf-snmp-proxy {
19 | revision-date 2014-12-10;
20 | }
21 | include ietf-snmp-community {
22 | revision-date 2014-12-10;
23 | }
24 | include ietf-snmp-usm {
25 | revision-date 2014-12-10;
26 | }
27 | include ietf-snmp-tsm {
28 | revision-date 2014-12-10;
29 | }
30 | include ietf-snmp-vacm {
31 | revision-date 2014-12-10;
32 | }
33 | include ietf-snmp-tls {
34 | revision-date 2014-12-10;
35 | }
36 | include ietf-snmp-ssh {
37 | revision-date 2014-12-10;
38 | }
39 |
40 | organization
41 | "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
42 |
43 | contact
44 | "WG Web:
45 | WG List:
46 |
47 | WG Chair: Thomas Nadeau
48 |
49 |
50 | WG Chair: Juergen Schoenwaelder
51 |
52 |
53 | Editor: Martin Bjorklund
54 |
55 |
56 | Editor: Juergen Schoenwaelder
57 | ";
58 | description
59 | "This module contains a collection of YANG definitions for
60 | configuring SNMP engines.
61 |
62 | Copyright (c) 2014 IETF Trust and the persons identified as
63 | authors of the code. All rights reserved.
64 |
65 | Redistribution and use in source and binary forms, with or
66 | without modification, is permitted pursuant to, and subject
67 | to the license terms contained in, the Simplified BSD License
68 | set forth in Section 4.c of the IETF Trust's Legal Provisions
69 | Relating to IETF Documents
70 | (http://trustee.ietf.org/license-info).
71 |
72 | This version of this YANG module is part of RFC 7407; see
73 | the RFC itself for full legal notices.";
74 |
75 | revision 2014-12-10 {
76 | description
77 | "Initial revision.";
78 | reference
79 | "RFC 7407: A YANG Data Model for SNMP Configuration";
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-template.yang:
--------------------------------------------------------------------------------
1 | module ietf-template {
2 | yang-version 1.1;
3 |
4 | // replace this string with a unique namespace URN value
5 |
6 | namespace "urn:ietf:params:xml:ns:yang:ietf-template";
7 |
8 | // replace this string, and try to pick a unique prefix
9 |
10 | prefix temp;
11 |
12 | // import statements here: e.g.,
13 | // import ietf-yang-types { prefix yang; }
14 | // import ietf-inet-types { prefix inet; }
15 | // identify the IETF working group if applicable
16 |
17 | organization
18 | "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
19 |
20 | // update this contact statement with your info
21 |
22 | contact
23 | "WG Web:
24 | WG List:
25 |
26 | Editor: your-name
27 | ";
28 |
29 | // replace the first sentence in this description statement.
30 | // replace the copyright notice with the most recent
31 | // version, if it has been updated since the publication
32 | // of this document
33 |
34 | description
35 | "This module defines a template for other YANG modules.
36 |
37 | Copyright (c) IETF Trust and the persons
38 | identified as authors of the code. All rights reserved.
39 |
40 | Redistribution and use in source and binary forms, with or
41 | without modification, is permitted pursuant to, and subject
42 | to the license terms contained in, the Simplified BSD License
43 | set forth in Section 4.c of the IETF Trust's Legal Provisions
44 |
45 | Relating to IETF Documents
46 | (http://trustee.ietf.org/license-info).
47 |
48 | This version of this YANG module is part of RFC XXXX; see
49 | the RFC itself for full legal notices.";
50 |
51 | // RFC Ed.: replace XXXX with actual RFC number and remove
52 | // this note
53 |
54 | // replace '2016-03-20' with the module publication date
55 | // the format is (year-month-day)
56 |
57 | revision 2016-03-20 {
58 | description
59 | "what changed in this revision";
60 | reference "RFC XXXX: ";
61 | }
62 |
63 | // extension statements
64 | // feature statements
65 | // identity statements
66 | // typedef statements
67 | // grouping statements
68 | // data definition statements
69 | // augment statements
70 | // rpc statements
71 | // notification statements
72 | // DO NOT put deviation statements in a published module
73 | }
74 |
--------------------------------------------------------------------------------
/yang/ietf-rfc/ietf-yang-metadata.yang:
--------------------------------------------------------------------------------
1 | module ietf-yang-metadata {
2 |
3 | namespace "urn:ietf:params:xml:ns:yang:ietf-yang-metadata";
4 |
5 | prefix "md";
6 |
7 | organization
8 | "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
9 |
10 | contact
11 | "WG Web:
12 |
13 | WG List:
14 |
15 | WG Chair: Lou Berger
16 |
17 |
18 | WG Chair: Kent Watsen
19 |
20 |
21 | Editor: Ladislav Lhotka
22 | ";
23 |
24 | description
25 | "This YANG module defines an 'extension' statement that allows
26 | for defining metadata annotations.
27 |
28 | Copyright (c) 2016 IETF Trust and the persons identified as
29 | authors of the code. All rights reserved.
30 |
31 | Redistribution and use in source and binary forms, with or
32 | without modification, is permitted pursuant to, and subject to
33 | the license terms contained in, the Simplified BSD License set
34 | forth in Section 4.c of the IETF Trust's Legal Provisions
35 | Relating to IETF Documents
36 | (http://trustee.ietf.org/license-info).
37 |
38 | This version of this YANG module is part of RFC 7952
39 | (http://www.rfc-editor.org/info/rfc7952); see the RFC itself
40 | for full legal notices.";
41 |
42 | revision 2016-08-05 {
43 | description
44 | "Initial revision.";
45 | reference
46 | "RFC 7952: Defining and Using Metadata with YANG";
47 | }
48 |
49 | extension annotation {
50 | argument name;
51 | description
52 | "This extension allows for defining metadata annotations in
53 | YANG modules. The 'md:annotation' statement can appear only
54 | at the top level of a YANG module or submodule, i.e., it
55 | becomes a new alternative in the ABNF production rule for
56 | 'body-stmts' (Section 14 in RFC 7950).
57 |
58 | The argument of the 'md:annotation' statement defines the name
59 | of the annotation. Syntactically, it is a YANG identifier as
60 | defined in Section 6.2 of RFC 7950.
61 |
62 | An annotation defined with this 'extension' statement inherits
63 | the namespace and other context from the YANG module in which
64 | it is defined.
65 |
66 | The data type of the annotation value is specified in the same
67 | way as for a leaf data node using the 'type' statement.
68 |
69 | The semantics of the annotation and other documentation can be
70 | specified using the following standard YANG substatements (all
71 | are optional): 'description', 'if-feature', 'reference',
72 | 'status', and 'units'.
73 |
74 | A server announces support for a particular annotation by
75 | including the module in which the annotation is defined among
76 | the advertised YANG modules, e.g., in a NETCONF
77 | message or in the YANG library (RFC 7950). The annotation can
78 | then be attached to any instance of a data node defined in any
79 | YANG module that is advertised by the server.
80 |
81 | XML encoding and JSON encoding of annotations are defined in
82 | RFC 7952.";
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/ypath.go:
--------------------------------------------------------------------------------
1 | package restconf
2 |
3 | import (
4 | "embed"
5 |
6 | "github.com/freeconf/yang"
7 | "github.com/freeconf/yang/source"
8 | )
9 |
10 | //go:embed yang/*.yang
11 | var internal embed.FS
12 |
13 | // Access to fc-yang and fc-doc yang definitions.
14 | var InternalYPath = source.Any(yang.InternalYPath, source.EmbedDir(internal, "yang"))
15 |
16 | //go:embed yang/ietf-rfc/*.yang
17 | var internalIetf embed.FS
18 |
19 | // Access to IETF RFC yang definitions (as of 2023-12-29)
20 | var InternalIetfRfcYPath = source.EmbedDir(internalIetf, "yang/ietf-rfc")
21 |
--------------------------------------------------------------------------------