Building a relevant query

  • As a root ( use a Virtual machine) run cd /yangsuite/docker && ./start_yang_suite.sh and answer the question

  • Open web interface at https://localhost:8443/

  • Load your yang models via 'Setup' menu

  • Setup 'my_device' in Device profiles

  • Then click 'protocols', 'netconf'

  • Select your 'YANG set', select "Modules" of interest

  • "Netconf Operation" select 'get', device 'my_devise' from the previous steps

  • Click thorough items of your interest and click 'Build RPC', you should get something similar to the pictured below.

netconf getyang interfaces

Picture above in the result from the Drivenets YANG model.

Get a sample reply from a network device.

In many cases one can not perform queries from the yangsute directly to a device, therefore let’s do a query from a go(lang) code.

package main

import (
        "encoding/xml"
        "flag"
        "fmt"
        "log"

        "git.dev.as6453.net/gtac-systems/if-stats-snmp/internal/cfg"
        "github.com/Juniper/go-netconf/netconf"
        "golang.org/x/crypto/ssh"
)

func GetInterfaceData(s *netconf.Session, routerName string) error {
        request := `<?xml version="1.0"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101">
  <get>
    <filter>
      <drivenets-top xmlns="http://drivenets.com/ns/yang/dn-top">
        <interfaces xmlns="http://drivenets.com/ns/yang/dn-interfaces">
          <interface>
            <name/>
            <oper-items>
              <name/>
              <description/>
              <enabled/>
              <type/>
              <oper-status/>
              <interface-speed/>
              <if-index/>
              <counters>
                <ethernet-counters>
                  <rx-octets/>
                  <tx-octets/>
                </ethernet-counters>
                <fec-counters>
                  <fec-uncorrectable-words/>
                </fec-counters>
              </counters>
            </oper-items>
          </interface>
        </interfaces>
      </drivenets-top>
    </filter>
  </get>
</rpc>`
        s.Transport.Send([]byte(request))
        r, err := s.Transport.Receive()
        log.Printf("Answer is: %+v\n", string(r))
        return err
}

func main() {
        fname := flag.String("c", "/path/to/config.json", "path to config")
        flag.Parse()
        confParams := cfg.LoadConfiguration(*fname)
        fmt.Printf("%+v\n", confParams)
        var routerNames []string
        routerNames = flag.Args()
        netconfPort := confParams.RouterCredentials.NetconfPort

        sshConfig := &ssh.ClientConfig{
                User:            confParams.RouterCredentials.User,
                Auth:            []ssh.AuthMethod{ssh.Password(confParams.RouterCredentials.Password)},
                HostKeyCallback: ssh.InsecureIgnoreHostKey(),
        }

        for _, routerName := range routerNames {
                connectStr := routerName + ":" + netconfPort
                s, err := netconf.DialSSH(connectStr, sshConfig)
                log.Printf("Server Capabiliies: %v\n", s.ServerCapabilities)
                log.Printf("SessionID: %v\n", s.SessionID)
                defer s.Close()
                err = GetInterfaceData(s, routerName)
                if err != nil {
                        log.Printf("Error %v\n", err)
                }
                //log.Printf("%+v\n", interfaceData)
                //fmt.Println(s.ServerCapabilities)
                //fmt.Println(s.SessionID)
                //reply, err := s.Exec(netconf.MethodGetConfig("running"))
        }
}

SSH login/password is stored in the json file.

Once router quiried, there should be an XML answer back, something like:

<?xml version="1.0"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101">
  <data>
    <drivenets-top xmlns="http://drivenets.com/ns/yang/dn-top">
      <interfaces xmlns="http://drivenets.com/ns/yang/dn-interfaces">
        <interface>
          <name>ge400-0/0/12</name>
          <oper-items>
            <name>ge400-0/0/12</name>
            <description>SOME_CUSTOMER_LINK</description>
            <enabled>true</enabled>
            <oper-status>up</oper-status>
            <interface-speed>400000</interface-speed>
            <if-index>1</if-index>
            <type xmlns:iana-if-type="urn:ietf:params:xml:ns:yang:iana-if-type">iana-if-type:ethernetCsmacd</type>
            <counters>
              <ethernet-counters>
                <rx-octets>82498375161495453</rx-octets>
                <tx-octets>25852003909943900</tx-octets>
              </ethernet-counters>
              <fec-counters>
                <fec-uncorrectable-words>19</fec-uncorrectable-words>
              </fec-counters>
            </counters>
          </oper-items>
        </interface>
---- the rest is omited ----

Generate a go(lang) type definitions from the reply

Once reply is saved as a file, chidley https://github.com/gnewton/chidley can be used to generate go(lang) type definition. ~/chidley/chidley -X ./i-f.xml where i-f.xml in a file with the reply results.

One should get the file below (make sure to add package / import):

package main

import (
        "encoding/xml"
)

type Crpc_dash_reply__nc struct {
        XMLName             xml.Name `xml:"rpc-reply,omitempty" json:"rpc-reply,omitempty"`
        Attrmessage_dash_id string   `xml:"message-id,attr"  json:",omitempty"`
        AttrXmlnsnc         string   `xml:"xmlns nc,attr"  json:",omitempty"`
        Cdata               *Cdata   `xml:"data,omitempty" json:"data,omitempty"`
}

type Cdata struct {
        XMLName             xml.Name             `xml:"data,omitempty" json:"data,omitempty"`
        Cdrivenets_dash_top *Cdrivenets_dash_top `xml:"http://drivenets.com/ns/yang/dn-top drivenets-top,omitempty" json:"drivenets-top,omitempty"`
}

type Cdrivenets_dash_top struct {
        XMLName     xml.Name     `xml:"drivenets-top,omitempty" json:"drivenets-top,omitempty"`
        Attrxmlns   string       `xml:"xmlns,attr"  json:",omitempty"`
        Cinterfaces *Cinterfaces `xml:"http://drivenets.com/ns/yang/dn-interfaces interfaces,omitempty" json:"interfaces,omitempty"`
}

type Cinterfaces struct {
        XMLName    xml.Name      `xml:"interfaces,omitempty" json:"interfaces,omitempty"`
        Attrxmlns  string        `xml:"xmlns,attr"  json:",omitempty"`
        Cinterface []*Cinterface `xml:"http://drivenets.com/ns/yang/dn-interfaces interface,omitempty" json:"interface,omitempty"`
}

type Cinterface struct {
        XMLName          xml.Name          `xml:"interface,omitempty" json:"interface,omitempty"`
        Cname            *Cname            `xml:"http://drivenets.com/ns/yang/dn-interfaces name,omitempty" json:"name,omitempty"`
        Coper_dash_items *Coper_dash_items `xml:"http://drivenets.com/ns/yang/dn-interfaces oper-items,omitempty" json:"oper-items,omitempty"`
}

type Cname struct {
        XMLName xml.Name `xml:"name,omitempty" json:"name,omitempty"`
        Name    string   `xml:",chardata" json:",omitempty"`
}

type Coper_dash_items struct {
        XMLName                xml.Name                `xml:"oper-items,omitempty" json:"oper-items,omitempty"`
        Ccounters              *Ccounters              `xml:"http://drivenets.com/ns/yang/dn-interfaces counters,omitempty" json:"counters,omitempty"`
        Cdescription           *Cdescription           `xml:"http://drivenets.com/ns/yang/dn-interfaces description,omitempty" json:"description,omitempty"`
        Cenabled               *Cenabled               `xml:"http://drivenets.com/ns/yang/dn-interfaces enabled,omitempty" json:"enabled,omitempty"`
        Cif_dash_index         *Cif_dash_index         `xml:"http://drivenets.com/ns/yang/dn-interfaces if-index,omitempty" json:"if-index,omitempty"`
        Coper_dash_status      *Coper_dash_status      `xml:"http://drivenets.com/ns/yang/dn-interfaces oper-status,omitempty" json:"oper-status,omitempty"`
        Cparent_dash_interface *Cparent_dash_interface `xml:"http://drivenets.com/ns/yang/dn-interfaces parent-interface,omitempty" json:"parent-interface,omitempty"`
}

type Cdescription struct {
        XMLName     xml.Name `xml:"description,omitempty" json:"description,omitempty"`
        Description string   `xml:",chardata" json:",omitempty"`
}

type Cenabled struct {
        XMLName xml.Name `xml:"enabled,omitempty" json:"enabled,omitempty"`
        Enabled string   `xml:",chardata" json:",omitempty"`
}

type Cparent_dash_interface struct {
        XMLName         xml.Name `xml:"parent-interface,omitempty" json:"parent-interface,omitempty"`
        ParentInterface string   `xml:",chardata" json:",omitempty"`
}

type Coper_dash_status struct {
        XMLName    xml.Name `xml:"oper-status,omitempty" json:"oper-status,omitempty"`
        OperStatus string   `xml:",chardata" json:",omitempty"`
}

type Cif_dash_index struct {
        XMLName xml.Name `xml:"if-index,omitempty" json:"if-index,omitempty"`
        IfIndex string   `xml:",chardata" json:",omitempty"`
}

type Ccounters struct {
        XMLName                           xml.Name                           `xml:"counters,omitempty" json:"counters,omitempty"`
        Cethernet_dash_counters           *Cethernet_dash_counters           `xml:"http://drivenets.com/ns/yang/dn-interfaces ethernet-counters,omitempty" json:"ethernet-counters,omitempty"`
        Cethernet_dash_drop_dash_counters *Cethernet_dash_drop_dash_counters `xml:"http://drivenets.com/ns/yang/dn-interfaces ethernet-drop-counters,omitempty" json:"ethernet-drop-counters,omitempty"`
        Cfec_dash_counters                *Cfec_dash_counters                `xml:"http://drivenets.com/ns/yang/dn-interfaces fec-counters,omitempty" json:"fec-counters,omitempty"`
}

type Cethernet_dash_counters struct {
        XMLName         xml.Name         `xml:"ethernet-counters,omitempty" json:"ethernet-counters,omitempty"`
        Crx_dash_octets *Crx_dash_octets `xml:"http://drivenets.com/ns/yang/dn-interfaces rx-octets,omitempty" json:"rx-octets,omitempty"`
        Ctx_dash_octets *Ctx_dash_octets `xml:"http://drivenets.com/ns/yang/dn-interfaces tx-octets,omitempty" json:"tx-octets,omitempty"`
}

type Crx_dash_octets struct {
        XMLName  xml.Name `xml:"rx-octets,omitempty" json:"rx-octets,omitempty"`
        RxOctets string   `xml:",chardata" json:",omitempty"`
}

type Ctx_dash_octets struct {
        XMLName  xml.Name `xml:"tx-octets,omitempty" json:"tx-octets,omitempty"`
        TxOctets string   `xml:",chardata" json:",omitempty"`
}

type Cfec_dash_counters struct {
        XMLName                            xml.Name                            `xml:"fec-counters,omitempty" json:"fec-counters,omitempty"`
        Cfec_dash_uncorrectable_dash_words *Cfec_dash_uncorrectable_dash_words `xml:"http://drivenets.com/ns/yang/dn-interfaces fec-uncorrectable-words,omitempty" json:"fec-uncorrectable-words,omitempty"`
}

type Cfec_dash_uncorrectable_dash_words struct {
        XMLName               xml.Name `xml:"fec-uncorrectable-words,omitempty" json:"fec-uncorrectable-words,omitempty"`
        FecUncorrectableWords string   `xml:",chardata" json:",omitempty"`
}

type Cethernet_dash_drop_dash_counters struct {
        XMLName                  xml.Name                  `xml:"ethernet-drop-counters,omitempty" json:"ethernet-drop-counters,omitempty"`
        Crx_dash_errors          *Crx_dash_errors          `xml:"http://drivenets.com/ns/yang/dn-interfaces rx-errors,omitempty" json:"rx-errors,omitempty"`
        Crx_dash_fcs_dash_errors *Crx_dash_fcs_dash_errors `xml:"http://drivenets.com/ns/yang/dn-interfaces rx-fcs-errors,omitempty" json:"rx-fcs-errors,omitempty"`
        Ctx_dash_errors          *Ctx_dash_errors          `xml:"http://drivenets.com/ns/yang/dn-interfaces tx-errors,omitempty" json:"tx-errors,omitempty"`
}

type Crx_dash_errors struct {
        XMLName  xml.Name `xml:"rx-errors,omitempty" json:"rx-errors,omitempty"`
        RxErrors string   `xml:",chardata" json:",omitempty"`
}

type Crx_dash_fcs_dash_errors struct {
        XMLName     xml.Name `xml:"rx-fcs-errors,omitempty" json:"rx-fcs-errors,omitempty"`
        RxFcsErrors string   `xml:",chardata" json:",omitempty"`
}

type Ctx_dash_errors struct {
        XMLName  xml.Name `xml:"tx-errors,omitempty" json:"tx-errors,omitempty"`
        TxErrors string   `xml:",chardata" json:",omitempty"`
}

save all above as interfaces-structs.go

Finalize the program

package main

import (
        "encoding/xml"
        "flag"
        "fmt"
        "log"

        "git.dev.as6453.net/gtac-systems/if-stats-snmp/internal/cfg"
        "github.com/Juniper/go-netconf/netconf"
        "golang.org/x/crypto/ssh"
)

func GetInterfaceData(s *netconf.Session, routerName string) error {
        //var allInterfaces AllInterfaces
        var rpcReply Crpc_dash_reply__nc
        request := `<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101">
  <get>
    <filter>
      <drivenets-top xmlns="http://drivenets.com/ns/yang/dn-top">
        <interfaces xmlns="http://drivenets.com/ns/yang/dn-interfaces">
          <interface>
            <name/>
            <oper-items>
              <description/>
              <enabled/>
              <parent-interface/>
              <oper-status/>
              <if-index/>
              <counters>
                <ethernet-counters>
                  <rx-octets/>
                  <tx-octets/>
                </ethernet-counters>
                <fec-counters>
                  <fec-uncorrectable-words/>
                </fec-counters>
                <ethernet-drop-counters>
                  <rx-errors/>
                  <rx-fcs-errors/>
                  <tx-errors/>
                </ethernet-drop-counters>
              </counters>
            </oper-items>
          </interface>
        </interfaces>
      </drivenets-top>
    </filter>
  </get>
</rpc>`
        s.Transport.Send([]byte(request))
        r, err := s.Transport.Receive()
        log.Printf("Answer is: %+v\n", string(r))

        err = xml.Unmarshal([]byte(r), &rpcReply)
        //log.Printf("rpcReply unmarshaled: %+v, error: %v\n", rpcReply, err)
        log.Printf("rpcReply Cdata: %+v", rpcReply.Cdata.Cdrivenets_dash_top.Cinterfaces.Cinterface)
        for k, v := range rpcReply.Cdata.Cdrivenets_dash_top.Cinterfaces.Cinterface {
                log.Printf("interface data for %#v is %#v\n", k, v)
                log.Printf("interface Name for %#v is %#v\n", k, v.Cname.Name)
                log.Printf("IfIndex for %#v is %#v\n", k, v.Coper_dash_items.Cif_dash_index.IfIndex)
                if v.Coper_dash_items.Coper_dash_status != nil {
                        log.Printf("OperStatus for %#v is %#v\n", k, v.Coper_dash_items.Coper_dash_status.OperStatus)
                }
                if v.Coper_dash_items.Ccounters.Cethernet_dash_drop_dash_counters != nil {
                        if v.Coper_dash_items.Ccounters.Cethernet_dash_drop_dash_counters.Crx_dash_errors != nil {
                                log.Printf("RxErrors for %#v is %#v\n", k, v.Coper_dash_items.Ccounters.Cethernet_dash_drop_dash_counters.Crx_dash_errors.RxErrors)
                                log.Printf("TxErrors for %#v is %#v\n", k, v.Coper_dash_items.Ccounters.Cethernet_dash_drop_dash_counters.Ctx_dash_errors.TxErrors)
                        }
                        if v.Coper_dash_items.Ccounters.Cethernet_dash_drop_dash_counters.Crx_dash_fcs_dash_errors != nil {
                                log.Printf("TxErrors for %#v is %#v\n", k, v.Coper_dash_items.Ccounters.Cethernet_dash_drop_dash_counters.Crx_dash_fcs_dash_errors.RxFcsErrors)
                        }
                }
                if v.Coper_dash_items.Ccounters.Cethernet_dash_counters != nil {
                        if v.Coper_dash_items.Ccounters.Cethernet_dash_counters.Crx_dash_octets != nil {
                                log.Printf("RxOctets for %#v is %#v\n", k, v.Coper_dash_items.Ccounters.Cethernet_dash_counters.Crx_dash_octets.RxOctets)
                                log.Printf("TxOctets for %#v is %#v\n", k, v.Coper_dash_items.Ccounters.Cethernet_dash_counters.Ctx_dash_octets.TxOctets)
                        }
                }
                if v.Coper_dash_items.Ccounters.Cfec_dash_counters.Cfec_dash_uncorrectable_dash_words != nil {
                        log.Printf("FecUncorrectableWords for %#v is %#v\n", k, v.Coper_dash_items.Ccounters.Cfec_dash_counters.Cfec_dash_uncorrectable_dash_words.FecUncorrectableWords)
                }
                log.Printf("interface data for %#v is %#v\n", k, v.Coper_dash_items.Cparent_dash_interface)
        }
        return err
}

func main() {
        fname := flag.String("c", "/path/to/config.json", "path to config")
        flag.Parse()
        confParams := cfg.LoadConfiguration(*fname)
        fmt.Printf("%+v\n", confParams)
        var routerNames []string
        routerNames = flag.Args()
        netconfPort := confParams.RouterCredentials.NetconfPort

        sshConfig := &ssh.ClientConfig{
                User:            confParams.RouterCredentials.User,
                Auth:            []ssh.AuthMethod{ssh.Password(confParams.RouterCredentials.Password)},
                HostKeyCallback: ssh.InsecureIgnoreHostKey(),
        }

        for _, routerName := range routerNames {
                connectStr := routerName + ":" + netconfPort
                s, err := netconf.DialSSH(connectStr, sshConfig)
                log.Printf("Server Capabiliies: %v\n", s.ServerCapabilities)
                log.Printf("SessionID: %v\n", s.SessionID)
                defer s.Close()
                err = GetInterfaceData(s, routerName)
                if err != nil {
                        log.Printf("Error %v\n", err)
                }
                //log.Printf("%+v\n", interfaceData)
                //fmt.Println(s.ServerCapabilities)
                //fmt.Println(s.SessionID)
                //reply, err := s.Exec(netconf.MethodGetConfig("running"))
        }

}

Results

Run ./netconf-if-stats -c ./route.json 192.168.7.19

Results for the first three interfaces.

2024/09/24 08:23:58 interface Name for 0 is "ge400-0/0/12"
2024/09/24 08:23:58 IfIndex for 0 is "1"
2024/09/24 08:23:58 OperStatus for 0 is "up"
2024/09/24 08:23:58 RxErrors for 0 is "0"
2024/09/24 08:23:58 TxErrors for 0 is "0"
2024/09/24 08:23:58 TxErrors for 0 is "0"
2024/09/24 08:23:58 RxOctets for 0 is "85490659663335152"
2024/09/24 08:23:58 TxOctets for 0 is "27402847406025900"
2024/09/24 08:23:58 FecUncorrectableWords for 0 is "19"
2024/09/24 08:23:58 interface Name for 1 is "ge400-0/0/23"
2024/09/24 08:23:58 IfIndex for 1 is "2"
2024/09/24 08:23:58 OperStatus for 1 is "up"
2024/09/24 08:23:58 RxErrors for 1 is "25384"
2024/09/24 08:23:58 TxErrors for 1 is "0"
2024/09/24 08:23:58 TxErrors for 1 is "25368"
2024/09/24 08:23:58 RxOctets for 1 is "39374827552939581"
2024/09/24 08:23:58 TxOctets for 1 is "63651355058334264"
2024/09/24 08:23:58 interface Name for 2 is "ge400-0/0/16"
2024/09/24 08:23:58 IfIndex for 2 is "3"
2024/09/24 08:23:58 OperStatus for 2 is "up"
2024/09/24 08:23:58 RxErrors for 2 is "917348"
2024/09/24 08:23:58 TxErrors for 2 is "0"
2024/09/24 08:23:58 TxErrors for 2 is "917342"
2024/09/24 08:23:58 RxOctets for 2 is "60103917409819006"
2024/09/24 08:23:58 TxOctets for 2 is "42222945991248034"
2024/09/24 08:23:58 interface Name for 3 is "ge400-0/0/32"
2024/09/24 08:23:58 IfIndex for 3 is "4"
2024/09/24 08:23:58 OperStatus for 3 is "down"
2024/09/24 08:23:58 RxErrors for 3 is "0"
2024/09/24 08:23:58 TxErrors for 3 is "0"
2024/09/24 08:23:58 TxErrors for 3 is "0"
2024/09/24 08:23:58 RxOctets for 3 is "0"
2024/09/24 08:23:58 TxOctets for 3 is "0"