Move experimental items undo the experiments directory and some minor name cleanuos

Change-Id: I748e0546a80a593ddef6b8a4ac749d592b9d26b1
diff --git a/experiments/netconf/proto2yang/addressbook.proto b/experiments/netconf/proto2yang/addressbook.proto
new file mode 100644
index 0000000..fc1a10f
--- /dev/null
+++ b/experiments/netconf/proto2yang/addressbook.proto
@@ -0,0 +1,34 @@
+// See README.txt for information and build instructions.
+
+syntax = "proto3";
+
+package tutorial;
+
+option java_package = "com.example.tutorial";
+option java_outer_classname = "AddressBookProtos";
+option csharp_namespace = "Google.Protobuf.Examples.AddressBook";
+
+message Person {
+  string name = 1;
+  int32 id = 2;        // Unique ID number for this person.
+  string email = 3;
+
+  enum PhoneType {
+    MOBILE = 0;
+    HOME = 1;
+    WORK = 2;
+  }
+
+  message PhoneNumber {
+    string number = 1;
+    PhoneType type = 2;
+  }
+
+  repeated PhoneNumber phones = 4;
+  repeated string khen = 5;
+}
+
+// Our address book file is just one of these.
+message AddressBook {
+  repeated Person people = 1;
+}
diff --git a/experiments/netconf/proto2yang/descriptor.desc b/experiments/netconf/proto2yang/descriptor.desc
new file mode 100644
index 0000000..ac12c36
--- /dev/null
+++ b/experiments/netconf/proto2yang/descriptor.desc
Binary files differ
diff --git a/experiments/netconf/proto2yang/descriptor_parser.py b/experiments/netconf/proto2yang/descriptor_parser.py
new file mode 100644
index 0000000..c23f497
--- /dev/null
+++ b/experiments/netconf/proto2yang/descriptor_parser.py
@@ -0,0 +1,164 @@
+#
+# Copyright 2016 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import os
+from collections import OrderedDict
+
+from google.protobuf import descriptor_pb2
+from google.protobuf.descriptor import FieldDescriptor, Descriptor
+from google.protobuf.message import Message
+
+
+class InvalidDescriptorError(Exception): pass
+
+
+class DescriptorParser(object):
+    """
+    Used to parse protobuf FileDescriptor objects into native Python
+    data structures (nested dict/list/intrinsic values. Two of the typical
+    sources of FileDescriptor objects are:
+    1. CodeGeneratorRequest, used as binary input to any protoc plugin,
+       contains a list of these FileDescriptor objects (under the
+       proto_file attribute)
+    2. FileDescriptorSet, as saved by protoc when using the -o option.
+
+    An important feature of the parser is that it can process the source
+    code annotations and can fold comments into the relevant defintions
+    present in the proto file.
+
+    Usage (in a protoc plugin):
+    >>> request = plugin.CodeGeneratorRequest()
+    >>> request.ParseFromString(sys.stdin.read())
+    >>> parser = DescriptorParser()
+    >>> for proto_file in request.proto_file:
+    >>>     parsed_data = parser.parse_file_descriptor()
+    >>>     print json.dumps(parsed_data, indent=4)
+    """
+
+    meta = None
+
+    def __init__(self):
+        if DescriptorParser.meta is None:
+            DescriptorParser.meta = self.load_meta_descriptor()
+
+    def load_meta_descriptor(self):
+        """
+        Load the protobuf version of descriptor.proto to use it in
+        decoding protobuf paths.
+        """
+        fpath = os.path.abspath(os.path.join(os.path.dirname(__file__),
+                                             'descriptor.desc'))
+        with open(fpath, 'r') as f:
+            blob = f.read()
+        proto = descriptor_pb2.FileDescriptorSet()
+        proto.ParseFromString(blob)
+        assert len(proto.file) == 1
+        return proto.file[0]
+
+    parser_table = {
+        unicode: lambda x: x,
+        int: lambda x: x,
+        bool: lambda x: x,
+    }
+
+    def parse(self, o, type_tag_name=None):
+        if isinstance(o, Message):
+            return self.parse_message(o, type_tag_name)
+        else:
+            return self.parser_table[type(o)](o)
+
+    def parse_message(self, m, type_tag_name=None):
+        assert isinstance(m, Message)
+        d = OrderedDict()
+        for field, value in m.ListFields():
+            assert isinstance(field, FieldDescriptor)
+            if field.label in (1, 2):
+                d[field.name] = self.parse(value, type_tag_name)
+            elif field.label == 3:
+                d[field.name] = [self.parse(x, type_tag_name) for x in
+                                 value]
+            else:
+                raise InvalidDescriptorError()
+
+        if type_tag_name is not None:
+            d[type_tag_name] = m.DESCRIPTOR.full_name.strip('.')
+
+        return d
+
+    def parse_file_descriptor(self, descriptor,
+                              type_tag_name=None,
+                              fold_comments=False):
+
+        d = self.parse(descriptor, type_tag_name=type_tag_name)
+
+        if fold_comments:
+            locations = d.get('source_code_info', {}).get('location', [])
+            for location in locations:
+                path = location.get('path', [])
+                comments = ''.join([
+                    location.get('leading_comments', '').strip(' '),
+                    location.get('trailing_comments', '').strip(' '),
+                    ''.join(block.strip(' ') for block
+                            in
+                            location.get('leading_detached_comments', ''))
+                ]).strip()
+
+                # ignore locations with no comments
+                if not comments:
+                    continue
+
+                # we ignore path with odd number of entries, since these do
+                # not address our schema nodes, but rather the meta schema
+                if (len(path) % 2 == 0):
+                    node = self.find_node_by_path(
+                        path, self.meta.DESCRIPTOR, d)
+                    assert isinstance(node, dict)
+                    node['_description'] = comments
+
+            # remove source_code_info
+            del d['source_code_info']
+
+        return d
+
+    def parse_file_descriptors(self, descriptors,
+                              type_tag_name=None,
+                              fold_comments=False):
+        return [self.parse_file_descriptor(descriptor,
+                                           type_tag_name=type_tag_name,
+                                           fold_comments=fold_comments)
+                for descriptor in descriptors]
+
+    def find_node_by_path(self, path, meta, o):
+        # stop recursion when path is empty
+        if not path:
+            return o
+
+        # sanity check
+        assert len(path) >= 2
+        assert isinstance(meta, Descriptor)
+        assert isinstance(o, dict)
+
+        # find field name, then actual field
+        field_number = path.pop(0)
+        field_def = meta.fields_by_number[field_number]
+        field = o[field_def.name]
+
+        # field must be a list, extract entry with given index
+        assert isinstance(field, list)  # expected to be a list field
+        index = path.pop(0)
+        child_o = field[index]
+
+        child_meta = field_def.message_type
+        return self.find_node_by_path(path, child_meta, child_o)
diff --git a/experiments/netconf/proto2yang/proto2yang.py b/experiments/netconf/proto2yang/proto2yang.py
new file mode 100755
index 0000000..8d9a40b
--- /dev/null
+++ b/experiments/netconf/proto2yang/proto2yang.py
@@ -0,0 +1,494 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""protoc plugin to convert a protobuf schema to a yang schema
+
+   - basic support for message, fields. enumeration, service, method
+
+   - yang semantic rules needs to be implemented
+
+   - to run this plugin :
+
+   $ python -m grpc.tools.protoc -I.
+   --plugin=protoc-gen-custom=./proto2yang.py --custom_out=. <proto file>.proto
+
+   - the above will produce a ietf-<proto file>.yang file formatted for yang
+
+   - two examples of proto that can be used in the same directory are
+   yang.proto and addressbook.proto
+
+"""
+
+import sys
+
+from jinja2 import Template
+from google.protobuf.compiler import plugin_pb2 as plugin
+from descriptor_parser import DescriptorParser
+
+from google.protobuf.descriptor import FieldDescriptor
+
+template_yang = Template("""
+module ietf-{{ module.name }} {
+    yang-version 1.1;
+    namespace "urn:ietf:params:xml:ns:yang:ietf-{{ module.name }}";
+    prefix "voltha";
+
+    organization "CORD";
+    contact
+        " Any name";
+
+    description
+        "{{ module.description }}";
+
+    revision "2016-11-15" {
+        description "Initial revision.";
+        reference "reference";
+    }
+
+    {% for enum in module.enums %}
+    typedef {{ enum.name }} {
+        type enumeration {
+        {% for v in enum.value %}
+            enum {{ v.name }} {
+                description "{{ v.description }}";
+            }
+        {% endfor %}
+        }
+        description
+            "{{ enum.description }}";
+    }
+    {% endfor %}
+
+    {% for message in module.messages recursive %}
+    {% if message.name in module.referenced_messages %}
+    grouping {{ message.name }} {
+    {% else %}
+    container {{ message.name }} {
+    {% endif %}
+        description
+            "{{ message.description }}";
+        {% for field in message.fields %}
+        {% if field.type_ref %}
+        {% for dict_item in module.referred_messages_with_keys %}
+                {% if dict_item.name == field.type %}
+        list {{ field.name }} {
+            key "{{ dict_item.key }}";
+            {% if not field.repeated %}
+            max-elements 1;
+            {% endif %}
+            uses {{ field.type }};
+            description
+                "{{ field.description }}";
+        }
+                {% endif %}
+        {% endfor %}
+        {% elif field.repeated %}
+        list {{ field.name }} {
+            key "{{ field.name }}";
+            leaf {{ field.name }} {
+                {% if field.type == "decimal64" %}
+                type {{ field.type }} {
+                   fraction-digits 5;
+                }
+                {% else %}
+                type {{ field.type }};
+                {% endif %}
+                description
+                    "{{ field.description }}";
+            }
+            description
+                "{{ field.description }}";
+        }
+        {% else %}
+        leaf {{ field.name }} {
+            {% if field.type == "decimal64" %}
+            type {{ field.type }} {
+               fraction-digits 5;
+            }
+            {% else %}
+            type {{ field.type }};
+            {% endif %}
+            description
+                "{{ field.description }}";
+        }
+        {% endif %}
+
+        {% endfor %}
+        {% for enum_type in message.enums %}
+        typedef {{ enum_type.name }} {
+            type enumeration {
+            {% for v in enum_type.value %}
+                enum {{ v.name }} {
+                    description "{{ v.description }}";
+                }
+            {% endfor %}
+            }
+            description
+                "{{ enum_type.description }}";
+        }
+
+        {% endfor %}
+    {% if message.messages %}
+    {{ loop (message.messages)|indent(4, false) }}
+    {% endif %}
+    }
+
+    {% endfor %}
+    {% for service in module.services %}
+    {% if service.description %}
+    /*  {{ service.description }}" */
+    {% endif %}
+    {% for method in service.methods %}
+    rpc {{ service.service }}-{{ method.method }} {
+        description
+            "{{ method.description }}";
+        {% if method.input %}
+        input {
+            {% if method.input_ref %}
+            uses {{ method.input }};
+            {% else %}
+            leaf {{ method.input }} {
+                type {{ method.input }};
+            }
+            {% endif %}
+        }
+        {% endif %}
+        {% if method.output %}
+        output {
+            {% if method.output_ref %}
+            uses {{ method.output }};
+            {% else %}
+            leaf {{ method.output }} {
+                type {{ method.output }};
+            }
+            {% endif %}
+        }
+        {% endif %}
+    }
+
+    {% endfor %}
+
+    {% endfor %}
+}
+""", trim_blocks=True, lstrip_blocks=True)
+
+
+def traverse_messages(message_types, prefix, referenced_messages):
+    messages = []
+    for message_type in message_types:
+        assert message_type['_type'] == 'google.protobuf.DescriptorProto'
+
+        # full_name = prefix + '-' + message_type['name']
+        full_name = message_type['name']
+
+        # parse the fields
+        fields = traverse_fields(message_type.get('field', []), full_name,
+                                 referenced_messages)
+
+        # parse the enums
+        enums = traverse_enums(message_type.get('enum_type', []), full_name)
+
+        # parse nested messages
+        nested = message_type.get('nested_type', [])
+        nested_messages = traverse_messages(nested, full_name,
+                                            referenced_messages)
+        messages.append(
+            {
+                'name': full_name,
+                'fields': fields,
+                'enums': enums,
+                # 'extensions': extensions,
+                'messages': nested_messages,
+                'description': remove_unsupported_characters(
+                    message_type.get('_description', '')),
+                # 'extension_ranges': extension_ranges,
+                # 'oneof': oneof
+            }
+        )
+    return messages
+
+
+def traverse_fields(fields_desc, prefix, referenced_messages):
+    fields = []
+    for field in fields_desc:
+        assert field['_type'] == 'google.protobuf.FieldDescriptorProto'
+        yang_base_type = is_base_type(field['type'])
+        _type = get_yang_type(field)
+        if not yang_base_type:
+            referenced_messages.append(_type)
+
+        fields.append(
+            {
+                # 'name': prefix + '-' + field.get('name', ''),
+                'name': field.get('name', ''),
+                'label': field.get('label', ''),
+                'repeated': field['label'] == FieldDescriptor.LABEL_REPEATED,
+                'number': field.get('number', ''),
+                'options': field.get('options', ''),
+                'type_name': field.get('type_name', ''),
+                'type': _type,
+                'type_ref': not yang_base_type,
+                'description': remove_unsupported_characters(field.get(
+                    '_description', ''))
+            }
+        )
+    return fields
+
+
+def traverse_enums(enums_desc, prefix):
+    enums = []
+    for enum in enums_desc:
+        assert enum['_type'] == 'google.protobuf.EnumDescriptorProto'
+        # full_name = prefix + '-' + enum.get('name', '')
+        full_name = enum.get('name', '')
+        enums.append(
+            {
+                'name': full_name,
+                'value': enum.get('value', ''),
+                'description': remove_unsupported_characters(enum.get(
+                    '_description', ''))
+            }
+        )
+    return enums
+
+
+def traverse_services(service_desc, referenced_messages):
+    services = []
+    for service in service_desc:
+        methods = []
+        for method in service.get('method', []):
+            assert method['_type'] == 'google.protobuf.MethodDescriptorProto'
+
+            input_name = method.get('input_type')
+            input_ref = False
+            if not is_base_type(input_name):
+                input_name = remove_first_character_if_match(input_name, '.')
+                # input_name = input_name.replace(".", "-")
+                input_name = input_name.split('.')[-1]
+                referenced_messages.append(input_name)
+                input_ref = True
+
+            output_name = method.get('output_type')
+            output_ref = False
+            if not is_base_type(output_name):
+                output_name = remove_first_character_if_match(output_name, '.')
+                # output_name = output_name.replace(".", "-")
+                output_name = output_name.split('.')[-1]
+                referenced_messages.append(output_name)
+                output_ref = True
+
+            methods.append(
+                {
+                    'method': method.get('name', ''),
+                    'input': input_name,
+                    'input_ref': input_ref,
+                    'output': output_name,
+                    'output_ref': output_ref,
+                    'description': remove_unsupported_characters(method.get(
+                        '_description', '')),
+                    'server_streaming': method.get('server_streaming',
+                                                   False) == True
+                }
+            )
+        services.append(
+            {
+                'service': service.get('name', ''),
+                'methods': methods,
+                'description': remove_unsupported_characters(service.get(
+                    '_description', '')),
+            }
+        )
+    return services
+
+
+def rchop(thestring, ending):
+    if thestring.endswith(ending):
+        return thestring[:-len(ending)]
+    return thestring
+
+
+def traverse_desc(descriptor):
+    referenced_messages = []
+    name = rchop(descriptor.get('name', ''), '.proto')
+    package = descriptor.get('package', '')
+    description = descriptor.get('_description', '')
+    messages = traverse_messages(descriptor.get('message_type', []),
+                                 package, referenced_messages)
+    enums = traverse_enums(descriptor.get('enum_type', []), package)
+    services = traverse_services(descriptor.get('service', []),
+                                 referenced_messages)
+    # extensions = _traverse_extensions(descriptors)
+    # options = _traverse_options(descriptors)
+    set_messages_keys(messages)
+    unique_referred_messages_with_keys = []
+    for message_name in list(set(referenced_messages)):
+        unique_referred_messages_with_keys.append(
+            {
+                'name': message_name,
+                'key': get_message_key(message_name, messages)
+            }
+        )
+
+    data = {
+        'name': name,
+        'package': package,
+        'description': description,
+        'messages': messages,
+        'enums': enums,
+        'services': services,
+        'referenced_messages': list(set(referenced_messages)),
+        # TODO:  simplify for easier jinja2 template use
+        'referred_messages_with_keys': unique_referred_messages_with_keys,
+        # 'extensions': extensions,
+        # 'options': options
+    }
+    return data
+
+
+def set_messages_keys(messages):
+    for message in messages:
+        message['key'] = _get_message_key(message)
+        if message['messages']:
+            set_messages_keys(message['messages'])
+
+
+def _get_message_key(message):
+    # assume key is first yang base type field
+    for field in message['fields']:
+        if not field['type_ref']:
+            return field['name']
+    # no key yet - search nested messaged
+    if message['messages']:
+        return get_message_key(message['messages'])
+    else:
+        return None
+
+
+def get_message_key(message_name, messages):
+    for message in messages:
+        if message_name == message['name']:
+            return message['key']
+        if message['messages']:
+            return get_message_key(message_name, message['messages'])
+    return None
+
+
+def generate_code(request, response):
+    assert isinstance(request, plugin.CodeGeneratorRequest)
+
+    parser = DescriptorParser()
+
+    # idx = 1
+    for proto_file in request.proto_file:
+        native_data = parser.parse_file_descriptor(proto_file,
+                                                   type_tag_name='_type',
+                                                   fold_comments=True)
+
+        # print native_data
+        yang_data = traverse_desc(native_data)
+
+        f = response.file.add()
+        # TODO: We should have a separate file for each output. There is an
+        # issue reusing the same filename with an incremental suffix.  Using
+        # a different file name works but not the actual proto file name
+        f.name = '{}-{}'.format('ietf', proto_file.name.replace('.proto',
+                                                                '.yang'))
+        # f.name = '{}_{}{}'.format(_rchop(proto_file.name, '.proto'), idx,
+        #                            '.yang')
+        # idx += 1
+        f.content = template_yang.render(module=yang_data)
+
+
+def get_yang_type(field):
+    type = field['type']
+    if type in YANG_TYPE_MAP.keys():
+        _type, _ = YANG_TYPE_MAP[type]
+        if _type in ['enumeration', 'message', 'group']:
+            return field['type_name'].split('.')[-1]
+            # return remove_first_character_if_match(field['type_name'],
+            #                                        '.').replace('.', '-')
+        else:
+            return _type
+    else:
+        return type
+
+
+def is_base_type(type):
+    # check numeric value of the type first
+    if type in YANG_TYPE_MAP.keys():
+        _type, _ = YANG_TYPE_MAP[type]
+        return _type not in ['message', 'group']
+    else:
+        # proto name of the type
+        result = [_format for (_, _format) in YANG_TYPE_MAP.values() if
+                  _format == type and _format not in ['message', 'group']]
+        return len(result) > 0
+
+
+def remove_unsupported_characters(text):
+    unsupported_characters = ["{", "}", "[", "]", "\"", "\\", "*", "/"]
+    return ''.join([i if i not in unsupported_characters else ' ' for i in
+                    text])
+
+
+def remove_first_character_if_match(str, char):
+    if str.startswith(char):
+        return str[1:]
+    return str
+
+
+YANG_TYPE_MAP = {
+    FieldDescriptor.TYPE_BOOL: ('boolean', 'boolean'),
+    FieldDescriptor.TYPE_BYTES: ('binary', 'byte'),
+    FieldDescriptor.TYPE_DOUBLE: ('decimal64', 'double'),
+    FieldDescriptor.TYPE_ENUM: ('enumeration', 'enum'),
+    FieldDescriptor.TYPE_FIXED32: ('int32', 'int64'),
+    FieldDescriptor.TYPE_FIXED64: ('int64', 'uint64'),
+    FieldDescriptor.TYPE_FLOAT: ('decimal64', 'float'),
+    FieldDescriptor.TYPE_INT32: ('int32', 'int32'),
+    FieldDescriptor.TYPE_INT64: ('int64', 'int64'),
+    FieldDescriptor.TYPE_SFIXED32: ('int32', 'int32'),
+    FieldDescriptor.TYPE_SFIXED64: ('int64', 'int64'),
+    FieldDescriptor.TYPE_STRING: ('string', 'string'),
+    FieldDescriptor.TYPE_SINT32: ('int32', 'int32'),
+    FieldDescriptor.TYPE_SINT64: ('int64', 'int64'),
+    FieldDescriptor.TYPE_UINT32: ('uint32', 'int64'),
+    FieldDescriptor.TYPE_UINT64: ('uint64', 'uint64'),
+    FieldDescriptor.TYPE_MESSAGE: ('message', 'message'),
+    FieldDescriptor.TYPE_GROUP: ('group', 'group')
+}
+
+if __name__ == '__main__':
+    # Read request message from stdin
+    data = sys.stdin.read()
+
+    # Parse request
+    request = plugin.CodeGeneratorRequest()
+    request.ParseFromString(data)
+
+    # Create response
+    response = plugin.CodeGeneratorResponse()
+
+    # Generate code
+    generate_code(request, response)
+
+    # Serialise response message
+    output = response.SerializeToString()
+
+    # Write to stdout
+    sys.stdout.write(output)
+    # print is_base_type(9)
diff --git a/experiments/netconf/proto2yang/yang.proto b/experiments/netconf/proto2yang/yang.proto
new file mode 100644
index 0000000..718951c
--- /dev/null
+++ b/experiments/netconf/proto2yang/yang.proto
@@ -0,0 +1,64 @@
+syntax = "proto3";
+
+package experiment;
+
+message AsyncEvent {
+    int32 seq = 1;
+    enum EventType {
+        BIG_BANG = 0;  // just a big bang
+        SMALL_BANG = 1;  // so small bang
+        NO_BANG = 2;
+    }
+    EventType type = 2;
+    string details = 3;
+}
+
+enum SimpleEnum {
+    APPLE = 0;
+    BANANA = 1;
+    ORANGE = 2;
+}
+
+message Packet {
+    int32 source = 1;
+    bytes content = 2;
+    message InnerPacket {
+        string url = 1;
+        string title = 2;
+        repeated string snippets = 3;
+        message InnerInnerPacket {
+            string input = 1;
+            string desc = 2;
+        }
+        repeated InnerInnerPacket inner_inner_packet = 4;
+    }
+    repeated InnerPacket inner_packets = 3;
+}
+
+message Echo {
+    string msg = 1;
+    float delay = 2;
+}
+
+message testMessage{
+    oneof oneOfTest {
+        string test2 = 1;
+        int32 test3 = 2;
+    }
+}
+
+service ExperimentalService {
+
+    rpc GetEcho(Echo) returns(Echo);
+
+    // For server to send async stream to client
+    rpc ReceiveStreamedEvents(Packet)
+        returns(stream AsyncEvent);
+
+    // For server to send async packets to client
+    rpc ReceivePackets(Echo) returns(stream Packet);
+
+    // For client to send async packets to server
+    rpc SendPackets(stream Packet) returns(Echo);
+
+}
\ No newline at end of file
diff --git a/experiments/netconf/tests/proto2yang/__init__.py b/experiments/netconf/tests/proto2yang/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/experiments/netconf/tests/proto2yang/__init__.py
diff --git a/experiments/netconf/tests/proto2yang/protobuf_to_yang_test.py b/experiments/netconf/tests/proto2yang/protobuf_to_yang_test.py
new file mode 100644
index 0000000..7336524
--- /dev/null
+++ b/experiments/netconf/tests/proto2yang/protobuf_to_yang_test.py
@@ -0,0 +1,558 @@
+#
+# Copyright 2016 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import json
+import os
+from unittest import TestCase
+import time
+from tests.itests.docutests.test_utils import run_command_to_completion_with_raw_stdout
+from tests.utests.chameleon.protoc_plugins.test_utils import load_file, \
+    unindent, save_file
+
+proto_to_yang_cmd='python -m grpc.tools.protoc -I{}  ' \
+                  '--plugin=protoc-gen-custom=/voltha/experiments' \
+                  '/netconf/proto2yang/proto2yang.py --custom_out={} {}'
+
+yang_validate_cmd="pyang -f tree --ietf {}"
+
+TEMP_PATH="/tmp/proto2yang"
+TEMP_INPUT_PROTO_FILE="test.proto"
+TEMP_OUTPUT_YANG_FILE="ietf-test.yang"
+TEMP_PROTO_PATH='{}/{}'.format(TEMP_PATH, TEMP_INPUT_PROTO_FILE)
+TEMP_YANG_PATH='{}/{}'.format(TEMP_PATH, TEMP_OUTPUT_YANG_FILE)
+
+
+class ProtoToYang(TestCase):
+
+    def setup(self):
+        if not os.path.exists(TEMP_PATH):
+            os.makedirs(TEMP_PATH)
+
+
+    def _compare_file(self, response, expected_response):
+        # compare two files and strip empty lines, blanks, etc
+        def _filter(x):
+            x.strip()
+            return x is not None
+
+        response = filter(_filter,response.split())
+        expected_response = filter(_filter,expected_response.split())
+        print response
+        print expected_response
+
+        self.assertEqual(set(response), set(expected_response))
+
+    def _gen_yang(self, proto):
+        try:
+            save_file(os.path.join(TEMP_PATH, TEMP_INPUT_PROTO_FILE), proto)
+            cmd = proto_to_yang_cmd.format(TEMP_PATH, TEMP_PATH, TEMP_PROTO_PATH
+                                           )
+            print 'running command: {}'.format(cmd)
+            response, err, rc = run_command_to_completion_with_raw_stdout(cmd)
+            self.assertEqual(rc, 0)
+
+            return load_file(TEMP_YANG_PATH)
+        except Exception as e:
+            print('Failure to generate yang file {}'.format(e))
+
+
+    def test_01_empty_proto(self):
+        print "Test_01_empty_proto_Start:------------------"
+        t0 = time.time()
+
+        proto = unindent("""
+            syntax = "proto3";
+            package test;
+        """)
+
+        expected_response = """
+            module ietf-test {
+                yang-version 1.1;
+                namespace "urn:ietf:params:xml:ns:yang:ietf-test";
+                prefix "voltha";
+
+                organization "CORD";
+                contact
+                    " Any name";
+
+                description
+                    "";
+
+                revision "2016-11-15" {
+                    description "Initial revision.";
+                    reference "reference";
+                }
+            }
+            """
+
+        try:
+            yang = self._gen_yang(proto)
+            self._compare_file(yang, expected_response)
+        finally:
+            print "Test_01_empty_proto_End:------------------ took {} " \
+                  "secs\n\n".format(time.time() - t0)
+
+
+    def test_02_empty_message_with_service(self):
+        print "Test_02_empty_message_with_service_Start:------------------"
+        t0 = time.time()
+
+        proto = unindent("""
+            syntax = "proto3";
+            package test;
+            message Null {}
+            service TestService {
+              rpc Get(Null) returns(Null);
+            }
+        """)
+
+        expected_response = """
+            module ietf-test {
+                yang-version 1.1;
+                namespace "urn:ietf:params:xml:ns:yang:ietf-test";
+                prefix "voltha";
+
+                organization "CORD";
+                contact
+                    " Any name";
+
+                description
+                    "";
+
+                revision "2016-11-15" {
+                    description "Initial revision.";
+                    reference "reference";
+                }
+
+                grouping Null {
+                    description
+                        "";
+                }
+
+                rpc TestService-Get {
+                    description
+                        "";
+                    input {
+                        uses Null;
+                    }
+                    output {
+                        uses Null;
+                    }
+                }
+            }
+            """
+
+        try:
+            yang = self._gen_yang(proto)
+            self._compare_file(yang, expected_response)
+        finally:
+            print "Test_02_empty_message_with_service_End:------------------ took {} " \
+                  "secs\n\n".format(time.time() - t0)
+
+
+    def test_03_simple_message_with_service(self):
+        print "Test__03_simple_message_with_service_Start:------------------"
+        t0 = time.time()
+
+        proto = unindent("""
+            syntax = "proto3";
+            package test;
+
+            // Simple Message
+            message Simple {
+                string str = 1; // a string attribute
+                int32 int = 2; // an int32 attribute
+            }
+
+            // Service to get things done
+            service TestService {
+
+              /* Get simple answer
+               *
+               * Returns the true answer to all of life's persistent questions.
+               */
+              rpc Get(Simple) returns(Simple);
+            }
+            """)
+
+        expected_response = """
+            module ietf-test {
+                yang-version 1.1;
+                namespace "urn:ietf:params:xml:ns:yang:ietf-test";
+                prefix "voltha";
+
+                organization "CORD";
+                contact
+                    " Any name";
+
+                description
+                    "";
+
+                revision "2016-11-15" {
+                    description "Initial revision.";
+                    reference "reference";
+                }
+
+
+                grouping Simple {
+                    description
+                        "Simple Message";
+                    leaf str {
+                        type string;
+                        description
+                            "a string attribute";
+                    }
+
+                    leaf int {
+                        type int32;
+                        description
+                            "an int32 attribute";
+                    }
+
+                }
+
+                /*  Service to get things done" */
+                rpc TestService-Get {
+                    description
+                        "Get simple answer
+
+             Returns the true answer to all of life's persistent questions.";
+                    input {
+                        uses Simple;
+                    }
+                    output {
+                        uses Simple;
+                    }
+                }
+            }
+            """
+
+        try:
+            yang = self._gen_yang(proto)
+            self._compare_file(yang, expected_response)
+        finally:
+            print "Test_03_simple_message_with_service_End" \
+                  ":------------------ took {} secs\n\n".format(time.time() - t0)
+
+
+    def test_04_mix_types(self):
+        print "Test__04_mix_types_Start:------------------"
+        t0 = time.time()
+
+        proto = unindent("""
+            syntax = "proto3";
+
+            package experiment;
+
+            message AsyncEvent {
+                int32 seq = 1;
+                enum EventType {
+                    BIG_BANG = 0;  // just a big bang
+                    SMALL_BANG = 1;  // so small bang
+                    NO_BANG = 2;
+                }
+                EventType type = 2;
+                string details = 3;
+            }
+
+            enum SimpleEnum {
+                APPLE = 0;
+                BANANA = 1;
+                ORANGE = 2;
+            }
+
+            message Packet {
+                int32 source = 1;
+                bytes content = 2;
+                message InnerPacket {
+                    string url = 1;
+                    string title = 2;
+                    repeated string snippets = 3;
+                    message InnerInnerPacket {
+                        string input = 1;
+                        string desc = 2;
+                    }
+                    repeated InnerInnerPacket inner_inner_packet = 4;
+                }
+                repeated InnerPacket inner_packets = 3;
+            }
+
+            message Echo {
+                string msg = 1;
+                float delay = 2;
+            }
+
+            message testMessage{
+                string test2 = 1;
+                int32 test3 = 2;
+            }
+
+            service ExperimentalService {
+
+                rpc GetEcho(Echo) returns(Echo);
+
+                // For server to send async stream to client
+                rpc ReceiveStreamedEvents(Packet)
+                    returns(stream AsyncEvent);
+
+                // For server to send async packets to client
+                rpc ReceivePackets(Echo) returns(stream Packet);
+
+                // For client to send async packets to server
+                rpc SendPackets(stream Packet) returns(Echo);
+
+            }
+            """)
+
+        expected_response = """
+            module ietf-test {
+                yang-version 1.1;
+                namespace "urn:ietf:params:xml:ns:yang:ietf-test";
+                prefix "voltha";
+
+                organization "CORD";
+                contact
+                    " Any name";
+
+                description
+                    "";
+
+                revision "2016-11-15" {
+                    description "Initial revision.";
+                    reference "reference";
+                }
+
+                typedef SimpleEnum {
+                    type enumeration {
+                        enum APPLE {
+                            description "";
+                        }
+                        enum BANANA {
+                            description "";
+                        }
+                        enum ORANGE {
+                            description "";
+                        }
+                    }
+                    description
+                        "";
+                }
+
+                grouping AsyncEvent {
+                    description
+                        "";
+                    leaf seq {
+                        type int32;
+                        description
+                            "";
+                    }
+
+                    leaf type {
+                        type EventType;
+                        description
+                            "";
+                    }
+
+                    leaf details {
+                        type string;
+                        description
+                            "";
+                    }
+
+                    typedef EventType {
+                        type enumeration {
+                            enum BIG_BANG {
+                                description "";
+                            }
+                            enum SMALL_BANG {
+                                description "";
+                            }
+                            enum NO_BANG {
+                                description "";
+                            }
+                        }
+                        description
+                            "";
+                    }
+
+                }
+
+                grouping Packet {
+                    description
+                        "";
+                    leaf source {
+                        type int32;
+                        description
+                            "";
+                    }
+
+                    leaf content {
+                        type binary;
+                        description
+                            "";
+                    }
+
+                    list inner_packets {
+                        key "url";
+                        uses InnerPacket;
+                        description
+                            "";
+                    }
+
+                    grouping InnerPacket {
+                        description
+                            "";
+                        leaf url {
+                            type string;
+                            description
+                                "";
+                        }
+
+                        leaf title {
+                            type string;
+                            description
+                                "";
+                        }
+
+                        list snippets {
+                            key "snippets";
+                            leaf snippets {
+                                type string;
+                                description
+                                    "";
+                            }
+                            description
+                                "";
+                        }
+
+                        list inner_inner_packet {
+                            key "input";
+                            uses InnerInnerPacket;
+                            description
+                                "";
+                        }
+
+                        grouping InnerInnerPacket {
+                            description
+                                "";
+                            leaf input {
+                                type string;
+                                description
+                                    "";
+                            }
+
+                            leaf desc {
+                                type string;
+                                description
+                                    "";
+                            }
+
+                        }
+
+                    }
+
+                }
+
+                grouping Echo {
+                    description
+                        "";
+                    leaf msg {
+                        type string;
+                        description
+                            "";
+                    }
+
+                    leaf delay {
+                        type decimal64 {
+                           fraction-digits 5;
+                        }
+                        description
+                            "";
+                    }
+
+                }
+
+                container testMessage {
+                    description
+                        "";
+                    leaf test2 {
+                        type string;
+                        description
+                            "";
+                    }
+
+                    leaf test3 {
+                        type int32;
+                        description
+                            "";
+                    }
+
+                }
+
+                rpc ExperimentalService-GetEcho {
+                    description
+                        "";
+                    input {
+                        uses Echo;
+                    }
+                    output {
+                        uses Echo;
+                    }
+                }
+
+                rpc ExperimentalService-ReceiveStreamedEvents {
+                    description
+                        "For server to send async stream to client";
+                    input {
+                        uses Packet;
+                    }
+                    output {
+                        uses AsyncEvent;
+                    }
+                }
+
+                rpc ExperimentalService-ReceivePackets {
+                    description
+                        "For server to send async packets to client";
+                    input {
+                        uses Echo;
+                    }
+                    output {
+                        uses Packet;
+                    }
+                }
+
+                rpc ExperimentalService-SendPackets {
+                    description
+                        "For client to send async packets to server";
+                    input {
+                        uses Packet;
+                    }
+                    output {
+                        uses Echo;
+                    }
+                }
+
+
+            }
+            """
+
+        try:
+            yang = self._gen_yang(proto)
+            self._compare_file(yang, expected_response)
+        finally:
+            print "Test_04_mix_types_End" \
+                  ":------------------ took {} secs\n\n".format(time.time() - t0)
+
diff --git a/experiments/netconf/tests/yang2proto/basic-rpc.yang b/experiments/netconf/tests/yang2proto/basic-rpc.yang
new file mode 100644
index 0000000..334c227
--- /dev/null
+++ b/experiments/netconf/tests/yang2proto/basic-rpc.yang
@@ -0,0 +1,28 @@
+module basic-rpc {
+    namespace "urn:acme:yang:basic";
+    prefix "basic";
+
+    leaf my-id {
+        type uint8;
+    }
+    leaf my-name {
+        type string;
+    }
+    leaf my-status {
+        type boolean;
+        config false;
+    }
+
+    rpc do-something {
+        input {
+           leaf my-input {
+              type string;
+           }
+        }
+        output {
+           leaf my-output {
+              type string;
+           }
+        }
+    }
+}
diff --git a/experiments/netconf/tests/yang2proto/basic.yang b/experiments/netconf/tests/yang2proto/basic.yang
new file mode 100644
index 0000000..7bba944
--- /dev/null
+++ b/experiments/netconf/tests/yang2proto/basic.yang
@@ -0,0 +1,17 @@
+module basic {
+    namespace "urn:acme:yang:basic";
+    prefix "basic";
+
+    grouping commonAttributes {
+        leaf my-id {
+            type uint8;
+        }
+        leaf my-name {
+            type string;
+        }
+        leaf my-status {
+            type boolean;
+            config false;
+        }
+    }
+}
diff --git a/experiments/netconf/tests/yang2proto/container.yang b/experiments/netconf/tests/yang2proto/container.yang
new file mode 100644
index 0000000..dbc5dfb
--- /dev/null
+++ b/experiments/netconf/tests/yang2proto/container.yang
@@ -0,0 +1,34 @@
+module container {
+    yang-version "1";
+    namespace "http://rob.sh/yang/test/base-test";
+    prefix "foo";
+    organization "BugReports Inc";
+    contact "A bug reporter";
+
+    description
+        "A test module that checks that the plugin works OK";
+    revision 2014-01-01 {
+        description "april-fools";
+        reference "fooled-you";
+    }
+
+    container int-container {
+        description
+            "A container";
+        choice name {
+             case me {
+                    leaf eight {
+                        type int8;
+                        description
+                        "A test leaf for uint8";
+                    }
+             }
+             case myself {
+                     leaf nine {
+                       type int16;
+                       description "i am nine";
+                    }
+             }
+        }
+    }
+}
diff --git a/experiments/netconf/tests/yang2proto/cord-tenant.yang b/experiments/netconf/tests/yang2proto/cord-tenant.yang
new file mode 100644
index 0000000..c858094
--- /dev/null
+++ b/experiments/netconf/tests/yang2proto/cord-tenant.yang
@@ -0,0 +1,90 @@
+module cord-tenant {
+  namespace "urn:ietf:params:xml:ns:yang:cord-tenant";
+  prefix cord;
+  yang-version 1.1;
+
+  import xos-controller  { prefix xos; }
+
+  organization
+    "Open Networking Lab (CORD) / Corenova Technologies";
+
+  contact
+    "Larry Peterson <llp@onlab.us>
+     Peter K. Lee <peter@corenova.com>";
+
+  description
+    "This module contains a collection of core models for CORD.
+
+     Copyright (c) 2016 ON.LAB and the persons identified as authors
+     of the code.  All rights reserved.
+
+     Redistribution and use in source and binary forms, with or
+     without modification, is permitted pursuant to, and subject to
+     the license terms of the Apache License, Version 2.0 which
+     accompanies this distribution, and is available at
+     (http://www.apache.org/licenses/LICENSE-2.0).";
+
+  revision 2016-09-29 {
+    description "Combine CORD subscriber and augment XOS.";
+  }
+  revision 2016-07-22 {
+    description "Initial revision.";
+  }
+
+  identity cord-subscriber {
+    base xos:xos-subscriber;
+  }
+  /*
+   * Groupings
+   */
+  grouping subscriber {
+  /*
+    uses xos:subscriber {
+      refine kind { default cord-subscriber; }
+    }
+   */
+    leaf label {
+      config false;
+      type string {
+        pattern '^cordSubscriber-\w+$';
+      }
+    }
+    leaf status {
+      type enumeration {
+        enum "enabled" {
+          description "Enabled";
+          value 1;
+        }
+        enum "suspended" {
+          description "Suspended";
+        }
+        enum "delinquent" {
+          description "Delinquent";
+        }
+        enum "violation" {
+          description "Copyright Violation";
+        }
+      }
+      default enabled;
+    }
+    leaf demo {
+      type boolean;
+      default false;
+    }
+  }
+  /*
+   * Augmentations to XOS
+
+  augment "/xos:tenant" {
+    container cord {
+      description "Register cord as a tenant into XOS";
+      list subscriber {
+        key id;
+        description
+          "Each entry represents a unique CORD subscriber.";
+        uses cord:subscriber;
+      }
+    }
+  }
+  */
+}
diff --git a/experiments/netconf/tests/yang2proto/cord-volt-service.yang b/experiments/netconf/tests/yang2proto/cord-volt-service.yang
new file mode 100644
index 0000000..111421e
--- /dev/null
+++ b/experiments/netconf/tests/yang2proto/cord-volt-service.yang
@@ -0,0 +1,195 @@
+module cord-volt-service {
+  namespace "urn:ietf:params:xml:ns:yang:cord-volt-service";
+  prefix volt;
+  yang-version 1.1;
+
+  import cord-tenant     { prefix cord; }
+  import xos-controller  { prefix xos; }
+  import xos-types       { prefix xtype; }
+  
+  organization
+   "Open Networking Lab (CORD) / Corenova Technologies";
+
+  contact
+    "Larry Peterson <llp@onlab.us>
+     Peter K. Lee <peter@corenova.com>";
+
+  revision 2016-09-09 {
+    description "Initial revision.";
+  }
+
+  identity virtual-olt { base xos:xos-service; }
+
+  feature onos-app-olt {
+    description "System facility to configure VLAN tags on the OLT.";
+  }
+  feature onos-app-aaa {
+    description "System facility to broker authentication between CPE and Radius server.";
+  }
+  /*
+   * Type definitions
+   */
+  typedef bandwidth {
+    type xtype:bandwidth;
+    default 1000000000; // VOLT bandwidth default is 1Gbps
+  }
+  typedef subscriber-outflow {
+    type leafref {
+      path "/volt:controller/volt:port/volt:id";
+    }
+  }
+  /*
+   * Groupings
+   */ 
+  grouping devices-list {
+    grouping olt-device {
+      description
+        "This grouping describes an OLT device which contains ODN link attachments.";
+    
+      leaf name {
+        description "name of OLT device";
+        type string {
+          length 1..254;
+        }
+      }
+      leaf mac { type xtype:mac-address; mandatory true; }
+
+      container uplink {
+        description "Uplink description of the OLT device.";
+        leaf network { type xtype:network-identifier; }
+        leaf tag {
+          type xtype:vlan;
+          description
+            "Represents S-Tag for instructing OLT to associate a VLAN tag for
+             traffic originating from OLT device.";
+        }
+      }
+      list link {
+        description
+          "Each link represents an ONU/ONT (Optical Network Termination) endpoint
+           connection.";
+
+        key serial;
+        unique tag;
+      
+        leaf mac    { type xtype:mac-address; mandatory true; }
+        leaf serial { type string; mandatory true; }
+        leaf active { type boolean; default false; }
+        leaf tag {
+          type xtype:vlan;
+          description
+            "Represents C-Tag for instructing ONT to add/remove vlan tag for
+             traffic within OLT device.";
+        }
+      }
+    }
+    list device {
+      description
+        "Each entry represents an OLT device.";
+      key mac;
+      //unique 'uplink/tag';
+      uses olt-device;
+    }
+  }
+  grouping subscriber {
+    description
+      "This grouping represents a VOLT service subscriber along with
+       references to ONU/ONT access endpoints used by the subscriber.";
+    
+    container tags {
+      description
+        "Each entry represents a unique combination of the OLT uplink VLAN
+         (outer tag) and the ONU/ONT link VLAN (inner tag) connecting
+         into the fabric for the subscriber.";
+      
+      leaf outer {
+        type leafref {
+          path "/volt:controller/volt:device/volt:uplink/volt:tag";
+        }
+      }
+      leaf inner {
+        type leafref {
+          path "/volt:controller/volt:device/volt:link/volt:tag";
+        }
+      }
+    }
+    leaf outflow {
+      description
+        "Each entry represents a unique openflow port ID that the subscriber
+         uses to connect into the fabric from the VOLT service.";
+      config false;
+      type subscriber-outflow;
+    }
+  }
+
+  /*
+   * Configuration data nodes
+   */
+  container controller {
+    description
+      "The controller configuration block represents a VOLT agent/controller
+       which manages multiple OLT devices. The VOLT agent provides
+       aggregate abstraction of the entire PON as a sigle switch to
+       the controller. Each port entry of the agent represents each
+       ONU/ONT endpoint as a separate openflow port.";
+    
+    uses xos:service {
+      refine kind  {
+        default virtual-olt;
+      }
+      augment "subscriber" { uses volt:subscriber; }
+    }
+    // additional service specific configurations
+    uses devices-list;
+    list port {
+      description
+        "Each entry represents an ONU/ONT endpoint connected across OLT devices.";
+      key id;
+      leaf id {
+        description "OpenFlow Port ID";
+        type xtype:flow-identifier;
+        mandatory true;
+      }
+      leaf link {
+        type leafref {
+          path '/volt:controller/device/link/serial';
+        }
+        mandatory true;
+      }
+    }
+    container radius {
+      if-feature onos-app-aaa;
+      // configuration for how to broker authentication requests
+    }
+  }
+  /*
+   * Augmentations to CORD Tenant in XOS
+   */
+  augment "/xos:tenant/cord:cord/cord:subscriber" {
+    container service {
+      leaf id {
+        type leafref {
+          path "/volt:controller/subscriber/id";
+        }
+      }
+      leaf c-tag {
+        config false;
+        type leafref {
+          path "/volt:controller/subscriber/tags/outer";
+        }
+      }
+      leaf s-tag {
+        config false;
+        type leafref {
+          path "/volt:controller/subscriber/tags/inner";
+        }
+      }
+      leaf uplink-speed {
+        type volt:bandwidth;
+      }
+      leaf downlink-speed {
+        type volt:bandwidth;
+      }
+    }
+  }
+}
diff --git a/experiments/netconf/tests/yang2proto/corenova-node.yang b/experiments/netconf/tests/yang2proto/corenova-node.yang
new file mode 100644
index 0000000..e0873e0
--- /dev/null
+++ b/experiments/netconf/tests/yang2proto/corenova-node.yang
@@ -0,0 +1,41 @@
+module corenova-node {
+  namespace "urn:corenova:node";
+  prefix node;
+
+  organization
+    "Corenova Technologies, Inc.";
+
+  contact
+    "Peter K. Lee <peter@corenova.com>";
+
+  description
+    "This module defines extensions to dynamically control schema node
+     display and access overlay controls";
+
+  feature corenova-node {
+    description
+      "this is to indicate the agent supports dynamic schema node
+       link and view constraints.";
+  }
+    
+  extension link {
+    description
+      "Links a new 'link-name' element to an alternate data node element in
+       the schema data tree referenced by the 'path' sub statement. It
+       can be used within 'container' or 'list' type data element to
+       hold one or more such 'links' as part of its configuration data
+       tree.  The new link element becomes a 'mirror' instance of the
+       target node found in the path expression.";
+    argument link-name {
+      yin-element true;
+    }
+  }
+
+  extension view {
+    description
+      "Informs the interface layers that only the data nodes referenced
+       within the value argument should be made visible to the
+       consumer.";
+    argument value;
+  }
+}
diff --git a/experiments/netconf/tests/yang2proto/mix_simple_types.yang b/experiments/netconf/tests/yang2proto/mix_simple_types.yang
new file mode 100644
index 0000000..c73b6cf
--- /dev/null
+++ b/experiments/netconf/tests/yang2proto/mix_simple_types.yang
@@ -0,0 +1,91 @@
+module mix-simple-types {
+    yang-version "1";
+    namespace "http://rob.sh/yang/test/base-test";
+    prefix "foo";
+    organization "BugReports Inc";
+    contact "A bug reporter";
+
+    description
+        "A test module that checks that the plugin works OK";
+    revision 2014-01-01 {
+        description "april-fools";
+        reference "fooled-you";
+    }
+
+    container int-container {
+        description
+            "A container";
+        choice name {
+             case me {
+                    leaf eight {
+                        type int8;
+                        description
+                        "A test leaf for uint8";
+                    }
+             }
+             case myself {
+                     leaf nine {
+                       type int16;
+                       description "i am nine";
+                    }
+             }
+        }
+        leaf ten {
+             type int16;
+             description "i am ten";
+        }
+    }
+
+    list user {
+       key "name";
+       leaf name {
+         type string;
+       }
+       leaf full-name {
+         type string;
+       }
+       leaf class {
+         type string;
+       }
+     }
+
+    container container1 {
+        description
+            "A container";
+        choice name {
+             case acase {
+                    leaf a {
+                        type boolean;
+                        description
+                        "A test leaf for uint8";
+                    }
+             }
+             case bcase {
+                     leaf b {
+                       type binary;
+                       description "i am nine";
+                    }
+             }
+        }
+        leaf mleaf {
+             type string;
+             description "i am string";
+        }
+        leaf-list mleaf_list {
+             type string;
+             description "i am repeated string";
+        }
+        container inner-container {
+            description
+                "A container";
+            leaf mleaf1 {
+                 type string;
+                 description "i am leaf1";
+            }
+            leaf mleaf2 {
+                 type string;
+                 description "i am leaf2";
+            }
+        }
+    }
+}
diff --git a/experiments/netconf/tests/yang2proto/openconfig-extensions.yang b/experiments/netconf/tests/yang2proto/openconfig-extensions.yang
new file mode 100644
index 0000000..d5e9442
--- /dev/null
+++ b/experiments/netconf/tests/yang2proto/openconfig-extensions.yang
@@ -0,0 +1,69 @@
+module openconfig-extensions {
+
+  yang-version "1";
+
+  // namespace
+  namespace "http://openconfig.net/yang/openconfig-ext";
+
+  prefix "ocext";
+
+  // meta
+  organization "OpenConfig working group";
+
+  contact
+    "OpenConfig working group
+    www.openconfig.net";
+
+  description
+    "This module provides extensions to the YANG language to allow
+    OpenConfig specific functionality and meta-data to be defined.";
+
+  revision "2015-10-09" {
+    description
+      "Initial OpenConfig public release";
+  }
+
+  revision "2015-10-05" {
+    description
+      "Initial revision";
+    reference "TBD";
+  }
+
+  // extension statements
+  extension openconfig-version {
+    argument "semver" {
+      yin-element false;
+    }
+    description
+      "The OpenConfig version number for the module. This is
+      expressed as a semantic version number of the form:
+        x.y.z
+      where:
+        * x corresponds to the major version,
+        * y corresponds to a minor version,
+        * z corresponds to a patch version.
+      This version corresponds to the model file within which it is
+      defined, and does not cover the whole set of OpenConfig models.
+      Where several modules are used to build up a single block of
+      functionality, the same module version is specified across each
+      file that makes up the module.
+
+      A major version number of 0 indicates that this model is still
+      in development (whether within OpenConfig or with industry
+      partners), and is potentially subject to change.
+
+      Following a release of major version 1, all modules will
+      increment major revision number where backwards incompatible
+      changes to the model are made.
+
+      The minor version is changed when features are added to the
+      model that do not impact current clients use of the model.
+
+      The patch-level version is incremented when non-feature changes
+      (such as bugfixes or clarifications to human-readable
+      descriptions that do not impact model functionality) are made
+      that maintain backwards compatibility.
+
+      The version number is stored in the module meta-data.";
+  }
+}
\ No newline at end of file
diff --git a/experiments/netconf/tests/yang2proto/openconfig-interfaces.yang b/experiments/netconf/tests/yang2proto/openconfig-interfaces.yang
new file mode 100644
index 0000000..332f7e1
--- /dev/null
+++ b/experiments/netconf/tests/yang2proto/openconfig-interfaces.yang
@@ -0,0 +1,933 @@
+module openconfig-interfaces {
+
+  yang-version "1";
+
+  // namespace
+  namespace "http://openconfig.net/yang/interfaces";
+
+  prefix "oc-if";
+
+  // import some basic types
+  import ietf-interfaces { prefix ietf-if; }
+  import ietf-yang-types { prefix yang; }
+  import openconfig-extensions { prefix oc-ext; }
+
+  // meta
+  organization "OpenConfig working group";
+
+  contact
+    "OpenConfig working group
+    netopenconfig@googlegroups.com";
+
+  description
+    "Model for managing network interfaces and subinterfaces.  This
+    module also defines convenience types / groupings for other
+    models to create references to interfaces:
+
+      base-interface-ref (type) -  reference to a base interface
+      interface-ref (grouping) -  container for reference to a
+        interface + subinterface
+      interface-ref-state (grouping) - container for read-only
+        (opstate) reference to interface + subinterface
+
+    This model reuses data items defined in the IETF YANG model for
+    interfaces described by RFC 7223 with an alternate structure
+    (particularly for operational state data) and and with
+    additional configuration items.";
+
+  oc-ext:openconfig-version "1.0.2";
+
+  revision "2016-05-26" {
+    description
+      "OpenConfig public release";
+    reference "1.0.2";
+  }
+
+
+  // typedef statements
+
+  typedef base-interface-ref {
+    type leafref {
+      path "/oc-if:interfaces/oc-if:interface/oc-if:name";
+    }
+    description
+      "Reusable type for by-name reference to a base interface.
+      This type may be used in cases where ability to reference
+      a subinterface is not required.";
+  }
+
+  typedef interface-id {
+    type string;
+    description
+      "User-defined identifier for an interface, generally used to
+      name a interface reference.  The id can be arbitrary but a
+      useful convention is to use a combination of base interface
+      name and subinterface index.";
+  }
+
+  // grouping statements
+
+  grouping interface-ref-common {
+    description
+      "Reference leafrefs to interface / subinterface";
+
+    leaf interface {
+      type leafref {
+        path "/oc-if:interfaces/oc-if:interface/oc-if:name";
+      }
+      description
+        "Reference to a base interface.  If a reference to a
+        subinterface is required, this leaf must be specified
+        to indicate the base interface.";
+    }
+
+    leaf subinterface {
+      type leafref {
+        path "/oc-if:interfaces/" +
+          "oc-if:interface[oc-if:name=current()/../interface]/" +
+          "oc-if:subinterfaces/oc-if:subinterface/oc-if:index";
+      }
+      description
+        "Reference to a subinterface -- this requires the base
+        interface to be specified using the interface leaf in
+        this container.  If only a reference to a base interface
+        is requuired, this leaf should not be set.";
+    }
+  }
+
+  grouping interface-ref-state-container {
+    description
+      "Reusable opstate w/container for a reference to an
+      interface or subinterface";
+
+    container state {
+      config false;
+      description
+        "Operational state for interface-ref";
+
+      uses interface-ref-common;
+    }
+  }
+
+  grouping interface-ref {
+    description
+      "Reusable definition for a reference to an interface or
+      subinterface";
+
+    container interface-ref {
+      description
+        "Reference to an interface or subinterface";
+
+      container config {
+        description
+          "Configured reference to interface / subinterface";
+
+        uses interface-ref-common;
+      }
+
+      uses interface-ref-state-container;
+    }
+  }
+
+  grouping interface-ref-state {
+    description
+      "Reusable opstate w/container for a reference to an
+      interface or subinterface";
+
+    container interface-ref {
+      description
+        "Reference to an interface or subinterface";
+
+      uses interface-ref-state-container;
+    }
+  }
+
+
+  grouping interface-common-config {
+    description
+      "Configuration data data nodes common to physical interfaces
+      and subinterfaces";
+
+    leaf name {
+      type string;
+      description
+        "[adapted from IETF interfaces model (RFC 7223)]
+
+        The name of the interface.
+
+        A device MAY restrict the allowed values for this leaf,
+        possibly depending on the type of the interface.
+        For system-controlled interfaces, this leaf is the
+        device-specific name of the interface.  The 'config false'
+        list interfaces/interface[name]/state contains the currently
+        existing interfaces on the device.
+
+        If a client tries to create configuration for a
+        system-controlled interface that is not present in the
+        corresponding state list, the server MAY reject
+        the request if the implementation does not support
+        pre-provisioning of interfaces or if the name refers to
+        an interface that can never exist in the system.  A
+        NETCONF server MUST reply with an rpc-error with the
+        error-tag 'invalid-value' in this case.
+
+        The IETF model in RFC 7223 provides YANG features for the
+        following (i.e., pre-provisioning and arbitrary-names),
+        however they are omitted here:
+
+          If the device supports pre-provisioning of interface
+          configuration, the 'pre-provisioning' feature is
+          advertised.
+
+          If the device allows arbitrarily named user-controlled
+          interfaces, the 'arbitrary-names' feature is advertised.
+
+        When a configured user-controlled interface is created by
+        the system, it is instantiated with the same name in the
+        /interfaces/interface[name]/state list.";
+      reference
+        "RFC 7223: A YANG Data Model for Interface Management";
+    }
+
+    leaf description {
+      type string;
+      description
+        "[adapted from IETF interfaces model (RFC 7223)]
+
+        A textual description of the interface.
+
+        A server implementation MAY map this leaf to the ifAlias
+        MIB object.  Such an implementation needs to use some
+        mechanism to handle the differences in size and characters
+        allowed between this leaf and ifAlias.  The definition of
+        such a mechanism is outside the scope of this document.
+
+        Since ifAlias is defined to be stored in non-volatile
+        storage, the MIB implementation MUST map ifAlias to the
+        value of 'description' in the persistently stored
+        datastore.
+
+        Specifically, if the device supports ':startup', when
+        ifAlias is read the device MUST return the value of
+        'description' in the 'startup' datastore, and when it is
+        written, it MUST be written to the 'running' and 'startup'
+        datastores.  Note that it is up to the implementation to
+
+        decide whether to modify this single leaf in 'startup' or
+        perform an implicit copy-config from 'running' to
+        'startup'.
+
+        If the device does not support ':startup', ifAlias MUST
+        be mapped to the 'description' leaf in the 'running'
+        datastore.";
+      reference
+        "RFC 2863: The Interfaces Group MIB - ifAlias";
+    }
+
+    leaf enabled {
+      type boolean;
+      default "true";
+      description
+        "[adapted from IETF interfaces model (RFC 7223)]
+
+        This leaf contains the configured, desired state of the
+        interface.
+
+        Systems that implement the IF-MIB use the value of this
+        leaf in the 'running' datastore to set
+        IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry
+        has been initialized, as described in RFC 2863.
+
+        Changes in this leaf in the 'running' datastore are
+        reflected in ifAdminStatus, but if ifAdminStatus is
+        changed over SNMP, this leaf is not affected.";
+      reference
+        "RFC 2863: The Interfaces Group MIB - ifAdminStatus";
+    }
+
+  }
+
+  grouping interface-phys-config {
+    description
+      "Configuration data for physical interfaces";
+
+    leaf type {
+      type identityref {
+        base ietf-if:interface-type;
+      }
+      mandatory true;
+      description
+        "[adapted from IETF interfaces model (RFC 7223)]
+
+        The type of the interface.
+
+        When an interface entry is created, a server MAY
+        initialize the type leaf with a valid value, e.g., if it
+        is possible to derive the type from the name of the
+        interface.
+
+        If a client tries to set the type of an interface to a
+        value that can never be used by the system, e.g., if the
+        type is not supported or if the type does not match the
+        name of the interface, the server MUST reject the request.
+        A NETCONF server MUST reply with an rpc-error with the
+        error-tag 'invalid-value' in this case.";
+      reference
+        "RFC 2863: The Interfaces Group MIB - ifType";
+    }
+
+    leaf mtu {
+      type uint16;
+      description
+        "Set the max transmission unit size in octets
+        for the physical interface.  If this is not set, the mtu is
+        set to the operational default -- e.g., 1514 bytes on an
+        Ethernet interface.";
+    }
+
+    uses interface-common-config;
+  }
+
+  grouping interface-phys-holdtime-config {
+    description
+      "Configuration data for interface hold-time settings --
+      applies to physical interfaces.";
+
+    leaf up {
+      type uint32;
+      units milliseconds;
+      default 0;
+      description
+        "Dampens advertisement when the interface
+        transitions from down to up.  A zero value means dampening
+        is turned off, i.e., immediate notification.";
+    }
+
+    leaf down {
+      type uint32;
+      units milliseconds;
+      default 0;
+      description
+        "Dampens advertisement when the interface transitions from
+        up to down.  A zero value means dampening is turned off,
+        i.e., immediate notification.";
+    }
+  }
+
+  grouping interface-phys-holdtime-state {
+    description
+      "Operational state data for interface hold-time.";
+  }
+
+  grouping interface-phys-holdtime-top {
+    description
+      "Top-level grouping for setting link transition
+      dampening on physical and other types of interfaces.";
+
+    container hold-time {
+      description
+        "Top-level container for hold-time settings to enable
+        dampening advertisements of interface transitions.";
+
+      container config {
+        description
+          "Configuration data for interface hold-time settings.";
+
+        uses interface-phys-holdtime-config;
+      }
+
+      container state {
+
+        config false;
+
+        description
+          "Operational state data for interface hold-time.";
+
+        uses interface-phys-holdtime-config;
+        uses interface-phys-holdtime-state;
+      }
+    }
+  }
+
+  grouping interface-common-state {
+    description
+      "Operational state data (in addition to intended configuration)
+      at the global level for this interface";
+
+    leaf ifindex {
+      type uint32;
+      description
+        "System assigned number for each interface.  Corresponds to
+        ifIndex object in SNMP Interface MIB";
+      reference
+        "RFC 2863 - The Interfaces Group MIB";
+    }
+
+    leaf admin-status {
+      type enumeration {
+        enum UP {
+          description
+            "Ready to pass packets.";
+        }
+        enum DOWN {
+          description
+            "Not ready to pass packets and not in some test mode.";
+        }
+        enum TESTING {
+          //TODO: This is generally not supported as a configured
+          //admin state, though it's in the standard interfaces MIB.
+          //Consider removing it.
+          description
+            "In some test mode.";
+        }
+      }
+      //TODO:consider converting to an identity to have the
+      //flexibility to remove some values defined by RFC 7223 that
+      //are not used or not implemented consistently.
+      mandatory true;
+      description
+        "[adapted from IETF interfaces model (RFC 7223)]
+
+        The desired state of the interface.  In RFC 7223 this leaf
+        has the same read semantics as ifAdminStatus.  Here, it
+        reflects the administrative state as set by enabling or
+        disabling the interface.";
+      reference
+        "RFC 2863: The Interfaces Group MIB - ifAdminStatus";
+    }
+
+    leaf oper-status {
+      type enumeration {
+        enum UP {
+          value 1;
+          description
+            "Ready to pass packets.";
+        }
+        enum DOWN {
+          value 2;
+          description
+            "The interface does not pass any packets.";
+        }
+        enum TESTING {
+          value 3;
+          description
+            "In some test mode.  No operational packets can
+             be passed.";
+        }
+        enum UNKNOWN {
+          value 4;
+          description
+            "Status cannot be determined for some reason.";
+        }
+        enum DORMANT {
+          value 5;
+          description
+            "Waiting for some external event.";
+        }
+        enum NOT_PRESENT {
+          value 6;
+          description
+            "Some component (typically hardware) is missing.";
+        }
+        enum LOWER_LAYER_DOWN {
+          value 7;
+          description
+            "Down due to state of lower-layer interface(s).";
+        }
+      }
+      //TODO:consider converting to an identity to have the
+      //flexibility to remove some values defined by RFC 7223 that
+      //are not used or not implemented consistently.
+      mandatory true;
+      description
+        "[adapted from IETF interfaces model (RFC 7223)]
+
+        The current operational state of the interface.
+
+         This leaf has the same semantics as ifOperStatus.";
+      reference
+        "RFC 2863: The Interfaces Group MIB - ifOperStatus";
+    }
+
+    leaf last-change {
+      type yang:timeticks;
+      description
+        "Date and time of the last state change of the interface
+        (e.g., up-to-down transition).   This corresponds to the
+        ifLastChange object in the standard interface MIB.";
+      reference
+        "RFC 2863: The Interfaces Group MIB - ifLastChange";
+    }
+
+  }
+
+
+  grouping interface-counters-state {
+    description
+      "Operational state representing interface counters
+      and statistics.  Some of these are adapted from RFC 7223";
+
+      //TODO: we may need to break this list of counters into those
+      //that would appear for physical vs. subinterface or logical
+      //interfaces.  For now, just replicating the full stats
+      //grouping to both interface and subinterface.
+
+    container counters {
+      description
+        "A collection of interface-related statistics objects.";
+
+      reference
+        "RFC 7223 - A YANG Data Model for Interface
+        Management";
+
+      leaf in-octets {
+        type yang:counter64;
+        description
+          "[adapted from IETF interfaces model (RFC 7223)]
+
+          The total number of octets received on the interface,
+          including framing characters.
+
+          Discontinuities in the value of this counter can occur
+          at re-initialization of the management system, and at
+          other times as indicated by the value of
+          'discontinuity-time'.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifHCInOctets";
+      }
+
+      leaf in-unicast-pkts {
+        type yang:counter64;
+        description
+          "[adapted from IETF interfaces model (RFC 7223)]
+
+          The number of packets, delivered by this sub-layer to a
+          higher (sub-)layer, that were not addressed to a
+          multicast or broadcast address at this sub-layer.
+
+          Discontinuities in the value of this counter can occur
+          at re-initialization of the management system, and at
+          other times as indicated by the value of
+          'discontinuity-time'.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts";
+      }
+
+      leaf in-broadcast-pkts {
+        type yang:counter64;
+        description
+          "[adapted from IETF interfaces model (RFC 7223)]
+
+          The number of packets, delivered by this sub-layer to a
+          higher (sub-)layer, that were addressed to a broadcast
+          address at this sub-layer.
+
+          Discontinuities in the value of this counter can occur
+          at re-initialization of the management system, and at
+          other times as indicated by the value of
+          'discontinuity-time'.";
+        reference
+          "RFC 2863: The Interfaces Group MIB -
+                     ifHCInBroadcastPkts";
+      }
+
+      leaf in-multicast-pkts {
+        type yang:counter64;
+        description
+          "[adapted from IETF interfaces model (RFC 7223)]
+
+
+          The number of packets, delivered by this sub-layer to a
+          higher (sub-)layer, that were addressed to a multicast
+          address at this sub-layer.  For a MAC-layer protocol,
+          this includes both Group and Functional addresses.
+
+          Discontinuities in the value of this counter can occur
+          at re-initialization of the management system, and at
+          other times as indicated by the value of
+          'discontinuity-time'.";
+        reference
+          "RFC 2863: The Interfaces Group MIB -
+                     ifHCInMulticastPkts";
+      }
+
+      leaf in-discards {
+        type yang:counter64;
+        description
+          "[adapted from IETF interfaces model (RFC 7223)]
+          Changed the counter type to counter64.
+
+          The number of inbound packets that were chosen to be
+          discarded even though no errors had been detected to
+          prevent their being deliverable to a higher-layer
+          protocol.  One possible reason for discarding such a
+          packet could be to free up buffer space.
+
+          Discontinuities in the value of this counter can occur
+          at re-initialization of the management system, and at
+          other times as indicated by the value of
+          'discontinuity-time'.";
+
+
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifInDiscards";
+      }
+
+      leaf in-errors {
+        type yang:counter64;
+        description
+          "[adapted from IETF interfaces model (RFC 7223)]
+          Changed the counter type to counter64.
+
+          For packet-oriented interfaces, the number of inbound
+          packets that contained errors preventing them from being
+          deliverable to a higher-layer protocol.  For character-
+          oriented or fixed-length interfaces, the number of
+          inbound transmission units that contained errors
+          preventing them from being deliverable to a higher-layer
+          protocol.
+
+          Discontinuities in the value of this counter can occur
+          at re-initialization of the management system, and at
+          other times as indicated by the value of
+          'discontinuity-time'.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifInErrors";
+      }
+
+      leaf in-unknown-protos {
+        type yang:counter32;
+        description
+          "[adapted from IETF interfaces model (RFC 7223)]
+          Changed the counter type to counter64.
+
+          For packet-oriented interfaces, the number of packets
+          received via the interface that were discarded because
+          of an unknown or unsupported protocol.  For
+          character-oriented or fixed-length interfaces that
+          support protocol multiplexing, the number of
+          transmission units received via the interface that were
+          discarded because of an unknown or unsupported protocol.
+          For any interface that does not support protocol
+          multiplexing, this counter is not present.
+
+          Discontinuities in the value of this counter can occur
+          at re-initialization of the management system, and at
+          other times as indicated by the value of
+          'discontinuity-time'.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos";
+      }
+
+      leaf out-octets {
+        type yang:counter64;
+        description
+          "[adapted from IETF interfaces model (RFC 7223)]
+          Changed the counter type to counter64.
+
+          The total number of octets transmitted out of the
+          interface, including framing characters.
+
+          Discontinuities in the value of this counter can occur
+          at re-initialization of the management system, and at
+          other times as indicated by the value of
+          'discontinuity-time'.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifHCOutOctets";
+      }
+
+      leaf out-unicast-pkts {
+        type yang:counter64;
+        description
+          "[adapted from IETF interfaces model (RFC 7223)]
+
+          The total number of packets that higher-level protocols
+          requested be transmitted, and that were not addressed
+          to a multicast or broadcast address at this sub-layer,
+          including those that were discarded or not sent.
+
+          Discontinuities in the value of this counter can occur
+          at re-initialization of the management system, and at
+          other times as indicated by the value of
+          'discontinuity-time'.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts";
+      }
+
+      leaf out-broadcast-pkts {
+        type yang:counter64;
+        description
+          "[adapted from IETF interfaces model (RFC 7223)]
+
+          The total number of packets that higher-level protocols
+          requested be transmitted, and that were addressed to a
+          broadcast address at this sub-layer, including those
+          that were discarded or not sent.
+
+          Discontinuities in the value of this counter can occur
+          at re-initialization of the management system, and at
+          other times as indicated by the value of
+          'discontinuity-time'.";
+        reference
+          "RFC 2863: The Interfaces Group MIB -
+                     ifHCOutBroadcastPkts";
+      }
+
+
+      leaf out-multicast-pkts {
+        type yang:counter64;
+        description
+          "[adapted from IETF interfaces model (RFC 7223)]
+          Changed the counter type to counter64.
+
+          The total number of packets that higher-level protocols
+          requested be transmitted, and that were addressed to a
+          multicast address at this sub-layer, including those
+          that were discarded or not sent.  For a MAC-layer
+          protocol, this includes both Group and Functional
+          addresses.
+
+          Discontinuities in the value of this counter can occur
+          at re-initialization of the management system, and at
+          other times as indicated by the value of
+          'discontinuity-time'.";
+        reference
+          "RFC 2863: The Interfaces Group MIB -
+                     ifHCOutMulticastPkts";
+      }
+
+      leaf out-discards {
+        type yang:counter64;
+        description
+          "[adapted from IETF interfaces model (RFC 7223)]
+          Changed the counter type to counter64.
+
+          The number of outbound packets that were chosen to be
+          discarded even though no errors had been detected to
+          prevent their being transmitted.  One possible reason
+          for discarding such a packet could be to free up buffer
+          space.
+
+          Discontinuities in the value of this counter can occur
+          at re-initialization of the management system, and at
+          other times as indicated by the value of
+          'discontinuity-time'.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifOutDiscards";
+      }
+
+      leaf out-errors {
+        type yang:counter64;
+        description
+          "[adapted from IETF interfaces model (RFC 7223)]
+          Changed the counter type to counter64.
+
+          For packet-oriented interfaces, the number of outbound
+          packets that could not be transmitted because of errors.
+          For character-oriented or fixed-length interfaces, the
+          number of outbound transmission units that could not be
+          transmitted because of errors.
+
+          Discontinuities in the value of this counter can occur
+          at re-initialization of the management system, and at
+          other times as indicated by the value of
+          'discontinuity-time'.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifOutErrors";
+      }
+
+      leaf last-clear {
+        type yang:date-and-time;
+        description
+          "Indicates the last time the interface counters were
+          cleared.";
+      }
+    }
+  }
+
+  // data definition statements
+
+  grouping sub-unnumbered-config {
+    description
+      "Configuration data for unnumbered subinterfaces";
+
+    leaf enabled {
+      type boolean;
+      default false;
+      description
+        "Indicates that the subinterface is unnumbered.  By default
+        the subinterface is numbered, i.e., expected to have an
+        IP address configuration.";
+    }
+  }
+
+  grouping sub-unnumbered-state {
+    description
+      "Operational state data unnumbered subinterfaces";
+  }
+
+  grouping sub-unnumbered-top {
+    description
+      "Top-level grouping unnumbered subinterfaces";
+
+    container unnumbered {
+      description
+        "Top-level container for setting unnumbered interfaces.
+        Includes reference the interface that provides the
+        address information";
+
+      container config {
+        description
+          "Configuration data for unnumbered interface";
+
+        uses sub-unnumbered-config;
+      }
+
+      container state {
+
+        config false;
+
+        description
+          "Operational state data for unnumbered interfaces";
+
+        uses sub-unnumbered-config;
+        uses sub-unnumbered-state;
+      }
+
+      uses oc-if:interface-ref;
+    }
+  }
+
+  grouping subinterfaces-config {
+    description
+      "Configuration data for subinterfaces";
+
+    leaf index {
+      type uint32;
+      default 0;
+      description
+        "The index of the subinterface, or logical interface number.
+        On systems with no support for subinterfaces, or not using
+        subinterfaces, this value should default to 0, i.e., the
+        default subinterface.";
+    }
+
+    uses interface-common-config;
+
+  }
+
+  grouping subinterfaces-state {
+    description
+      "Operational state data for subinterfaces";
+
+    uses interface-common-state;
+    uses interface-counters-state;
+  }
+
+  grouping subinterfaces-top {
+    description
+      "Subinterface data for logical interfaces associated with a
+      given interface";
+
+    container subinterfaces {
+      description
+        "Enclosing container for the list of subinterfaces associated
+        with a physical interface";
+
+      list subinterface {
+        key "index";
+
+        description
+          "The list of subinterfaces (logical interfaces) associated
+          with a physical interface";
+
+        leaf index {
+          type leafref {
+            path "../config/index";
+          }
+          description
+            "The index number of the subinterface -- used to address
+            the logical interface";
+        }
+
+        container config {
+          description
+            "Configurable items at the subinterface level";
+
+          uses subinterfaces-config;
+        }
+
+        container state {
+
+          config false;
+          description
+            "Operational state data for logical interfaces";
+
+          uses subinterfaces-config;
+          uses subinterfaces-state;
+        }
+      }
+    }
+  }
+
+  grouping interfaces-top {
+    description
+      "Top-level grouping for interface configuration and
+      operational state data";
+
+    container interfaces {
+      description
+        "Top level container for interfaces, including configuration
+        and state data.";
+
+
+      list interface {
+        key "name";
+
+        description
+          "The list of named interfaces on the device.";
+
+        leaf name {
+          type leafref {
+            path "../config/name";
+          }
+          description
+            "References the configured name of the interface";
+            //TODO: need to consider whether this should actually
+            //reference the name in the state subtree, which
+            //presumably would be the system-assigned name, or the
+            //configured name.  Points to the config/name now
+            //because of YANG 1.0 limitation that the list
+            //key must have the same "config" as the list, and
+            //also can't point to a non-config node.
+        }
+
+        container config {
+          description
+            "Configurable items at the global, physical interface
+            level";
+
+          uses interface-phys-config;
+        }
+
+        container state {
+
+          config false;
+          description
+            "Operational state data at the global interface level";
+
+          uses interface-phys-config;
+          uses interface-common-state;
+          uses interface-counters-state;
+        }
+
+        uses interface-phys-holdtime-top;
+        uses subinterfaces-top;
+      }
+    }
+  }
+
+  uses interfaces-top;
+
+
+}
diff --git a/experiments/netconf/tests/yang2proto/openconfig-local-routing.yang b/experiments/netconf/tests/yang2proto/openconfig-local-routing.yang
new file mode 100644
index 0000000..556bbee
--- /dev/null
+++ b/experiments/netconf/tests/yang2proto/openconfig-local-routing.yang
@@ -0,0 +1,402 @@
+module openconfig-local-routing {
+
+  yang-version "1";
+
+  // namespace
+  namespace "http://openconfig.net/yang/local-routing";
+
+  prefix "oc-loc-rt";
+
+  // import some basic types
+  import ietf-inet-types { prefix inet; }
+  import openconfig-policy-types { prefix oc-pt; }
+  import openconfig-extensions { prefix oc-ext; }
+  import openconfig-interfaces { prefix oc-if; }
+
+  // meta
+  organization "OpenConfig working group";
+
+  contact
+    "OpenConfig working group
+    www.openconfig.net";
+
+  description
+    "This module describes configuration and operational state data
+    for routes that are locally generated, i.e., not created by
+    dynamic routing protocols.  These include static routes, locally
+    created aggregate routes for reducing the number of constituent
+    routes that must be advertised, summary routes for IGPs, etc.
+
+    This model expresses locally generated routes as generically as
+    possible, avoiding configuration of protocol-specific attributes
+    at the time of route creation.  This is primarily to avoid
+    assumptions about how underlying router implementations handle
+    route attributes in various routing table data structures they
+    maintain.  Hence, the definition of locally generated routes
+    essentially creates 'bare' routes that do not have any protocol-
+    specific attributes.
+
+    When protocol-specific attributes must be attached to a route
+    (e.g., communities on a locally defined route meant to be
+    advertised via BGP), the attributes should be attached via a
+    protocol-specific policy after importing the route into the
+    protocol for distribution (again via routing policy).";
+
+  oc-ext:openconfig-version "1.0.0";
+
+  revision "2016-05-11" {
+    description
+      "OpenConfig public release";
+    reference "1.0.0";
+  }
+
+
+  // identity statements
+
+  identity LOCAL_DEFINED_NEXT_HOP {
+    description
+      "A base identity type of local defined next-hops";
+  }
+
+  identity DROP {
+    base LOCAL_DEFINED_NEXT_HOP;
+    description
+      "Discard traffic for the corresponding destination";
+  }
+
+  identity LOCAL_LINK {
+    base LOCAL_DEFINED_NEXT_HOP;
+    description
+      "Treat traffic towards addresses within the specified
+      next-hop prefix as though they are connected to a local
+      link. When the LOCAL_LINK next-hop type is specified,
+      an interface must also be specified such that
+      the local system can determine which link to trigger
+      link-layer address discovery against";
+  }
+
+  // typedef statements
+
+  typedef local-defined-next-hop {
+    type identityref {
+      base LOCAL_DEFINED_NEXT_HOP;
+    }
+    description
+      "Pre-defined next-hop designation for locally generated
+      routes";
+  }
+
+  // grouping statements
+
+  grouping local-generic-settings {
+    description
+      "Generic options that can be set on local routes When
+      they are defined";
+
+    leaf set-tag {
+      type oc-pt:tag-type;
+      description
+        "Set a generic tag value on the route. This tag can be
+        used for filtering routes that are distributed to other
+        routing protocols.";
+    }
+  }
+
+  grouping local-static-config {
+    description
+      "Configuration data for static routes.";
+
+    leaf prefix {
+      type inet:ip-prefix;
+      description
+        "Destination prefix for the static route, either IPv4 or
+        IPv6.";
+    }
+
+    uses local-generic-settings;
+  }
+
+  grouping local-static-state {
+    description
+      "Operational state data for static routes";
+  }
+
+
+  grouping local-static-nexthop-config {
+    description
+      "Configuration parameters related to each next-hop entry
+      specified for a static route";
+
+    leaf index {
+      type string;
+      description
+        "An user-specified identifier utilised to uniquely reference
+        the next-hop entry in the next-hop list. The value of this
+        index has no semantic meaning other than for referencing
+        the entry.";
+    }
+
+    leaf next-hop {
+      type union {
+        type inet:ip-address-no-zone;
+        type local-defined-next-hop;
+      }
+      description
+        "The next-hop that is to be used for the static route
+        - this may be specified as an IP address, an interface
+        or a pre-defined next-hop type - for instance, DROP or
+        LOCAL_LINK. When this leaf is not set, and the interface-ref
+        value is specified for the next-hop, then the system should
+        treat the prefix as though it is directly connected to the
+        interface.";
+    }
+
+    leaf metric {
+      type uint32;
+      description
+        "A metric which is utilised to specify the preference of
+        the next-hop entry when it is injected into the RIB. The
+        lower the metric, the more preferable the prefix is. When
+        this value is not specified the metric is inherited from
+        the default metric utilised for static routes within the
+        network instance that the static routes are being
+        instantiated. When multiple next-hops are specified for a
+        static route, the metric is utilised to determine which of
+        the next-hops is to be installed in the RIB. When multiple
+        next-hops have the same metric (be it specified, or simply
+        the default) then these next-hops should all be installed
+        in the RIB";
+    }
+
+    leaf recurse {
+      type boolean;
+      default false;
+      description
+        "Determines whether the next-hop should be allowed to
+        be looked up recursively - i.e., via a RIB entry which has
+        been installed by a routing protocol, or another static route
+        - rather than needing to be connected directly to an
+        interface of the local system within the current network
+        instance. When the interface reference specified within the
+        next-hop entry is set (i.e., is not null) then forwarding is
+        restricted to being via the interface specified - and
+        recursion is hence disabled.";
+    }
+  }
+
+  grouping local-static-nexthop-state {
+    description
+      "Operational state parameters relating to a next-hop entry
+      for a static route";
+  }
+
+
+  grouping local-static-top {
+    description
+      "Top-level grouping for the list of static route definitions";
+
+    container static-routes {
+      description
+        "Enclosing container for the list of static routes";
+
+      list static {
+        key prefix;
+        description
+          "List of locally configured static routes";
+
+        leaf prefix {
+          type leafref {
+            path "../config/prefix";
+          }
+          description
+            "Reference to the destination prefix list key.";
+        }
+
+        container config {
+          description
+            "Configuration data for static routes";
+
+          uses local-static-config;
+        }
+
+        container state {
+
+          config false;
+
+          description
+            "Operational state data for static routes";
+
+          uses local-static-config;
+          uses local-static-state;
+        }
+
+        container next-hops {
+          description
+            "Configuration and state parameters relating to the
+            next-hops that are to be utilised for the static
+            route being specified";
+
+          list next-hop {
+            key index;
+
+            description
+              "A list of next-hops to be utilised for the static
+              route being specified.";
+
+            leaf index {
+              type leafref {
+                path "../config/index";
+              }
+              description
+                "A reference to the index of the current next-hop.
+                The index is intended to be a user-specified value
+                which can be used to reference the next-hop in
+                question, without any other semantics being
+                assigned to it.";
+            }
+
+            container config {
+              description
+                "Configuration parameters relating to the next-hop
+                entry";
+
+              uses local-static-nexthop-config;
+            }
+
+            container state {
+              config false;
+              description
+                "Operational state parameters relating to the
+                next-hop entry";
+
+              uses local-static-nexthop-config;
+              uses local-static-nexthop-state;
+            }
+
+            uses oc-if:interface-ref;
+          }
+        }
+      }
+    }
+  }
+
+  grouping local-aggregate-config {
+    description
+      "Configuration data for aggregate routes";
+
+    leaf prefix {
+      type inet:ip-prefix;
+      description
+        "Aggregate prefix to be advertised";
+    }
+
+    leaf discard {
+      type boolean;
+      default false;
+      description
+        "When true, install the aggregate route with a discard
+        next-hop -- traffic destined to the aggregate will be
+        discarded with no ICMP message generated.  When false,
+        traffic destined to an aggregate address when no
+        constituent routes are present will generate an ICMP
+        unreachable message.";
+    }
+
+    uses local-generic-settings;
+
+  }
+
+  grouping local-aggregate-state {
+    description
+      "Operational state data for local aggregate advertisement
+      definitions";
+  }
+
+  grouping local-aggregate-top {
+    description
+      "Top-level grouping for local aggregates";
+
+    container local-aggregates {
+      description
+        "Enclosing container for locally-defined aggregate
+        routes";
+
+      list aggregate {
+        key prefix;
+        description
+          "List of aggregates";
+
+        leaf prefix {
+          type leafref {
+            path "../config/prefix";
+          }
+          description
+            "Reference to the configured prefix for this aggregate";
+        }
+
+        container config {
+          description
+            "Configuration data for aggregate advertisements";
+
+          uses local-aggregate-config;
+        }
+
+        container state {
+
+          config false;
+
+          description
+            "Operational state data for aggregate
+            advertisements";
+
+          uses local-aggregate-config;
+          uses local-aggregate-state;
+        }
+      }
+    }
+  }
+
+  grouping local-routes-config {
+    description
+      "Configuration data for locally defined routes";
+  }
+
+  grouping local-routes-state {
+    description
+      "Operational state data for locally defined routes";
+  }
+
+  grouping local-routes-top {
+    description
+      "Top-level grouping for local routes";
+
+    container local-routes {
+      description
+        "Top-level container for local routes";
+
+      container config {
+        description
+          "Configuration data for locally defined routes";
+
+        uses local-routes-config;
+      }
+
+      container state {
+
+        config false;
+
+        description
+          "Operational state data for locally defined routes";
+
+        uses local-routes-config;
+        uses local-routes-state;
+      }
+
+      uses local-static-top;
+      uses local-aggregate-top;
+    }
+  }
+
+  uses local-routes-top;
+
+}
diff --git a/experiments/netconf/tests/yang2proto/openconfig-policy-types.yang b/experiments/netconf/tests/yang2proto/openconfig-policy-types.yang
new file mode 100644
index 0000000..8e1fe36
--- /dev/null
+++ b/experiments/netconf/tests/yang2proto/openconfig-policy-types.yang
@@ -0,0 +1,178 @@
+module openconfig-policy-types {
+
+  yang-version "1";
+
+  // namespace
+  namespace "http://openconfig.net/yang/policy-types";
+
+  prefix "oc-pol-types";
+
+  // import some basic types
+  import ietf-yang-types { prefix yang; }
+  import openconfig-extensions { prefix oc-ext; }
+
+  // meta
+  organization
+    "OpenConfig working group";
+
+  contact
+    "OpenConfig working group
+    netopenconfig@googlegroups.com";
+
+  description
+    "This module contains general data definitions for use in routing
+    policy.  It can be imported by modules that contain protocol-
+    specific policy conditions and actions.";
+
+  oc-ext:openconfig-version "2.0.1";
+
+  revision "2016-05-12" {
+    description
+      "OpenConfig public release";
+    reference "2.0.1";
+  }
+
+  // identity statements
+
+  identity ATTRIBUTE_COMPARISON {
+    description
+      "base type for supported comparison operators on route
+      attributes";
+  }
+
+  identity ATTRIBUTE_EQ {
+    base ATTRIBUTE_COMPARISON;
+    description "== comparison";
+  }
+
+  identity ATTRIBUTE_GE {
+    base ATTRIBUTE_COMPARISON;
+    description ">= comparison";
+  }
+
+  identity ATTRIBUTE_LE {
+    base ATTRIBUTE_COMPARISON;
+    description "<= comparison";
+  }
+
+  typedef match-set-options-type {
+    type enumeration {
+      enum ANY {
+        description "match is true if given value matches any member
+        of the defined set";
+      }
+      enum ALL {
+        description "match is true if given value matches all
+        members of the defined set";
+      }
+      enum INVERT {
+        description "match is true if given value does not match any
+        member of the defined set";
+      }
+    }
+    default ANY;
+    description
+      "Options that govern the behavior of a match statement.  The
+      default behavior is ANY, i.e., the given value matches any
+      of the members of the defined set";
+  }
+
+  typedef match-set-options-restricted-type {
+    type enumeration {
+      enum ANY {
+        description "match is true if given value matches any member
+        of the defined set";
+      }
+      enum INVERT {
+        description "match is true if given value does not match any
+        member of the defined set";
+      }
+    }
+    default ANY;
+    description
+      "Options that govern the behavior of a match statement.  The
+      default behavior is ANY, i.e., the given value matches any
+      of the members of the defined set.  Note this type is a
+      restricted version of the match-set-options-type.";
+      //TODO: restriction on enumerated types is only allowed in
+      //YANG 1.1.  Until then, we will require this additional type
+  }
+
+  grouping attribute-compare-operators {
+    description "common definitions for comparison operations in
+    condition statements";
+
+    leaf operator {
+        type identityref {
+          base ATTRIBUTE_COMPARISON;
+        }
+        description
+          "type of comparison to be performed";
+      }
+
+    leaf value {
+      type uint32;
+      description
+        "value to compare with the community count";
+    }
+  }
+
+  typedef tag-type {
+    type union {
+      type uint32;
+      type yang:hex-string;
+    }
+    description "type for expressing route tags on a local system,
+    including IS-IS and OSPF; may be expressed as either decimal or
+    hexidecimal integer";
+    reference
+      "RFC 2178 OSPF Version 2
+      RFC 5130 A Policy Control Mechanism in IS-IS Using
+      Administrative Tags";
+  }
+
+  identity INSTALL_PROTOCOL_TYPE {
+    description
+      "Base type for protocols which can install prefixes into the
+      RIB";
+  }
+
+  identity BGP {
+    base INSTALL_PROTOCOL_TYPE;
+    description "BGP";
+    reference "RFC 4271";
+  }
+
+  identity ISIS {
+    base INSTALL_PROTOCOL_TYPE;
+    description "IS-IS";
+    reference "ISO/IEC 10589";
+  }
+
+  identity OSPF {
+    base INSTALL_PROTOCOL_TYPE;
+    description "OSPFv2";
+    reference "RFC 2328";
+  }
+
+  identity OSPF3 {
+    base INSTALL_PROTOCOL_TYPE;
+    description "OSPFv3";
+    reference "RFC 5340";
+  }
+
+  identity STATIC {
+    base INSTALL_PROTOCOL_TYPE;
+    description "Locally-installed static route";
+  }
+
+  identity DIRECTLY_CONNECTED {
+    base INSTALL_PROTOCOL_TYPE;
+    description "A directly connected route";
+  }
+
+  identity LOCAL_AGGREGATE {
+    base INSTALL_PROTOCOL_TYPE;
+    description "Locally defined aggregate route";
+  }
+}
diff --git a/experiments/netconf/tests/yang2proto/xos-accessibility.yang b/experiments/netconf/tests/yang2proto/xos-accessibility.yang
new file mode 100644
index 0000000..9cebf1b
--- /dev/null
+++ b/experiments/netconf/tests/yang2proto/xos-accessibility.yang
@@ -0,0 +1,462 @@
+module xos-unreconciled-models {
+  namespace "urn:ietf:params:xml:ns:yang:xos-incomplete";
+  prefix xacc;
+  yang-version 1.1;
+
+  import ietf-yang-types { prefix yang; }
+  import ietf-inet-types { prefix inet; }
+  import xos-types       { prefix xtype; }
+
+  organization
+    "Open Networking Lab (XOS) / Corenova Technologies";
+
+  contact
+    "Larry Peterson <llp@onlab.us>
+     Peter K. Lee <peter@corenova.com>";
+  
+  description
+    "This module contains a collection of core models for XOS.
+
+    Copyright (c) 2016 ON.LAB and the persons identified as authors of
+    the code.  All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, is permitted pursuant to, and subject to the license
+    terms of the Apache License, Version 2.0 which accompanies this
+    distribution, and is available at
+    (http://www.apache.org/licenses/LICENSE-2.0).";
+  
+  revision 2016-09-13 {
+    description "Initial revision.";
+  }
+
+  /* Future Consideration
+  typedef image {
+    type unique-identifier;
+  }
+  typedef controller-network {
+    type unique-identifier;
+  }
+  typedef site {
+    type unique-identifier;
+  }
+  typedef tenant-root-role {
+    type unique-identifier;
+  }
+  typedef slice-role {
+    type unique-identifier;
+  }
+  typedef site-deployment {
+    type unique-identifier;
+  }
+  typedef tenant-privilege {
+    type unique-identifier;
+  }
+  typedef tag {
+    type unique-identifier;
+  }
+  typedef user-credential {
+    type unique-identifier;
+  }
+  typedef invoice {
+    type unique-identifier;
+  }
+  typedef slice-privilege {
+    type unique-identifier;
+  }
+  typedef flavor {
+    type unique-identifier;
+  }
+  typedef port {
+    type unique-identifier;
+  }
+  typedef service-role {
+    type unique-identifier;
+  }
+  typedef controller-site {
+    type unique-identifier;
+  }
+  typedef controller-slice {
+    type unique-identifier;
+  }
+  typedef tenant-role {
+    type unique-identifier;
+  }
+  typedef slice {
+    type unique-identifier;
+  }
+  typedef network {
+    type unique-identifier;
+  }
+  typedef controller-role {
+    type unique-identifier;
+  }
+  typedef diag {
+    type unique-identifier;
+  }
+  typedef service-class {
+    type unique-identifier;
+  }
+  typedef tenant-attribute {
+    type unique-identifier;
+  }
+  typedef site-role {
+    type unique-identifier;
+  }
+  typedef subscriber {
+    type unique-identifier;
+  }
+  typedef instance {
+    type unique-identifier;
+  }
+  typedef charge {
+    type unique-identifier;
+  }
+  typedef program {
+    type unique-identifier;
+  }
+  typedef role {
+    type unique-identifier;
+  }
+  typedef usable-object {
+    type unique-identifier;
+  }
+  typedef node-label {
+    type unique-identifier;
+  }
+  typedef slice-credential {
+    type unique-identifier;
+  }
+  typedef node {
+    type unique-identifier;
+  }
+  typedef address-pool {
+    type unique-identifier;
+  }
+  typedef dashboard-view {
+    type unique-identifier;
+  }
+  typedef network-parameter {
+    type unique-identifier;
+  }
+  typedef image-deployments {
+    type unique-identifier;
+  }
+  typedef controller-user {
+    type unique-identifier;
+  }
+  typedef reserved-resource {
+    type unique-identifier;
+  }
+  typedef network-template {
+    type unique-identifier;
+  }
+  typedef controller-dashboard-view {
+    type unique-identifier;
+  }
+  typedef user-dashboard-view {
+    type unique-identifier;
+  }
+  typedef controller {
+    type unique-identifier;
+  }
+  typedef user {
+    type unique-identifier;
+  }
+  typedef deployment {
+    type unique-identifier;
+  }
+  typedef reservation {
+    type unique-identifier;
+  }
+  typedef site-privilege {
+    type unique-identifier;
+  }
+  typedef payment {
+    type unique-identifier;
+  }
+  typedef tenant {
+    type unique-identifier;
+  }
+  typedef network-slice {
+    type unique-identifier;
+  }
+  typedef account {
+    type unique-identifier;
+  }
+  typedef tenant-root {
+    type unique-identifier;
+  }
+  typedef service {
+    type unique-identifier;
+  }
+  typedef controller-slice-privilege {
+    type unique-identifier;
+  }
+  typedef site-credential {
+    type unique-identifier;
+  }
+  typedef deployment-privilege {
+    type unique-identifier;
+  }
+  typedef network-parameter-type {
+    type unique-identifier;
+  }
+  typedef provider {
+    type unique-identifier;
+  }
+  typedef tenant-with-container {
+    type unique-identifier;
+  }
+  typedef deployment-role {
+    type unique-identifier;
+  }
+  typedef project {
+    type unique-identifier;
+  }
+  typedef tenant-root-privilege {
+    type unique-identifier;
+  }
+  typedef slice-tag {
+    type unique-identifier;
+  }
+  typedef coarse-tenant {
+    type unique-identifier;
+  }
+  typedef router {
+    type unique-identifier;
+  }
+  typedef service-resource {
+    type unique-identifier;
+  }
+  typedef service-privilege {
+    type unique-identifier;
+  }
+  */
+  grouping service-class {
+    uses xos-base;
+    
+    leaf description { 
+      type string;
+    }
+    leaf commitment { 
+      type uint32;
+    }
+    leaf membership-fee { 
+      type uint32;
+    }
+    leaf membership-fee-months { 
+      type uint32;
+    }
+    leaf upgrade-requires-approval { 
+      type boolean;
+    }
+  }
+  grouping invoice {
+    uses xos-base;
+    leaf date { 
+      type xtype:datetime;
+    }
+    leaf account { 
+      type xtype:account; 
+    }
+  }
+
+  grouping charge {
+    uses xos-base;
+    leaf account { 
+      type xtype:account; 
+    }
+    leaf slice { 
+      type xtype:slice; 
+    }
+    leaf kind { 
+      type string;
+    }
+    leaf state { 
+      type string;
+    }
+    leaf date { 
+      type xtype:datetime;
+    }
+    leaf object { 
+      type xtype:object; 
+    }
+    leaf amount { 
+      type decimal64;
+    }
+    leaf core-hours { 
+      type decimal64;
+    }
+    leaf invoice { 
+      type xtype:invoice; 
+    }
+  }
+
+  grouping role {
+    uses xos-base;
+    leaf role-type { 
+      type string;
+    }
+    leaf role { 
+      type string;
+    }
+    leaf description { 
+      type string;
+    }
+    leaf content-type { 
+      type xtype:content-type; 
+    }
+  }
+
+  grouping dashboard-view {
+    uses xos-base;
+    leaf url { 
+      type string;
+    }
+    leaf enabled { 
+      type boolean;
+    }
+  }
+
+  grouping controller-dashboard-view {
+    uses xos-base;
+    leaf controller { 
+      type xtype:controller; 
+    }
+    leaf dashboardView { 
+      type xtype:dashboardView; 
+    }
+    leaf enabled { 
+      type boolean;
+    }
+    leaf url { 
+      type string;
+    }
+  }
+
+  grouping user-dashboard-view {
+    uses xos-base;
+    leaf user { 
+      type xtype:user; 
+    }
+    leaf dashboardView { 
+      type xtype:dashboardView; 
+    }
+    leaf order { 
+      type uint32;
+    }
+  }
+  grouping service-privilege {
+    uses xos-base;
+    leaf user { 
+      type xtype:user; 
+    }
+    leaf service { 
+      type xtype:service; 
+    }
+    leaf role { 
+      type xtype:role; 
+    }
+  }
+
+  grouping payment {
+    uses xos-base;
+    leaf account { 
+      type xtype:account; 
+    }
+    leaf amount { 
+      type decimal64;
+    }
+    leaf date { 
+      type xtype:datetime;
+    }
+  }
+  
+  grouping account {
+    uses xos-base;
+    leaf site { 
+      type xtype:site; 
+    }
+  }
+
+  grouping service-resource {
+    uses xos-base;
+    leaf service-class { 
+      type xtype:service-class; 
+    }
+    
+    leaf max-units-deployment { 
+      type uint32;
+    }
+
+    leaf max-units-node { 
+      type uint32;
+    }
+
+    leaf max-duration { 
+      type uint32;
+    }
+
+    leaf bucket-in-rate { 
+      type uint32;
+    }
+
+    leaf bucket-max-size { 
+      type uint32;
+    }
+
+    leaf cost { 
+      type uint32;
+    }
+
+    leaf calendar-reservable { 
+      type boolean;
+    }
+  }
+
+  grouping diag {
+    uses xos-base;
+    
+  }
+
+  grouping program {
+    uses xos-base;
+    leaf description { 
+      type string;
+    }
+
+    leaf kind { 
+      type string;
+    }
+
+    leaf command { 
+      type string;
+    }
+
+    leaf owner { 
+      type xtype:owner; 
+    }
+
+    leaf contents { 
+      type string;
+    }
+
+    leaf output { 
+      type string;
+    }
+
+    leaf messages { 
+      type string;
+    }
+
+    leaf status { 
+      type string;
+    }
+  }
+
+  grouping usable-object {
+    uses xos-base;
+  }
+
+}
diff --git a/experiments/netconf/tests/yang2proto/xos-controller.yang b/experiments/netconf/tests/yang2proto/xos-controller.yang
new file mode 100644
index 0000000..40ce8a6
--- /dev/null
+++ b/experiments/netconf/tests/yang2proto/xos-controller.yang
@@ -0,0 +1,225 @@
+module xos-controller {
+  namespace "urn:ietf:params:xml:ns:yang:xos-controller";
+  prefix xos;
+  yang-version 1.1;
+
+  import ietf-yang-types { prefix yang; }
+  import ietf-inet-types { prefix inet; }
+  import iana-crypt-hash { prefix ianach; }
+  import yang-meta-types { prefix meta; }
+  import xos-types       { prefix xtype; }
+
+  organization
+   "Open Networking Lab / Corenova Technologies";
+
+  contact
+    "Larry Peterson <llp@onlab.us>
+     Peter K. Lee <peter@corenova.com>";
+  
+  description
+   "This module contains a collection of core models for XOS.  The core
+    models represent primary first-order entities such as Tenant,
+    Subscriber, Provider, and Service.
+
+    Copyright (c) 2016 ON.LAB and the persons identified as authors of
+    the code.  All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, is permitted pursuant to, and subject to the license
+    terms of the Apache License, Version 2.0 which accompanies this
+    distribution, and is available at
+    (http://www.apache.org/licenses/LICENSE-2.0).";
+  
+  revision 2016-07-14 {
+    description "Initial revision.";
+  }
+
+  identity xos-tenant {
+    description "Describes the type of tenant within XOS";
+  }
+  identity xos-subscriber {
+    base xos-tenant;
+  }
+  identity xos-provider {
+    base xos-tenant;
+  }
+  identity xos-service {
+    base xos-provider;
+  }
+  
+  feature synchronizer {
+    description
+      "Enables configuration synchronization to the distributed store.";
+  }
+  /*
+   * Groupings
+   */
+  grouping record {
+    description "Synchronizer-specific properties for model entities";
+    
+    leaf created { type yang:date-and-time; }
+    leaf updated { type yang:date-and-time; }
+    leaf enacted { type yang:date-and-time; }
+    leaf policed { type yang:date-and-time; }
+        
+    leaf writable { type boolean; default true; }
+    leaf locked   { type boolean; default false; }
+    leaf deleted  { type boolean; default false; }
+      
+    leaf dirty {
+      config false;
+      type boolean;
+      default false;
+    }
+      
+    container sync {
+      anydata register {
+        description "scratchpad used by the Observer";
+      }
+      leaf progress {
+        type enumeration {
+          enum provisioning {
+            value 0;
+            description "Provisioning in progress";
+          }
+        }
+      }
+      leaf disabled { type boolean; default false; }
+      leaf enforced { type boolean; default true; }
+
+      list policy {
+        // TODO: how are policy defined/enforced?
+      }
+    }
+    action diff {
+      description "retrieve diff of model state if dirty";
+    }
+    action save {
+      description "trigger save into data store via synchronizer";
+    }
+  }
+  grouping credentials-list {
+    list credential {
+      key token;
+      leaf token {
+        type string;
+      }
+      leaf password {
+        type meta:password;
+      }
+      leaf user {
+        type xtype:unique-identifier;
+      }
+      leaf role {
+        type xtype:access-role;
+      }
+    }
+  }
+  grouping parameter {
+    leaf name { type yang:yang-identifier; }
+    leaf value { type string; }
+    leaf content-type { type string; }
+    leaf object-id { type uint32; }
+  }
+  grouping tenant {
+    leaf id {
+      type xtype:unique-identifier;
+      mandatory true;
+    }
+    leaf name { type string { length 0..255; } }
+    leaf kind {
+      config false;
+      type identityref {
+        base xos-tenant;
+      }
+    }
+    leaf description { type meta:description; }
+    leaf enabled { type boolean; default true; }
+    container record {
+      if-feature synchronizer;
+      uses xos:record;
+    }
+  }
+  grouping subscriber {
+    uses xos:tenant {
+      refine kind { default xos-subscriber; }
+    }
+    leaf connectivity {
+      type enumeration {
+        enum public { description "Public"; }
+        enum private { description "Private"; }
+        enum private-unidirectional { description "Private Uni-directional"; }
+        enum na { description "Not Applicable"; }
+      }
+      default na;
+    }
+  }
+  grouping provider {
+    uses xos:tenant {
+      refine kind { default xos-provider; }
+    }
+    uses xos:credentials-list;
+  }
+  grouping service {
+    uses xos:provider {
+      refine 'name' {
+        description "Name of the Service";
+      }
+    }
+    leaf view-url {
+      type inet:uri;
+    }
+    leaf icon-url {
+      type inet:uri;
+    }
+    leaf published {
+      type boolean;
+      default true;
+    }
+    list keypair {
+      description "collection of public/private key pair(s)";
+      // should be a specific typedef for storing this content
+      leaf public  { type string { length 0..1024; } }
+      leaf private { type string { length 0..1024; } }
+    }
+    list attribute {
+      key name;
+      leaf name  { type string { length 0..128; } }
+      leaf value { type string; }
+      status deprecated;
+      reference "XOS: service-specific-attribute";
+      description "backwards-compatible attribute association";
+    }
+    leaf service-specific-id {
+      type xtype:unique-identifier;
+      status deprecated;
+    }
+    list subscriber {
+      description
+        "Each entry represents a subscriber of the service. Each unique service
+         provider should augment this block with service specific
+         attributes.";
+      key id;
+      uses xos:subscriber;
+      notification subscriber-added;
+      notification subscriber-deleted;
+    }
+    // TODO: need to better understand relationship between Service and VTN
+  }
+  /*
+   * Configuration data nodes (main configuration tree for XOS)
+   */
+  container core {
+    description
+      "Primary endpoint for additional core entities to augment";
+    uses xos:provider;
+  }
+  container service {
+    description
+      "Primary endpoint for services to augment";
+  }
+  container tenant {
+    description
+      "Primary endpoint for tenants to augment";
+  }
+}
diff --git a/experiments/netconf/tests/yang2proto/xos-core-service.yang b/experiments/netconf/tests/yang2proto/xos-core-service.yang
new file mode 100644
index 0000000..3923221
--- /dev/null
+++ b/experiments/netconf/tests/yang2proto/xos-core-service.yang
@@ -0,0 +1,120 @@
+module xos-core-service {
+  namespace "urn:xos:core:service";
+  prefix xos-cs;
+
+  import complex-types { prefix ct; }
+
+  revision 2015-10-01 {
+    description "Initial revision.";
+  }
+
+  grouping service-attribute {
+    leaf name { type string { length 128; } }
+    leaf value { type string { length 1024; } }
+    leaf service {
+      type instance-identifier { ct:instance-type Service; require-instance true; }
+    }
+  }
+
+  grouping service-role {
+    leaf role {
+      type enumeration {
+        enum "admin";
+        //enum "Admin";
+      }
+    }
+  }
+
+  grouping common-model-attr {
+    leaf kind {
+      type string { length 30; }
+      default "generic";
+    }
+    leaf name { type string { length 255; } }
+  }
+
+  ct:complex-type ServiceElement {
+    ct:abstract true;
+    leaf enabled { type boolean; default true; }
+  }
+  
+  ct:complex-type Service {
+    ct:extends ServiceElement;
+    
+    leaf description {
+      type string { length 255; }
+      description "Description of Service";
+    }
+    leaf published { type boolean; default true; }
+
+    uses common-model-attr {
+      refine kind {
+        description "Kind of Service";
+      }
+      refine name {
+        description "Service Name";
+      }
+    }
+  }
+
+  ct:complex-type User {
+    ct:extends ServiceElement;
+    // TBD - should go to a separate xos-core-user module or such
+  }
+  
+  ct:complex-type ServicePrivilege {
+    key "user-service-role";
+    
+    leaf user {
+      type instance-identifier { ct:instance-type User; require-instance true; }
+    }
+    leaf service {
+      type instance-identifier { ct:instance-type Service; }
+    }
+    uses service-role;
+  }
+
+  ct:complex-type TenantRoot {
+    ct:extends ServiceElement;
+    
+    description
+      "A tenantRoot is one of the things that can sit at the root of a chain
+        of tenancy. This object represents a node.";
+    uses common-model-attr;
+  }
+
+  ct:complex-type ContainerImage {
+    // TBD
+  }
+  
+  ct:complex-type Tenancy {
+    ct:extends ServiceElement;
+    
+    description
+      "A Tenancy describes relationship between a subscriber and a provider";
+    
+    uses common-model-attr;
+
+    leaf provider { type instance-identifer { ct:instance-type Service; } }
+    leaf subscriber {
+      type instance-identifier {
+        ct:instance-type ServiceElement;
+      }
+    }
+
+    // adding stuff from TenantWithContainer here...
+    leaf creator { type instance-identifier { ct:instance-type User; } }
+    leaf image { type instance-identifier { ct:instance-type ContainerImage; } }
+  }
+
+  ct:complex-type Subscriber {
+    ct:extends TenantRoot;
+    refine kind { default "Subscriber"; }
+  }
+
+  ct:complex-type Provider {
+    ct:extends TenantRoot;
+    refine kind { default "Provider"; }
+  }
+  
+}
diff --git a/experiments/netconf/tests/yang2proto/xos-openstack.yang b/experiments/netconf/tests/yang2proto/xos-openstack.yang
new file mode 100644
index 0000000..3f79fe0
--- /dev/null
+++ b/experiments/netconf/tests/yang2proto/xos-openstack.yang
@@ -0,0 +1,190 @@
+module xos-controller {
+  namespace "urn:ietf:params:xml:ns:yang:xos-controller";
+  prefix xcontrol;
+  yang-version 1.1;
+
+  import ietf-yang-types { prefix yang; }
+  import ietf-inet-types { prefix inet; }
+  import xos-core        { prefix xos; }
+  import xos-types       { prefix xtype; }
+
+  organization
+    "Open Networking Lab (XOS) / Corenova Technologies";
+
+  contact
+    "Larry Peterson <llp@onlab.us>
+     Peter K. Lee <peter@corenova.com>";
+  
+  description
+    "This module contains a collection of Controller models for XOS.
+
+    Copyright (c) 2016 ON.LAB and the persons identified as authors of
+    the code.  All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, is permitted pursuant to, and subject to the license
+    terms of the Apache License, Version 2.0 which accompanies this
+    distribution, and is available at
+    (http://www.apache.org/licenses/LICENSE-2.0).";
+  
+  revision 2016-09-23 {
+    description "Initial revision.";
+  }
+
+  grouping controller {
+    uses xos:provider;
+    leaf backend-type { 
+      type string;
+    }
+    leaf version { 
+      type string;
+    }
+    leaf auth-url { 
+      type string;
+    }
+    leaf admin-user { 
+      type string;
+    }
+    leaf admin-password { 
+      type string;
+    }
+    leaf admin-tenant { 
+      type string;
+    }
+    leaf domain { 
+      type string;
+    }
+    container message-queue {
+      leaf rabbit-host { 
+        type string;
+      }
+      leaf rabbit-user { 
+        type string;
+      }
+      leaf rabbit-password { 
+        type string;
+      }
+    }
+    leaf deployment { 
+      type xtype:deployment; 
+    }
+  }
+  grouping controller-site-privilege {
+    uses xos:tenant;
+
+    leaf controller { 
+      type xtype:controller; 
+    }
+
+    leaf site-privilege { 
+      type xtype:site-privilege; 
+    }
+    
+    container synchronizer {
+      if-feature xos:synchronizer;
+      leaf role-id { 
+        type string;
+      }
+    }
+  }
+  grouping controller-network {
+    uses xos:tenant;
+    leaf network { 
+      type xtype:network; 
+    }
+    leaf controller { 
+      type xtype:controller; 
+    }
+    leaf subnet { 
+      type string;
+    }
+    container synchronizer {
+      if-feature synchronizer;
+      leaf net-id { 
+        type string;
+      }
+      leaf router-id { 
+        type string;
+      }
+      leaf subnet-id { 
+        type string;
+      }
+    }
+  }
+  grouping controller-site {
+    uses xos:tenant;
+    leaf site { 
+      type xtype:site; 
+    }
+
+    leaf controller { 
+      type xtype:controller; 
+    }
+
+    container synchronizer {
+      if-feature synchronizer;
+      leaf tenant-id { 
+        type string;
+      }
+    }
+  }
+  grouping controller-slice {
+    uses xos:tenant;
+    leaf controller { 
+      type xtype:controller; 
+    }
+    leaf slice { 
+      type xtype:slice; 
+    }
+    
+    container synchronizer {
+      if-feature synchronizer;
+      leaf tenant-id { 
+        type string;
+      }
+    }
+  }
+  grouping controller-role {
+    uses xos:tenant;
+    leaf role { 
+      type string;
+    }
+  }
+  grouping controller-user {
+    uses xos:tenant;
+    leaf user { 
+      type xtype:user; 
+    }
+
+    leaf controller { 
+      type xtype:controller; 
+    }
+    
+    container synchronizer {
+      if-feature synchronizer {
+        leaf kuser-id { 
+          type string;
+        }
+      }
+    }
+  }
+  grouping controller-slice-privilege {
+    uses xos:tenant;
+    leaf controller { 
+      type xtype:controller; 
+    }
+
+    leaf slice-privilege { 
+      type xtype:slice-privilege; 
+    }
+    
+    container synchronizer {
+      if-feature synchronizer {
+        leaf role-id { 
+          type string;
+        }
+      }
+    }
+  }
+  
+}
diff --git a/experiments/netconf/tests/yang2proto/xos-package.yang b/experiments/netconf/tests/yang2proto/xos-package.yang
new file mode 100644
index 0000000..e6a0537
--- /dev/null
+++ b/experiments/netconf/tests/yang2proto/xos-package.yang
@@ -0,0 +1,84 @@
+module xos-package {
+  namespace "urn:ietf:params:xml:ns:yang:xos-package";
+  prefix xpkg;
+  yang-version 1.1;
+
+  import ietf-yang-types { prefix yang; }
+  import ietf-inet-types { prefix inet; }
+  import yang-meta-types { prefix meta; }
+  import xos-controller  { prefix xos; }
+  import xos-types       { prefix xtype; }
+
+  organization
+    "Open Networking Lab (XOS) / Corenova Technologies";
+
+  contact
+    "Larry Peterson <llp@onlab.us>
+     Peter K. Lee <peter@corenova.com>";
+  
+  description
+    "This module contains a collection of Package models for XOS.
+
+    Copyright (c) 2016 ON.LAB and the persons identified as authors of
+    the code.  All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, is permitted pursuant to, and subject to the license
+    terms of the Apache License, Version 2.0 which accompanies this
+    distribution, and is available at
+    (http://www.apache.org/licenses/LICENSE-2.0).";
+  
+  revision 2016-09-29 {
+    description "Initial revision.";
+  }
+  typedef package-identifier {
+    type yang:yang-identifier;
+  }
+
+  identity xos-package {
+    base xos:xos-tenant;
+  }
+  /*
+   * Groupings
+   */
+  grouping package-info {
+    uses meta:yang-package-info {
+      refine version {
+        description "Version of Service Definition";
+      }
+      refine description {
+        description "Description of the Service Package";
+      }
+    }
+    container controller {
+      leaf model {
+        type leafref {
+          path "../../model/name";
+        }
+        mandatory true;
+      }
+      leaf initializer {
+        type meta:filename;
+      }
+      leaf synchronizer {
+        type meta:filename;
+      }
+      leaf public-key {
+        type meta:filename;
+      }
+      leaf private-key {
+        type meta:filename;
+      }
+    }
+  }
+  /*
+   * Augmentations to XOS
+   */
+  augment "/xos:core" {
+    list package {
+      key id;
+      uses package-info;
+    }
+  }
+}
+
diff --git a/experiments/netconf/tests/yang2proto/xos-slice.yang b/experiments/netconf/tests/yang2proto/xos-slice.yang
new file mode 100644
index 0000000..1878575
--- /dev/null
+++ b/experiments/netconf/tests/yang2proto/xos-slice.yang
@@ -0,0 +1,263 @@
+module xos-slice {
+  namespace "urn:ietf:params:xml:ns:yang:xos-slice";
+  prefix xslice;
+  yang-version 1.1;
+
+  import ietf-yang-types { prefix yang; }
+  import ietf-inet-types { prefix inet; }
+  import xos-controller  { prefix xos; }
+  import xos-types       { prefix xtype; }
+  import opnfv-infrastructure { prefix nfvi; }
+
+  organization
+    "Open Networking Lab (XOS) / Corenova Technologies, Inc.";
+
+  contact
+    "Larry Peterson <llp@onlab.us>
+     Peter K. Lee <peter@corenova.com>";
+  
+  description
+    "This module contains a collection of Slice and related models for XOS.
+
+    Copyright (c) 2016 ON.LAB and the persons identified as authors of
+    the code.  All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, is permitted pursuant to, and subject to the license
+    terms of the Apache License, Version 2.0 which accompanies this
+    distribution, and is available at
+    (http://www.apache.org/licenses/LICENSE-2.0).";
+  
+  revision 2016-09-13 {
+    description "Initial revision.";
+  }
+
+  grouping slice {
+    description 
+      "A slice is a logically centralized container for network and compute resources";
+
+    uses xos:provider {
+      refine "name" { description "Name of the slice"; }
+    }
+    leaf slice-url {
+      description "A URL describing the purpose of this slice";
+      type inet:uri;
+      // blank true;
+    }
+    leaf-list instances {
+      type leafref {
+        path "/nfvi:controller/compute/server/id";
+      }
+    }
+    leaf-list networks {
+      type leafref {
+        path "/nfvi:controller/fabric/network/id";
+      }
+    }
+    container policy {
+      leaf max-instances {
+        description "The maximum number of VMs that this slice is allowed to allocate";
+        type uint16;
+      }
+      leaf default-flavor {
+        type leafref {
+          path "/nfvi:controller/compute/flavor/id";
+        }
+      }
+      leaf default-image {
+        type leafref {
+          path "/nfvi:controller/compute/image/id";
+        }
+      }
+      leaf default-node {
+        type leafref {
+          path "/nfvi:controller/compute/host/id";
+        }
+      } 
+      leaf mount-data-sets {
+        type string {
+          length 0..256; 
+        }
+        default "GenBank";
+      } 
+      leaf default-isolation {
+        type xtype:isolation;
+      } 
+    }
+    leaf network {
+      description
+        "The network that the slice uses to connect to other slices and to the Internet.";
+      type enumeration {
+        enum none;
+        enum host;
+        enum bridged;
+        enum noauto;
+      }
+      default none;
+    }
+    leaf-list ports {
+      description
+        "The ports to be exposed for other slices to connect to";
+      type uint16;
+    }
+    leaf creator {
+      type leafref {
+        path "/xos:core/user/id";
+      }
+    }
+    leaf-list tags {
+      type string;
+    }
+    // below are deprecated and should not be used
+    leaf service {
+      description "The service that runs in this slice";
+      status deprecated;
+    }
+    // TOOD: need to better understand relationship between Service and Slice
+    action scale {
+      description "Adjust scale for slice(s)";
+    }
+  }
+
+  /*
+   * Augmentations to XOS core and NFVI modules
+   */
+  augment "/xos:core" {
+    list slice {
+      key id;
+      uses xslice:slice;
+    }
+  }
+  
+  augment "/nfvi:controller/nfvi:compute/nfvi:server" {
+    leaf slice {
+      // do we need this circular reference?
+    }
+    leaf deployment {
+      type leafref {
+        path "/xos:core/deployment/id";
+      }
+    }
+    leaf isolation {
+      type xtype:isolation;
+    }
+    leaf-list volumes {
+      type string; // should be leafref
+    }
+    // leaf parent {
+    //   type xtype:parent;
+    // }
+    container synchronizer {
+      if-feature synchronizer;
+      leaf instance-id { 
+        type string;
+      }
+      leaf instance-uuid { 
+        type string;
+      }
+      leaf instance-name { 
+        type string;
+      }
+      leaf ip { 
+        type inet:ip-address;
+      }
+    }
+  }
+  augment "/nfvi:controller/nfvi:compute/nfvi:image" {
+    leaf template {
+      type leafref {
+        path "/xos:core/template/id";
+      }
+    }
+    leaf-list deployments {
+      type leafref {
+        path "/xos:core/deployment/id";
+      }
+    }
+    container synchronizer {
+      if-feature xos:synchronizer;
+      leaf glance-image-id { 
+        type string;
+      }
+    }
+  }
+  augment "/nfvi:controller/nfvi:fabric/nfvi:network" {
+    leaf autoconnect {
+      type boolean;
+      default true;
+    }
+    leaf-list labels {
+      type string;
+    }
+    container policy {
+      leaf guaranteed-bandwidth { 
+        type uint32;
+      }
+      leaf permit-all-slices { 
+        type boolean;
+        default true;
+      }
+      container parameters {
+        list topology {
+          key name;
+          uses xos:parameter;
+        }
+        list controller {
+          key name;
+          uses xos:parameter;
+        }
+        list network {
+          key name;
+          uses xos:parameter;
+        }
+      }
+    }
+    container synchronizer {
+      if-feature synchronizer;
+      leaf network-id { 
+        type string;
+      }
+      leaf router-id { 
+        type string;
+      }
+      leaf subnet-id { 
+        type string;
+      }
+    }
+  }
+  augment "/nfvi:controller/nfvi:fabric/nfvi:network/nfvi:subnet/nfvi:port" {
+    leaf instance {
+      type instance-identifier;
+    }
+    leaf xos-created {
+      type boolean;
+      status deprecated;
+    }
+    container synchronizer {
+      if-feature xos:synchronizer;
+      leaf ip { 
+        type inet:ip-address;
+      }
+      leaf port-id { 
+        type yang:uuid;
+      }
+      leaf mac { 
+        type yang:mac-address;
+      }
+    }
+  }
+  augment "/nfvi:controller/nfvi:fabric/nfvi:network/nfvi:subnet/nfvi:dhcp/nfvi:pool" {
+    leaf gateway-ip {
+      type inet:ip-address;
+    }
+    leaf gateway-mac {
+      type yang:mac-address;
+    }
+    leaf cidr {
+      type string;
+    }
+    leaf inuse {
+      type boolean;
+    }
+  }
+}
diff --git a/experiments/netconf/tests/yang2proto/xos-topology.yang b/experiments/netconf/tests/yang2proto/xos-topology.yang
new file mode 100644
index 0000000..469cb84
--- /dev/null
+++ b/experiments/netconf/tests/yang2proto/xos-topology.yang
@@ -0,0 +1,224 @@
+module xos-topology {
+  namespace "urn:ietf:params:xml:ns:yang:xos-topology";
+  prefix xtop;
+  yang-version 1.1;
+
+  import ietf-yang-types { prefix yang; }
+  import ietf-inet-types { prefix inet; }
+  import yang-meta-types { prefix meta; }
+  import xos-controller  { prefix xos; }
+  import xos-types       { prefix xtype; }
+
+  organization
+    "Open Networking Lab (XOS) / Corenova Technologies";
+
+  contact
+    "Larry Peterson <llp@onlab.us>
+     Peter K. Lee <peter@corenova.com>";
+  
+  description
+    "This module contains a collection of Topology models for XOS.
+
+    Copyright (c) 2016 ON.LAB and the persons identified as authors of
+    the code.  All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, is permitted pursuant to, and subject to the license
+    terms of the Apache License, Version 2.0 which accompanies this
+    distribution, and is available at
+    (http://www.apache.org/licenses/LICENSE-2.0).";
+  
+  revision 2016-09-13 {
+    description "Initial revision.";
+  }
+
+  grouping deployment {
+    uses xos:provider {
+      refine "name" { description "Name of the deployment"; }
+    }
+    leaf-list sites {
+      config false;
+      type leafref {
+        path "/xos:core/site/id";
+      }
+    }
+    leaf-list templates {
+      type leafref {
+        path "/xos:core/template/id";
+      }
+    }
+  }
+  grouping site {
+    uses xos:provider {
+      refine "name" { description "Name of the site"; }
+    }
+    leaf site-url { 
+      description "A URL describing the purpose of this site";
+      type inet:uri;
+    }
+    container location {
+      leaf geoposition {
+        type string; // what is this data format?
+      }
+      leaf longitude { 
+        type decimal64;
+      }
+      leaf latitude { 
+        type decimal64;
+      }
+    }
+    leaf is-public { 
+      type boolean;
+    }
+    leaf login-base { 
+      type string;
+    }
+    leaf deployment {
+      type leafref {
+        path "/xos:core/deployment/id";
+      }
+    }
+    leaf-list nodes {
+      config false;
+      type leafref {
+        path "/xos:core/node/id";
+      }
+    }
+    leaf-list users {
+      config false;
+      type leafref {
+        path "/xos:core/user/id";
+      }
+    }
+    leaf hosts-nodes {
+      config false;
+      type boolean;
+    }
+    leaf hosts-users {
+      config false;
+      type boolean;
+    }
+    container synchronizer {
+      if-feature xos:synchronizer;
+      leaf availability-zone { 
+        type string;
+      }
+    }
+  }
+  grouping node {
+    uses xos:tenant;
+    leaf site { 
+      type leafref {
+        path "/xos:core/site/id";
+      }
+    }
+    leaf deployment {
+      config false;
+      type leafref {
+        path "/xos:core/deployment/id";
+      }
+    }
+  }
+  grouping user {
+    uses xos:tenant {
+      refine 'name' {
+        config false;
+        description "Name of the user";
+      }
+    }
+    leaf firstname {
+      type meta:person-name;
+    }
+    leaf lastname {
+      type meta:person-name;
+    }
+    leaf email {
+      type meta:email-address;
+    }
+    leaf phone {
+      type meta:phone-number;
+    }
+    leaf timezone {
+      type meta:timezone;
+    }
+    leaf site {
+      type leafref {
+        path "/xos:core/site/id";
+      }
+    }
+    container credential {
+      leaf username {
+        type yang:yang-identifier;
+      }
+      leaf password {
+        type meta:password;
+      }
+      leaf role {
+        type xtype:access-role;
+      }
+      leaf public-key {
+        type string;
+      }
+      leaf login-page {
+        type inet:uri;
+      }
+      leaf last-login {
+        config false;
+        type yang:date-and-time;
+      }
+    }
+  }
+  grouping network-template {
+    uses xos:tenant;
+    leaf guaranteed-bandwidth { 
+      type uint32;
+    }
+    leaf visibility { 
+      type string;
+    }
+    leaf translation { 
+      type string;
+    }
+    leaf access { 
+      type string;
+    }
+    leaf shared-network-name { 
+      type string;
+    }
+    leaf shared-network-id { 
+      type string;
+    }
+    leaf topology-kind { 
+      type string;
+    }
+    leaf controller-kind { 
+      type string;
+    }
+  }
+
+  /*
+   * Augmentations to XOS core
+   */
+  augment "/xos:core" {
+    list template {
+      key id;
+      uses xtop:network-template;
+    }
+    list deployment {
+      key id;
+      uses xtop:deployment;
+    }
+    list site {
+      key id;
+      uses xtop:site;
+    }
+    list node {
+      key id;
+      uses xtop:node;
+    }
+    list user {
+      key id;
+      uses xtop:user;
+    }
+  }
+}
diff --git a/experiments/netconf/tests/yang2proto/xos-types.yang b/experiments/netconf/tests/yang2proto/xos-types.yang
new file mode 100644
index 0000000..289587a
--- /dev/null
+++ b/experiments/netconf/tests/yang2proto/xos-types.yang
@@ -0,0 +1,87 @@
+module xos-types {
+  namespace "urn:ietf:params:xml:ns:yang:xos-types";
+  prefix xtype;
+  yang-version 1.1;
+
+  import ietf-yang-types { prefix yang; }
+  import ietf-inet-types { prefix inet; }
+  
+  organization
+   "Open Networking Lab (CORD) / Corenova Technologies";
+
+  contact
+    "Larry Peterson <llp@onlab.us>
+     Peter K. Lee <peter@corenova.com>";
+  
+  description
+   "This module contains a collection of common models and types for XOS.
+
+    Copyright (c) 2016 ON.LAB and the persons identified as authors of
+    the code.  All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, is permitted pursuant to, and subject to the license
+    terms of the Apache License, Version 2.0 which accompanies this
+    distribution, and is available at
+    (http://www.apache.org/licenses/LICENSE-2.0).";
+  
+  revision 2016-09-12 {
+    description "Initial revision.";
+  }
+
+  /*
+   * Typedefs
+   */
+  typedef unique-identifier {
+    description "defines valid formats for external reference id";
+    type union {
+      type uint32 { range 1..max; }
+      type yang:uuid;
+      type inet:uri;
+    }
+  }
+  typedef flow-identifier {
+    description "defines valid format for identifying a flow";
+    type yang:uuid;
+  }
+  typedef network-identifier {
+    description "defines valid format for identifying a network";
+    type yang:uuid;
+  }
+  typedef mac-address {
+    description "defines valid format for mac-address";
+    type yang:mac-address;
+  }
+  typedef bandwidth {
+    type uint32 {
+      range 1000000..max; // should be at least 1 Mbps?  
+    }
+    units 'bps';
+  }
+  typedef vlan {
+    type uint16 { range 0..4095; }
+  }
+  typedef isolation {
+    type enumeration {
+      enum vm     { description 'Virtual Machine'; }
+      enum lxc    { description 'Container'; }
+      enum hybrid { description 'Container in Virtual Machine'; }
+    }
+    default 'vm';
+  }
+  typedef access-role {
+    type enumeration {
+      enum admin;
+      enum staff;
+      enum user;
+    }
+    default 'user';
+  }
+  typedef certificate {
+    description
+      "Captures a security certificate, should be improved for better validation";
+    type string {
+      length 0..1024;
+    }
+  }
+}
diff --git a/experiments/netconf/tests/yang2proto/xos-unreconciled-models.yang b/experiments/netconf/tests/yang2proto/xos-unreconciled-models.yang
new file mode 100644
index 0000000..491a153
--- /dev/null
+++ b/experiments/netconf/tests/yang2proto/xos-unreconciled-models.yang
@@ -0,0 +1,462 @@
+module xos-accessibility {
+  namespace "urn:ietf:params:xml:ns:yang:xos-accessibility";
+  prefix xacc;
+  yang-version 1.1;
+
+  import ietf-yang-types { prefix yang; }
+  import ietf-inet-types { prefix inet; }
+  import xos-types       { prefix xtype; }
+
+  organization
+    "Open Networking Lab (XOS) / Corenova Technologies";
+
+  contact
+    "Larry Peterson <llp@onlab.us>
+     Peter K. Lee <peter@corenova.com>";
+  
+  description
+    "This module contains a collection of core models for XOS.
+
+    Copyright (c) 2016 ON.LAB and the persons identified as authors of
+    the code.  All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, is permitted pursuant to, and subject to the license
+    terms of the Apache License, Version 2.0 which accompanies this
+    distribution, and is available at
+    (http://www.apache.org/licenses/LICENSE-2.0).";
+  
+  revision 2016-09-13 {
+    description "Initial revision.";
+  }
+
+  /* Future Consideration
+  typedef image {
+    type unique-identifier;
+  }
+  typedef controller-network {
+    type unique-identifier;
+  }
+  typedef site {
+    type unique-identifier;
+  }
+  typedef tenant-root-role {
+    type unique-identifier;
+  }
+  typedef slice-role {
+    type unique-identifier;
+  }
+  typedef site-deployment {
+    type unique-identifier;
+  }
+  typedef tenant-privilege {
+    type unique-identifier;
+  }
+  typedef tag {
+    type unique-identifier;
+  }
+  typedef user-credential {
+    type unique-identifier;
+  }
+  typedef invoice {
+    type unique-identifier;
+  }
+  typedef slice-privilege {
+    type unique-identifier;
+  }
+  typedef flavor {
+    type unique-identifier;
+  }
+  typedef port {
+    type unique-identifier;
+  }
+  typedef service-role {
+    type unique-identifier;
+  }
+  typedef controller-site {
+    type unique-identifier;
+  }
+  typedef controller-slice {
+    type unique-identifier;
+  }
+  typedef tenant-role {
+    type unique-identifier;
+  }
+  typedef slice {
+    type unique-identifier;
+  }
+  typedef network {
+    type unique-identifier;
+  }
+  typedef controller-role {
+    type unique-identifier;
+  }
+  typedef diag {
+    type unique-identifier;
+  }
+  typedef service-class {
+    type unique-identifier;
+  }
+  typedef tenant-attribute {
+    type unique-identifier;
+  }
+  typedef site-role {
+    type unique-identifier;
+  }
+  typedef subscriber {
+    type unique-identifier;
+  }
+  typedef instance {
+    type unique-identifier;
+  }
+  typedef charge {
+    type unique-identifier;
+  }
+  typedef program {
+    type unique-identifier;
+  }
+  typedef role {
+    type unique-identifier;
+  }
+  typedef usable-object {
+    type unique-identifier;
+  }
+  typedef node-label {
+    type unique-identifier;
+  }
+  typedef slice-credential {
+    type unique-identifier;
+  }
+  typedef node {
+    type unique-identifier;
+  }
+  typedef address-pool {
+    type unique-identifier;
+  }
+  typedef dashboard-view {
+    type unique-identifier;
+  }
+  typedef network-parameter {
+    type unique-identifier;
+  }
+  typedef image-deployments {
+    type unique-identifier;
+  }
+  typedef controller-user {
+    type unique-identifier;
+  }
+  typedef reserved-resource {
+    type unique-identifier;
+  }
+  typedef network-template {
+    type unique-identifier;
+  }
+  typedef controller-dashboard-view {
+    type unique-identifier;
+  }
+  typedef user-dashboard-view {
+    type unique-identifier;
+  }
+  typedef controller {
+    type unique-identifier;
+  }
+  typedef user {
+    type unique-identifier;
+  }
+  typedef deployment {
+    type unique-identifier;
+  }
+  typedef reservation {
+    type unique-identifier;
+  }
+  typedef site-privilege {
+    type unique-identifier;
+  }
+  typedef payment {
+    type unique-identifier;
+  }
+  typedef tenant {
+    type unique-identifier;
+  }
+  typedef network-slice {
+    type unique-identifier;
+  }
+  typedef account {
+    type unique-identifier;
+  }
+  typedef tenant-root {
+    type unique-identifier;
+  }
+  typedef service {
+    type unique-identifier;
+  }
+  typedef controller-slice-privilege {
+    type unique-identifier;
+  }
+  typedef site-credential {
+    type unique-identifier;
+  }
+  typedef deployment-privilege {
+    type unique-identifier;
+  }
+  typedef network-parameter-type {
+    type unique-identifier;
+  }
+  typedef provider {
+    type unique-identifier;
+  }
+  typedef tenant-with-container {
+    type unique-identifier;
+  }
+  typedef deployment-role {
+    type unique-identifier;
+  }
+  typedef project {
+    type unique-identifier;
+  }
+  typedef tenant-root-privilege {
+    type unique-identifier;
+  }
+  typedef slice-tag {
+    type unique-identifier;
+  }
+  typedef coarse-tenant {
+    type unique-identifier;
+  }
+  typedef router {
+    type unique-identifier;
+  }
+  typedef service-resource {
+    type unique-identifier;
+  }
+  typedef service-privilege {
+    type unique-identifier;
+  }
+  */
+  grouping service-class {
+    uses xos-base;
+    
+    leaf description { 
+      type string;
+    }
+    leaf commitment { 
+      type uint32;
+    }
+    leaf membership-fee { 
+      type uint32;
+    }
+    leaf membership-fee-months { 
+      type uint32;
+    }
+    leaf upgrade-requires-approval { 
+      type boolean;
+    }
+  }
+  grouping invoice {
+    uses xos-base;
+    leaf date { 
+      type xtype:datetime;
+    }
+    leaf account { 
+      type xtype:account; 
+    }
+  }
+
+  grouping charge {
+    uses xos-base;
+    leaf account { 
+      type xtype:account; 
+    }
+    leaf slice { 
+      type xtype:slice; 
+    }
+    leaf kind { 
+      type string;
+    }
+    leaf state { 
+      type string;
+    }
+    leaf date { 
+      type xtype:datetime;
+    }
+    leaf object { 
+      type xtype:object; 
+    }
+    leaf amount { 
+      type decimal64;
+    }
+    leaf core-hours { 
+      type decimal64;
+    }
+    leaf invoice { 
+      type xtype:invoice; 
+    }
+  }
+
+  grouping role {
+    uses xos-base;
+    leaf role-type { 
+      type string;
+    }
+    leaf role { 
+      type string;
+    }
+    leaf description { 
+      type string;
+    }
+    leaf content-type { 
+      type xtype:content-type; 
+    }
+  }
+
+  grouping dashboard-view {
+    uses xos-base;
+    leaf url { 
+      type string;
+    }
+    leaf enabled { 
+      type boolean;
+    }
+  }
+
+  grouping controller-dashboard-view {
+    uses xos-base;
+    leaf controller { 
+      type xtype:controller; 
+    }
+    leaf dashboardView { 
+      type xtype:dashboardView; 
+    }
+    leaf enabled { 
+      type boolean;
+    }
+    leaf url { 
+      type string;
+    }
+  }
+
+  grouping user-dashboard-view {
+    uses xos-base;
+    leaf user { 
+      type xtype:user; 
+    }
+    leaf dashboardView { 
+      type xtype:dashboardView; 
+    }
+    leaf order { 
+      type uint32;
+    }
+  }
+  grouping service-privilege {
+    uses xos-base;
+    leaf user { 
+      type xtype:user; 
+    }
+    leaf service { 
+      type xtype:service; 
+    }
+    leaf role { 
+      type xtype:role; 
+    }
+  }
+
+  grouping payment {
+    uses xos-base;
+    leaf account { 
+      type xtype:account; 
+    }
+    leaf amount { 
+      type decimal64;
+    }
+    leaf date { 
+      type xtype:datetime;
+    }
+  }
+  
+  grouping account {
+    uses xos-base;
+    leaf site { 
+      type xtype:site; 
+    }
+  }
+
+  grouping service-resource {
+    uses xos-base;
+    leaf service-class { 
+      type xtype:service-class; 
+    }
+    
+    leaf max-units-deployment { 
+      type uint32;
+    }
+
+    leaf max-units-node { 
+      type uint32;
+    }
+
+    leaf max-duration { 
+      type uint32;
+    }
+
+    leaf bucket-in-rate { 
+      type uint32;
+    }
+
+    leaf bucket-max-size { 
+      type uint32;
+    }
+
+    leaf cost { 
+      type uint32;
+    }
+
+    leaf calendar-reservable { 
+      type boolean;
+    }
+  }
+
+  grouping diag {
+    uses xos-base;
+    
+  }
+
+  grouping program {
+    uses xos-base;
+    leaf description { 
+      type string;
+    }
+
+    leaf kind { 
+      type string;
+    }
+
+    leaf command { 
+      type string;
+    }
+
+    leaf owner { 
+      type xtype:owner; 
+    }
+
+    leaf contents { 
+      type string;
+    }
+
+    leaf output { 
+      type string;
+    }
+
+    leaf messages { 
+      type string;
+    }
+
+    leaf status { 
+      type string;
+    }
+  }
+
+  grouping usable-object {
+    uses xos-base;
+  }
+
+}
diff --git a/experiments/netconf/tests/yang2proto/yang_to_protobuf_test.py b/experiments/netconf/tests/yang2proto/yang_to_protobuf_test.py
new file mode 100644
index 0000000..16b12b7
--- /dev/null
+++ b/experiments/netconf/tests/yang2proto/yang_to_protobuf_test.py
@@ -0,0 +1,220 @@
+#
+# Copyright 2016 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import json
+import os
+from unittest import TestCase
+import time
+from tests.itests.docutests.test_utils import run_command_to_completion_with_raw_stdout
+from tests.utests.chameleon.protoc_plugins.test_utils import load_file
+
+
+pyang_cmd = "pyang --plugindir /voltha/experiments/netconf/yang2proto -f " \
+            "proto " \
+            "-p /voltha/experiments/netconf/tests/yang2proto " \
+            "/voltha/experiments/netconf/tests/yang2proto/{}"
+
+class YangToProtoBufTests(TestCase):
+
+    def _compare_file(self, response, expected_response):
+        # compare two files and strip empty lines, blanks, etc
+        def _filter(x):
+            x.strip()
+            return x is not None
+
+        response = filter(_filter,response.split())
+        expected_response = filter(_filter,expected_response.split())
+        print response
+        print expected_response
+
+        self.assertEqual(set(response), set(expected_response))
+
+
+    def test_01_basic_def(self):
+        print "Test_01_basic_def_Start:------------------"
+        t0 = time.time()
+
+        # input file: /voltha/tests/utests/netconf/yang/basic.yang
+
+        expected_response = """
+            syntax = "proto3";
+            package basic;
+
+            message commonAttributes {
+                uint32 my_id = 1 ;
+                string my_name = 2 ;
+                bool my_status = 3 ;
+            }
+            """
+
+        try:
+            cmd = pyang_cmd.format('basic.yang')
+            print 'running command: {}'.format(cmd)
+            response, err, rc = run_command_to_completion_with_raw_stdout(cmd)
+            self.assertEqual(rc, 0)
+
+            self._compare_file(response, expected_response)
+        finally:
+            print "Test_01_basic_def_End:------------------ took {} " \
+                  "secs\n\n".format(time.time() - t0)
+
+
+    def test_02_container_def(self):
+        print "Test_02_container_def_Start:------------------"
+        t0 = time.time()
+
+        # input file: /voltha/tests/utests/netconf/yang/container.yang
+
+        expected_response = """
+            syntax = "proto3";
+            package container;
+
+            message int_container {
+                int32 eight = 1 ;
+                int32 nine = 2 ;
+            }
+            """
+
+        try:
+            cmd = pyang_cmd.format('container.yang')
+            print 'running command: {}'.format(cmd)
+            response, err, rc = run_command_to_completion_with_raw_stdout(cmd)
+            self.assertEqual(rc, 0)
+
+            self._compare_file(response, expected_response)
+        finally:
+            print "Test_02_container_def_End:------------------ took {} " \
+                  "secs\n\n".format(time.time() - t0)
+
+
+    def test_03_mix_simple_types(self):
+        print "Test_03_mix_simple_types_Start:------------------"
+        t0 = time.time()
+
+        # input file: /voltha/tests/utests/netconf/yang/mix_simple_types.yang
+
+        expected_response = """
+            syntax = "proto3";
+            package mix_simple_types;
+
+            message user {
+                string name = 1 ;
+                string full_name = 2 ;
+                string class = 3 ;
+            }
+
+            message int_container {
+                int32 eight = 1 ;
+                int32 nine = 2 ;
+                int32 ten = 3 ;
+            }
+            message container1 {
+                bool a = 1 ;
+                Any b = 2 ;
+                string mleaf = 3 ;
+                repeated string mleaf_list = 4 ;
+                message inner_container {
+                    string mleaf1 = 1 ;
+                    string mleaf2 = 2 ;
+                }
+            }
+            """
+
+        try:
+            cmd = pyang_cmd.format('mix_simple_types.yang')
+            print 'running command: {}'.format(cmd)
+            response, err, rc = run_command_to_completion_with_raw_stdout(cmd)
+            self.assertEqual(rc, 0)
+
+            self._compare_file(response, expected_response)
+        finally:
+            print "Test_03_mix_simple_types_End:------------------ took {} " \
+                  "secs\n\n".format(time.time() - t0)
+
+
+    def test_04_cord_tenant(self):
+        print "Test_04_cord_tenant_Start:------------------"
+        t0 = time.time()
+
+        # input file: /voltha/tests/utests/netconf/yang/cord-tenant.yang
+
+        expected_response = """
+            syntax = "proto3";
+            package cord_tenant;
+
+            message subscriber {
+                string label = 1 ;
+                enum status
+                {
+                    violation = 0 ;
+                    enabled = 1 ;
+                    delinquent = 2 ;
+                    suspended = 3 ;
+                }
+                bool demo = 3 ;
+            }
+            """
+        try:
+            cmd = pyang_cmd.format('cord-tenant.yang')
+            print 'running command: {}'.format(cmd)
+            response, err, rc = run_command_to_completion_with_raw_stdout(cmd)
+            self.assertEqual(rc, 0)
+
+            self._compare_file(response, expected_response)
+        finally:
+            print "Test_04_cord_tenant_End:------------------ took {} " \
+                  "secs\n\n".format(time.time() - t0)
+
+
+    def test_05_basic_rpc(self):
+        print "Test_05_basic_rpc_Start:------------------"
+        t0 = time.time()
+
+        # input file: /voltha/tests/utests/netconf/yang/basic-rpc.yang
+
+        expected_response = """
+            syntax = "proto3";
+            package basic_rpc;
+
+            message my_id {
+                uint32 my_id = 1 ;
+            }
+            message my_name {
+                string my_name = 2 ;
+            }
+            message my_status {
+                bool my_status = 3 ;
+            }
+            message my_input {
+                string my_input = 4 ;
+            }
+            message my_output {
+                string my_output = 5 ;
+            }
+
+            service basic_rpc {
+                rpc do_something(my_input) returns(my_output) {}
+            }
+            """
+        try:
+            cmd = pyang_cmd.format('basic-rpc.yang')
+            print 'running command: {}'.format(cmd)
+            response, err, rc = run_command_to_completion_with_raw_stdout(cmd)
+            self.assertEqual(rc, 0)
+
+            self._compare_file(response, expected_response)
+        finally:
+            print "Test_05_basic_rpc_End:------------------ took {} " \
+                  "secs\n\n".format(time.time() - t0)
diff --git a/experiments/netconf/yang2proto/__init__.py b/experiments/netconf/yang2proto/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/experiments/netconf/yang2proto/__init__.py
diff --git a/experiments/netconf/yang2proto/yang2proto.py b/experiments/netconf/yang2proto/yang2proto.py
new file mode 100644
index 0000000..3808804
--- /dev/null
+++ b/experiments/netconf/yang2proto/yang2proto.py
@@ -0,0 +1,389 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""pyang plugin to convert a yang schema to a protobuf schema
+
+   - basic support for leaf, leaf-list, containers, list
+
+   - this plugin requires pyang to be present and is run using pyang as
+   follows:
+
+   $ pyang --plugindir /voltha/experiments/netconf/yang2proto -f yang2proto  -o
+   <protofile> -p /voltha/experiments/netconf/tests/yang2proto
+   /voltha/experiments/netconf/tests/yang2proto/<yang file>
+
+   - pyang validates the yang definition first and then invoke this plugin
+   to convert the yang model into protobuf.
+   
+"""
+
+from pyang import plugin, statements, error
+from pyang.util import unique_prefixes
+
+
+# Register the Protobuf plugin
+def pyang_plugin_init():
+    plugin.register_plugin(ProtobufPlugin())
+
+
+class Protobuf(object):
+    def __init__(self, module_name):
+        self.module_name = module_name.replace('-', '_')
+        self.tree = {}
+        self.containers = []
+        self.ylist = []
+        self.leafs = []
+        self.enums = []
+        self.headers = []
+        self.services = []
+        self.rpcs = []
+
+    def set_headers(self, module_name):
+        self.headers.append('syntax = "proto3";')
+        self.headers.append('package {};'.format(module_name.replace('-',
+                                                                     '_')))
+    def _filter_duplicate_names(self, list_obj):
+        current_names = []
+        def _filter_dup(obj):
+            if obj.name not in current_names:
+                current_names.append(obj.name)
+                return True
+
+        return filter(_filter_dup, list_obj)
+
+    def _print_rpc(self, out, level=0):
+        spaces = '    ' * level
+        out.append(''.join([spaces, 'service {} '.format(self.module_name)]))
+        out.append(''.join('{\n'))
+
+        rpc_space = spaces + '    '
+        for rpc in self.rpcs:
+            out.append(''.join([rpc_space, 'rpc {}({}) returns({})'.format(
+                rpc.name.replace('-','_'),
+                rpc.input.replace('-','_'),
+                rpc.output.replace('-','_'))]))
+            out.append(''.join(' {}\n'))
+
+        out.append(''.join([spaces, '}\n']))
+
+    def _print_container(self, container, out, level=0):
+        spaces = '    ' * level
+        out.append(''.join([spaces, 'message {} '.format(
+            container.name.replace('-','_'))]))
+        out.append(''.join('{\n'))
+
+        self._print_leaf(container.leafs, out, spaces=spaces)
+
+        for l in container.ylist:
+            self._print_list(l, out, level + 1)
+
+        for inner in container.containers:
+            self._print_container(inner, out, level + 1)
+
+        out.append(''.join([spaces, '}\n']))
+
+    def _print_list(self, ylist, out, level=0):
+        spaces = '    ' * level
+        out.append(''.join([spaces, 'message {} '.format(ylist.name.replace(
+            '-', '_'))]))
+        out.append(''.join('{\n'))
+
+        self._print_leaf(ylist.leafs, out, spaces=spaces)
+
+        for l in ylist.ylist:
+            self._print_list(l, out, level + 1)
+
+        out.append(''.join([spaces, '}\n']))
+
+
+    def _print_leaf(self, leafs, out, spaces='', include_message=False):
+        leafspaces = ''.join([spaces, '    '])
+        for idx, l in enumerate(leafs):
+            if l.type == "enum":
+                out.append(''.join([leafspaces, 'enum {}\n'.format(
+                    l.name.replace('-','_'))]))
+                out.append(''.join([leafspaces, '{\n']))
+                self._print_enumeration(l.enumeration, out, leafspaces)
+                out.append(''.join([leafspaces, '}\n']))
+            else:
+                if include_message:
+                    out.append(''.join([spaces, 'message {} '.format(
+                        l.name.replace('-','_'))]))
+                    out.append(''.join([spaces, '{\n']))
+                out.append(''.join([leafspaces, '{}{} {} = {} ;\n'.format(
+                    'repeated ' if l.leaf_list else '',
+                    l.type,
+                    l.name.replace('-', '_'),
+                    idx + 1)]))
+                if include_message:
+                    out.append(''.join([spaces, '}\n']))
+
+    def _print_enumeration(self, yang_enum, out, spaces):
+        enumspaces = ''.join([spaces, '    '])
+        for idx, e in enumerate(yang_enum):
+            out.append(''.join([enumspaces, '{}\n'.format(e)]))
+
+    def print_proto(self):
+        out = []
+        for h in self.headers:
+            out.append('{}\n'.format(h))
+        out.append('\n')
+
+        if self.leafs:
+            # Risk of duplicates if leaf was processed as both a
+            # children and a substatement.  Filter duplicates.
+            self.leafs = self._filter_duplicate_names(self.leafs)
+            self._print_leaf(self.leafs, out, spaces='', include_message=True)
+            out.append('\n')
+
+        if self.ylist:
+            # remove duplicates
+            self.ylist = self._filter_duplicate_names(self.ylist)
+            for l in self.ylist:
+                self._print_list(l, out)
+            out.append('\n')
+
+        if self.containers:
+            # remove duplicates
+            self.containers = self._filter_duplicate_names(self.containers)
+            for c in self.containers:
+                self._print_container(c, out)
+
+            out.append('\n')
+
+        if self.rpcs:
+            self.rpcs = self._filter_duplicate_names(self.rpcs)
+            self._print_rpc(out)
+
+        return out
+
+
+class YangContainer(object):
+    def __init__(self):
+        self.name = None
+        self.containers = []
+        self.enums = []
+        self.leafs = []
+        self.ylist = []
+
+
+class YangList(object):
+    def __init__(self):
+        self.name = None
+        self.leafs = []
+        self.containers = []
+        self.ylist = []
+
+
+class YangLeaf(object):
+    def __init__(self):
+        self.name = None
+        self.type = None
+        self.leaf_list = False
+        self.enumeration = []
+        self.description = None
+
+
+class YangEnumeration(object):
+    def __init__(self):
+        self.value = []
+
+
+class YangRpc(object):
+    def __init__(self):
+        self.name = None
+        self.input = ''
+        self.output = ''
+
+
+class ProtobufPlugin(plugin.PyangPlugin):
+    def add_output_format(self, fmts):
+        self.multiple_modules = True
+        fmts['proto'] = self
+
+    def setup_fmt(self, ctx):
+        ctx.implicit_errors = False
+
+    def emit(self, ctx, modules, fd):
+        """Main control function.
+        """
+        self.real_prefix = unique_prefixes(ctx)
+
+        for m in modules:
+            # m.pprint()
+            # statements.print_tree(m)
+            proto = Protobuf(m.i_modulename)
+            proto.set_headers(m.i_modulename)
+            self.process_substatements(m, proto, None)
+            self.process_children(m, proto, None)
+            out = proto.print_proto()
+            for i in out:
+                fd.write(i)
+
+    def process_substatements(self, node, parent, pmod):
+        """Process all substmts.
+        """
+        for st in node.substmts:
+            if st.keyword in ["rpc"]:
+                self.process_rpc(st, parent)
+            if st.keyword in ["notification"]:
+                continue
+            if st.keyword in ["choice", "case"]:
+                self.process_substatements(st, parent, pmod)
+                continue
+
+            if st.i_module.i_modulename == pmod:
+                nmod = pmod
+            else:
+                nmod = st.i_module.i_modulename
+
+            if st.keyword in ["container", "grouping"]:
+                c = YangContainer()
+                c.name = st.arg
+                self.process_substatements(st, c, nmod)
+                parent.containers.append(c)
+            elif st.keyword == "list":
+                l = YangList()
+                l.name = st.arg
+                self.process_substatements(st, l, nmod)
+                parent.ylist.append(l)
+            elif st.keyword in ["leaf", "leaf-list"]:
+                self.process_leaf(st, parent, st.keyword == "leaf-list")
+
+    def process_children(self, node, parent, pmod):
+        """Process all children of `node`, except "rpc" and "notification".
+        """
+        for ch in node.i_children:
+            if ch.keyword in ["rpc"]:
+                self.process_rpc(ch, parent)
+            if ch.keyword in ["notification"]:
+                continue
+            if ch.keyword in ["choice", "case"]:
+                self.process_children(ch, parent, pmod)
+                continue
+            if ch.i_module.i_modulename == pmod:
+                nmod = pmod
+            else:
+                nmod = ch.i_module.i_modulename
+            if ch.keyword in ["container", "grouping"]:
+                c = YangContainer()
+                c.name = ch.arg
+                self.process_children(ch, c, nmod)
+                parent.containers.append(c)
+                # self.process_container(ch, p, nmod)
+            elif ch.keyword == "list":
+                l = YangList()
+                l.name = ch.arg
+                self.process_children(ch, l, nmod)
+                parent.ylist.append(l)
+            elif ch.keyword in ["leaf", "leaf-list"]:
+                self.process_leaf(ch, parent, ch.keyword == "leaf-list")
+
+    def process_leaf(self, node, parent, leaf_list=False):
+        # Leaf have specific sub statements
+        leaf = YangLeaf()
+        leaf.name = node.arg
+        leaf.type = self.get_protobuf_type(node.search_one("type"))
+        if leaf.type == "enum":
+            self.process_enumeration(node, leaf)
+        # leaf.type = self.base_type(node.search_one("type"))
+        leaf.description = node.search_one("description")
+        leaf.leaf_list = leaf_list
+        parent.leafs.append(leaf)
+
+    def process_enumeration(self, node, leaf):
+        enumeration_dict = {}
+        start_node = None
+        for child in node.substmts:
+            if child.keyword == "type":
+                start_node = child;
+                break
+
+        for enum in start_node.search('enum'):
+            val = enum.search_one('value')
+            if val is not None:
+                enumeration_dict[enum.arg] = int(val.arg)
+            else:
+                enumeration_dict[enum.arg] = '0'
+
+        for key, value in enumerate(enumeration_dict):
+            leaf.enumeration.append('{} = {} ;'.format(value, key))
+
+    def process_rpc(self, node, parent):
+        yrpc = YangRpc()
+        yrpc.name = node.arg  # name of rpc call
+        # look for input node
+        input_node = node.search_one("input")
+        if input_node and input_node.substmts:
+            self.process_children(input_node, parent, None)
+            # Get the first children - there should be only 1
+            yrpc.input = input_node.i_children[0].arg
+
+        output_node = node.search_one("output")
+        if output_node and output_node.substmts:
+            self.process_children(output_node, parent, None)
+            # Get the first children - there should be only 1
+            yrpc.output = output_node.i_children[0].arg
+        # print yrpc.ouput
+        # print yrpc.input
+        parent.rpcs.append(yrpc)
+
+    def get_protobuf_type(self, type):
+        type = self.base_type(type)
+        if type in self.protobuf_types_map.keys():
+            return self.protobuf_types_map[type]
+        else:
+            return type
+
+    def base_type(self, type):
+        """Return the base type of `type`."""
+        while 1:
+            if type.arg == "leafref":
+                node = type.i_type_spec.i_target_node
+            elif type.i_typedef is None:
+                break
+            else:
+                node = type.i_typedef
+            type = node.search_one("type")
+        if type.arg == "decimal64":
+            return [type.arg, int(type.search_one("fraction-digits").arg)]
+        elif type.arg == "union":
+            # TODO convert union properly
+            return type.arg
+            # return [type.arg,
+            #         [self.base_type(x) for x in type.i_type_spec.types]]
+        else:
+            return type.arg
+
+    protobuf_types_map = dict(
+        binary='Any',
+        bits='bytes',
+        boolean='bool',
+        decimal64='sint64',
+        empty='string',
+        int8='int32',
+        int16='int32',
+        int32='int32',
+        int64='int64',
+        string='string',
+        uint8='uint32',
+        uint16='uint32',
+        uint32='uint32',
+        uint64='uint64',
+        union='string',  # TODO : not correct mapping
+        enumeration='enum'
+    )