├── 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 | # ![FreeCONF](https://s3.amazonaws.com/freeconf-static/freeconf-no-wrench.svg) 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: `0`, 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 | --------------------------------------------------------------------------------