Commit f03f3338 authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

Hackfest:

- added basic Netconf/OpenConfig server
parent 2fedcee6
Loading
Loading
Loading
Loading
+82 −0
Original line number Diff line number Diff line
# NETCONF TFS exercise

## 1. Interrogate Topology

```bash
# Configure basic NetConf/OpenConfig server

mkdir -p ~/yang-netconf/netconf-tfs/{git_openconfig,openconfig}
git clone https://github.com/openconfig/public.git ~/yang-netconf/netconf-tfs/git_openconfig

cd ~/yang-netconf/netconf-tfs

export PYBINDPLUGIN=`/usr/bin/env python -c 'import pyangbind; import os; print ("{}/plugin".format(os.path.dirname(pyangbind.__file__)))'`
pyang --plugindir $PYBINDPLUGIN -p git_openconfig/release/models/ -f pybind --split-class-dir openconfig \
    git_openconfig/release/models/interfaces/openconfig-interfaces.yang \
    git_openconfig/release/models/interfaces/openconfig-if-ethernet.yang \
    git_openconfig/release/models/platform/openconfig-platform.yang \
    git_openconfig/release/models/platform/openconfig-platform-port.yang

python3 serverOpenConfig.py

# Terminal 2 (client)
cd ~/yang-netconf/netconf
python3 clientTopology.py
```

### 2.2. Report a sample message following Topology YANG data model
```bash
mkdir -p sample-xml-skeleton
pyang -f sample-xml-skeleton --sample-xml-skeleton-annotations topology.yang -o sample-xml-skeleton/topology.xml
```

### 2.3. Convert Topology YANG to UML
```bash
mkdir -p uml
pyang -f uml topology.yang -o uml/topology.uml
java -jar plantuml.jar uml/topology.uml
```

### 2.4. Convert Topology YANG to Python bindings
```bash
export PYBINDPLUGIN=`/usr/bin/env python -c 'import pyangbind; import os; print ("{}/plugin".format(os.path.dirname(pyangbind.__file__)))'`
pyang -f pybind topology.yang --plugindir $PYBINDPLUGIN -o binding_topology.py
```

### 2.5. Test creation of JSON-/XML-encoded Topology messages
```bash
python topology.py
```


## 3. Connection YANG data model (exercise solution)

### 3.1. Convert Connection YANG to Text-based tree
```bash
mkdir -p tree
pyang -f tree connection.yang -o tree/connection.txt
```

### 3.2. Report a sample message following Connection YANG data model
```bash
mkdir -p sample-xml-skeleton
pyang -f sample-xml-skeleton --sample-xml-skeleton-annotations connection.yang -o sample-xml-skeleton/connection.xml
```

### 3.3. Convert Connection YANG to UML
```bash
mkdir -p uml
pyang -f uml connection.yang -o uml/connection.uml
java -jar plantuml.jar uml/connection.uml
```

### 3.4. Convert Connection YANG to Python bindings
```bash
export PYBINDPLUGIN=`/usr/bin/env python -c 'import pyangbind; import os; print ("{}/plugin".format(os.path.dirname(pyangbind.__file__)))'`
pyang -f pybind connection.yang --plugindir $PYBINDPLUGIN -o binding_connection.py
```

### 3.5. Test creation of JSON-/XML-encoded Connection messages
```bash
python topology.py
```
+24 −0
Original line number Diff line number Diff line
from lxml import etree
from netconf import util
from pyangbind.lib.serialise import pybindIETFXMLEncoder

from openconfig.components import components
from openconfig.interfaces import interfaces

def get_device_definition():
    occ = components()
    port_1_1 = occ.component.add("1/1")
    port_1_1.state._set_type("PORT") # state.type is read-only; use special method to set it

    port_1_2 = occ.component.add("1/2")
    port_1_2.state._set_type("PORT") # state.type is read-only; use special method to set it

    port_1_3 = occ.component.add("1/3")
    port_1_3.state._set_type("PORT") # state.type is read-only; use special method to set it

    ocif = interfaces()

    data = util.elm("nc:data")
    data.append(etree.XML(pybindIETFXMLEncoder.serialise(occ)))
    data.append(etree.XML(pybindIETFXMLEncoder.serialise(ocif)))
    return data
+220 −0
Original line number Diff line number Diff line
<?xml version='1.0' encoding='UTF-8'?>
<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <interfaces xmlns="http://openconfig.net/yang/interfaces">
    <interface>
      <!-- # keys: name-->
      <!-- # entries: 0.. -->
      <name>
        <!-- type: leafref -->
      </name>
      <config>
        <name>
          <!-- type: string -->
        </name>
        <type>
          <!-- type: identityref -->
        </type>
        <mtu>
          <!-- type: uint16 -->
        </mtu>
        <description>
          <!-- type: string -->
        </description>
      </config>
      <state>
        <name>
          <!-- type: string -->
        </name>
        <type>
          <!-- type: identityref -->
        </type>
        <mtu>
          <!-- type: uint16 -->
        </mtu>
        <description>
          <!-- type: string -->
        </description>
        <ifindex>
          <!-- type: uint32 -->
        </ifindex>
        <admin-status>
          <!-- type: enumeration -->
        </admin-status>
        <oper-status>
          <!-- type: enumeration -->
        </oper-status>
        <last-change>
          <!-- type: oc-types:timeticks64 -->
        </last-change>
        <logical>
          <!-- type: boolean -->
        </logical>
        <management>
          <!-- type: boolean -->
        </management>
        <cpu>
          <!-- type: boolean -->
        </cpu>
        <counters>
          <in-octets>
            <!-- type: oc-yang:counter64 -->
          </in-octets>
          <in-pkts>
            <!-- type: oc-yang:counter64 -->
          </in-pkts>
          <in-unicast-pkts>
            <!-- type: oc-yang:counter64 -->
          </in-unicast-pkts>
          <in-broadcast-pkts>
            <!-- type: oc-yang:counter64 -->
          </in-broadcast-pkts>
          <in-multicast-pkts>
            <!-- type: oc-yang:counter64 -->
          </in-multicast-pkts>
          <in-discards>
            <!-- type: oc-yang:counter64 -->
          </in-discards>
          <in-errors>
            <!-- type: oc-yang:counter64 -->
          </in-errors>
          <in-unknown-protos>
            <!-- type: oc-yang:counter64 -->
          </in-unknown-protos>
          <in-fcs-errors>
            <!-- type: oc-yang:counter64 -->
          </in-fcs-errors>
          <out-octets>
            <!-- type: oc-yang:counter64 -->
          </out-octets>
          <out-pkts>
            <!-- type: oc-yang:counter64 -->
          </out-pkts>
          <out-unicast-pkts>
            <!-- type: oc-yang:counter64 -->
          </out-unicast-pkts>
          <out-broadcast-pkts>
            <!-- type: oc-yang:counter64 -->
          </out-broadcast-pkts>
          <out-multicast-pkts>
            <!-- type: oc-yang:counter64 -->
          </out-multicast-pkts>
          <out-discards>
            <!-- type: oc-yang:counter64 -->
          </out-discards>
          <out-errors>
            <!-- type: oc-yang:counter64 -->
          </out-errors>
          <carrier-transitions>
            <!-- type: oc-yang:counter64 -->
          </carrier-transitions>
          <last-clear>
            <!-- type: oc-types:timeticks64 -->
          </last-clear>
        </counters>
      </state>
      <hold-time>
        <config/>
        <state/>
      </hold-time>
      <subinterfaces>
        <subinterface>
          <!-- # keys: index-->
          <!-- # entries: 0.. -->
          <index>
            <!-- type: leafref -->
          </index>
          <config>
            <description>
              <!-- type: string -->
            </description>
          </config>
          <state>
            <description>
              <!-- type: string -->
            </description>
            <name>
              <!-- type: string -->
            </name>
            <ifindex>
              <!-- type: uint32 -->
            </ifindex>
            <admin-status>
              <!-- type: enumeration -->
            </admin-status>
            <oper-status>
              <!-- type: enumeration -->
            </oper-status>
            <last-change>
              <!-- type: oc-types:timeticks64 -->
            </last-change>
            <logical>
              <!-- type: boolean -->
            </logical>
            <management>
              <!-- type: boolean -->
            </management>
            <cpu>
              <!-- type: boolean -->
            </cpu>
            <counters>
              <in-octets>
                <!-- type: oc-yang:counter64 -->
              </in-octets>
              <in-pkts>
                <!-- type: oc-yang:counter64 -->
              </in-pkts>
              <in-unicast-pkts>
                <!-- type: oc-yang:counter64 -->
              </in-unicast-pkts>
              <in-broadcast-pkts>
                <!-- type: oc-yang:counter64 -->
              </in-broadcast-pkts>
              <in-multicast-pkts>
                <!-- type: oc-yang:counter64 -->
              </in-multicast-pkts>
              <in-discards>
                <!-- type: oc-yang:counter64 -->
              </in-discards>
              <in-errors>
                <!-- type: oc-yang:counter64 -->
              </in-errors>
              <in-unknown-protos>
                <!-- type: oc-yang:counter64 -->
              </in-unknown-protos>
              <in-fcs-errors>
                <!-- type: oc-yang:counter64 -->
              </in-fcs-errors>
              <out-octets>
                <!-- type: oc-yang:counter64 -->
              </out-octets>
              <out-pkts>
                <!-- type: oc-yang:counter64 -->
              </out-pkts>
              <out-unicast-pkts>
                <!-- type: oc-yang:counter64 -->
              </out-unicast-pkts>
              <out-broadcast-pkts>
                <!-- type: oc-yang:counter64 -->
              </out-broadcast-pkts>
              <out-multicast-pkts>
                <!-- type: oc-yang:counter64 -->
              </out-multicast-pkts>
              <out-discards>
                <!-- type: oc-yang:counter64 -->
              </out-discards>
              <out-errors>
                <!-- type: oc-yang:counter64 -->
              </out-errors>
              <carrier-transitions>
                <!-- type: oc-yang:counter64 -->
              </carrier-transitions>
              <last-clear>
                <!-- type: oc-types:timeticks64 -->
              </last-clear>
            </counters>
          </state>
        </subinterface>
      </subinterfaces>
    </interface>
  </interfaces>
</data>
+399 −0

File added.

Preview size limit exceeded, changes collapsed.

+208 −0
Original line number Diff line number Diff line
# -*- coding: utf-8 -*-
from operator import attrgetter
from pyangbind.lib.yangtypes import RestrictedPrecisionDecimalType
from pyangbind.lib.yangtypes import RestrictedClassType
from pyangbind.lib.yangtypes import TypedListType
from pyangbind.lib.yangtypes import YANGBool
from pyangbind.lib.yangtypes import YANGListType
from pyangbind.lib.yangtypes import YANGDynClass
from pyangbind.lib.yangtypes import ReferenceType
from pyangbind.lib.base import PybindBase
from collections import OrderedDict
from decimal import Decimal
from bitarray import bitarray
import six

# PY3 support of some PY2 keywords (needs improved)
if six.PY3:
  import builtins as __builtin__
  long = int
elif six.PY2:
  import __builtin__

from . import component
class components(PybindBase):
  """
  This class was auto-generated by the PythonClass plugin for PYANG
  from YANG module openconfig-platform - based on the path /components. Each member element of
  the container is represented as a class variable - with a specific
  YANG type.

  YANG Description: Enclosing container for the components in the system.
  """
  __slots__ = ('_path_helper', '_extmethods', '__component',)

  _yang_name = 'components'
  _yang_namespace = 'http://openconfig.net/yang/platform'

  _pybind_generated_by = 'container'

  def __init__(self, *args, **kwargs):

    self._path_helper = False

    self._extmethods = False
    self.__component = YANGDynClass(base=YANGListType("name",component.component, yang_name="component", parent=self, is_container='list', user_ordered=False, path_helper=self._path_helper, yang_keys='name', extensions=None), is_container='list', yang_name="component", parent=self, path_helper=self._path_helper, extmethods=self._extmethods, register_paths=True, extensions=None, namespace='http://openconfig.net/yang/platform', defining_module='openconfig-platform', yang_type='list', is_config=True)

    load = kwargs.pop("load", None)
    if args:
      if len(args) > 1:
        raise TypeError("cannot create a YANG container with >1 argument")
      all_attr = True
      for e in self._pyangbind_elements:
        if not hasattr(args[0], e):
          all_attr = False
          break
      if not all_attr:
        raise ValueError("Supplied object did not have the correct attributes")
      for e in self._pyangbind_elements:
        nobj = getattr(args[0], e)
        if nobj._changed() is False:
          continue
        setmethod = getattr(self, "_set_%s" % e)
        if load is None:
          setmethod(getattr(args[0], e))
        else:
          setmethod(getattr(args[0], e), load=load)

  def _path(self):
    if hasattr(self, "_parent"):
      return self._parent._path()+[self._yang_name]
    else:
      return ['components']

  def _get_component(self):
    """
    Getter method for component, mapped from YANG variable /components/component (list)

    YANG Description: List of components, keyed by component name.
    """
    return self.__component
      
  def _set_component(self, v, load=False):
    """
    Setter method for component, mapped from YANG variable /components/component (list)
    If this variable is read-only (config: false) in the
    source YANG file, then _set_component is considered as a private
    method. Backends looking to populate this variable should
    do so via calling thisObj._set_component() directly.

    YANG Description: List of components, keyed by component name.
    """
    if hasattr(v, "_utype"):
      v = v._utype(v)
    try:
      t = YANGDynClass(v,base=YANGListType("name",component.component, yang_name="component", parent=self, is_container='list', user_ordered=False, path_helper=self._path_helper, yang_keys='name', extensions=None), is_container='list', yang_name="component", parent=self, path_helper=self._path_helper, extmethods=self._extmethods, register_paths=True, extensions=None, namespace='http://openconfig.net/yang/platform', defining_module='openconfig-platform', yang_type='list', is_config=True)
    except (TypeError, ValueError):
      raise ValueError({
          'error-string': """component must be of a type compatible with list""",
          'defined-type': "list",
          'generated-type': """YANGDynClass(base=YANGListType("name",component.component, yang_name="component", parent=self, is_container='list', user_ordered=False, path_helper=self._path_helper, yang_keys='name', extensions=None), is_container='list', yang_name="component", parent=self, path_helper=self._path_helper, extmethods=self._extmethods, register_paths=True, extensions=None, namespace='http://openconfig.net/yang/platform', defining_module='openconfig-platform', yang_type='list', is_config=True)""",
        })

    self.__component = t
    if hasattr(self, '_set'):
      self._set()

  def _unset_component(self):
    self.__component = YANGDynClass(base=YANGListType("name",component.component, yang_name="component", parent=self, is_container='list', user_ordered=False, path_helper=self._path_helper, yang_keys='name', extensions=None), is_container='list', yang_name="component", parent=self, path_helper=self._path_helper, extmethods=self._extmethods, register_paths=True, extensions=None, namespace='http://openconfig.net/yang/platform', defining_module='openconfig-platform', yang_type='list', is_config=True)

  component = __builtin__.property(_get_component, _set_component)


  _pyangbind_elements = OrderedDict([('component', component), ])


from . import component
class components(PybindBase):
  """
  This class was auto-generated by the PythonClass plugin for PYANG
  from YANG module openconfig-platform-common - based on the path /components. Each member element of
  the container is represented as a class variable - with a specific
  YANG type.

  YANG Description: Enclosing container for the components in the system.
  """
  __slots__ = ('_path_helper', '_extmethods', '__component',)

  _yang_name = 'components'
  _yang_namespace = 'http://openconfig.net/yang/platform'

  _pybind_generated_by = 'container'

  def __init__(self, *args, **kwargs):

    self._path_helper = False

    self._extmethods = False
    self.__component = YANGDynClass(base=YANGListType("name",component.component, yang_name="component", parent=self, is_container='list', user_ordered=False, path_helper=self._path_helper, yang_keys='name', extensions=None), is_container='list', yang_name="component", parent=self, path_helper=self._path_helper, extmethods=self._extmethods, register_paths=True, extensions=None, namespace='http://openconfig.net/yang/platform', defining_module='openconfig-platform', yang_type='list', is_config=True)

    load = kwargs.pop("load", None)
    if args:
      if len(args) > 1:
        raise TypeError("cannot create a YANG container with >1 argument")
      all_attr = True
      for e in self._pyangbind_elements:
        if not hasattr(args[0], e):
          all_attr = False
          break
      if not all_attr:
        raise ValueError("Supplied object did not have the correct attributes")
      for e in self._pyangbind_elements:
        nobj = getattr(args[0], e)
        if nobj._changed() is False:
          continue
        setmethod = getattr(self, "_set_%s" % e)
        if load is None:
          setmethod(getattr(args[0], e))
        else:
          setmethod(getattr(args[0], e), load=load)

  def _path(self):
    if hasattr(self, "_parent"):
      return self._parent._path()+[self._yang_name]
    else:
      return ['components']

  def _get_component(self):
    """
    Getter method for component, mapped from YANG variable /components/component (list)

    YANG Description: List of components, keyed by component name.
    """
    return self.__component
      
  def _set_component(self, v, load=False):
    """
    Setter method for component, mapped from YANG variable /components/component (list)
    If this variable is read-only (config: false) in the
    source YANG file, then _set_component is considered as a private
    method. Backends looking to populate this variable should
    do so via calling thisObj._set_component() directly.

    YANG Description: List of components, keyed by component name.
    """
    if hasattr(v, "_utype"):
      v = v._utype(v)
    try:
      t = YANGDynClass(v,base=YANGListType("name",component.component, yang_name="component", parent=self, is_container='list', user_ordered=False, path_helper=self._path_helper, yang_keys='name', extensions=None), is_container='list', yang_name="component", parent=self, path_helper=self._path_helper, extmethods=self._extmethods, register_paths=True, extensions=None, namespace='http://openconfig.net/yang/platform', defining_module='openconfig-platform', yang_type='list', is_config=True)
    except (TypeError, ValueError):
      raise ValueError({
          'error-string': """component must be of a type compatible with list""",
          'defined-type': "list",
          'generated-type': """YANGDynClass(base=YANGListType("name",component.component, yang_name="component", parent=self, is_container='list', user_ordered=False, path_helper=self._path_helper, yang_keys='name', extensions=None), is_container='list', yang_name="component", parent=self, path_helper=self._path_helper, extmethods=self._extmethods, register_paths=True, extensions=None, namespace='http://openconfig.net/yang/platform', defining_module='openconfig-platform', yang_type='list', is_config=True)""",
        })

    self.__component = t
    if hasattr(self, '_set'):
      self._set()

  def _unset_component(self):
    self.__component = YANGDynClass(base=YANGListType("name",component.component, yang_name="component", parent=self, is_container='list', user_ordered=False, path_helper=self._path_helper, yang_keys='name', extensions=None), is_container='list', yang_name="component", parent=self, path_helper=self._path_helper, extmethods=self._extmethods, register_paths=True, extensions=None, namespace='http://openconfig.net/yang/platform', defining_module='openconfig-platform', yang_type='list', is_config=True)

  component = __builtin__.property(_get_component, _set_component)


  _pyangbind_elements = OrderedDict([('component', component), ])

Loading