Update the Netconf custom rpc as follows:
1) Create a message/field YANG reference.  This is used to keep the
XML tags in the same order as the fields appear in the YANG schema.
This applies only for custom RPCs (one of Netconf twist)
2) Annotate the proto RPCs with custom annotations which are used
when constructing an XML response

Change-Id: I07a8a3f2a44b7081c78e00dab05734a7c6b0a358
diff --git a/netconf/protoc_plugins/proto2yang.py b/netconf/protoc_plugins/proto2yang.py
index fc887a6..e9bf614 100755
--- a/netconf/protoc_plugins/proto2yang.py
+++ b/netconf/protoc_plugins/proto2yang.py
@@ -40,12 +40,39 @@
     FieldDescriptorProto
 from descriptor_parser import DescriptorParser
 import copy
+from netconf.constants import Constants as C
 import yang_options_pb2
 
 from google.protobuf.descriptor import FieldDescriptor
 
 import jinja2
-env = jinja2.Environment(extensions=["jinja2.ext.do",], trim_blocks=True, lstrip_blocks=True)
+
+env = jinja2.Environment(extensions=["jinja2.ext.do", ], trim_blocks=True,
+                         lstrip_blocks=True)
+
+template_yang_definition = env.from_string("""
+# Generated file; please do not edit
+
+from structlog import get_logger
+
+log = get_logger()
+
+message_definitions = {
+    {% for m in messages %}
+    '{{ m.name }}': {{ m.fields }},
+     {% if loop.last %}{% endif %}
+    {% endfor %}
+}
+
+def get_fields(package, type_name, **kw):
+    log.info('fields-request', type=type_name, package=package, **kw)
+    full_name = ''.join([package, '-', type_name])
+    if message_definitions.has_key(full_name):
+        return message_definitions[full_name]
+    else:
+        return None
+
+""")
 
 template_yang = env.from_string("""
 module ietf-{{ module.name }} {
@@ -741,6 +768,77 @@
                 m['output'] = ''.join([m['output'], '_grouping'])
 
 
+def get_module_name(type, data_types):
+    for t in data_types:
+        # Verify both the type and when it is a referred type as they will
+        # both be in the same module
+        if t['type'] in [type, ''.join([type, '_grouping'])]:
+            return t['module']
+
+    # return the default module name
+    return 'voltha'
+
+
+def get_message_defs(messages, data_types, msg_response):
+    for msg in messages:
+        fields = []
+
+        # First process the fields as they appear before the oneofs in the
+        # YANG module
+        for f in msg['fields']:
+            module_name = '.'
+            if f['type_ref']:
+                module_name = get_module_name(f['type'], data_types)
+            fields.append(
+                {
+                    'oneof_key': None,
+                    'repeated': f['repeated'],
+                    'name': f['name'],
+                    'full_name': f['full_name'],
+                    'type': f['type'],
+                    'type_ref': f['type_ref'],
+                    'module': module_name
+                }
+            )
+
+        # Now process the oneofs
+        if msg['oneofs']:
+            for key, value in msg['oneofs'].iteritems():
+                # Value contains a list of fields
+                for v in value:
+                    module_name = '.'
+                    if v['type_ref']:
+                        module_name = get_module_name(v['type'], data_types)
+                    fields.append(
+                        {
+                            'oneof_key': key,
+                            'repeated': v['repeated'],
+                            'name': v['name'],
+                            'full_name': v['full_name'],
+                            'type': v['type'],
+                            'type_ref': v['type_ref'],
+                            'module': module_name
+                        }
+                    )
+
+        msg_response.append({
+            'name': msg['full_name'],
+            'fields': fields
+        })
+
+        if msg['messages']:
+            get_message_defs(msg['messages'], data_types, msg_response)
+
+
+def build_yang_definitions(all_proto_data):
+    msg_response = []
+    for proto_data in all_proto_data:
+        get_message_defs(proto_data['module']['messages'], proto_data[
+            'module']['data_types'], msg_response)
+
+    return msg_response
+
+
 def generate_code(request, response):
     assert isinstance(request, plugin.CodeGeneratorRequest)
 
@@ -839,6 +937,17 @@
         # print_message(proto_data['module']['messages'])
         f.content = template_yang.render(module=proto_data['module'])
 
+    # Create a summary of the YANG definitions with the order in which the
+    # attributes appear in each message.  It would have been easier to sort
+    # the attributes in the YANG files and then sort the XML tags when a
+    # XML response is built.  However, this strategy won't work with the oneof
+    # protobuf definition.  The attributes in the oneof need to be kept
+    # together and as such will break the sort strategy.
+    msg_response = build_yang_definitions(all_proto_data)
+    yang_def = response.file.add()
+    yang_def.name = C.YANG_MESSAGE_DEFINITIONS_FILE
+    yang_def.content = template_yang_definition.render(messages=msg_response)
+
 
 def get_yang_type(field):
     type = field['type']
diff --git a/netconf/protoc_plugins/rpc_gw_gen.py b/netconf/protoc_plugins/rpc_gw_gen.py
index 671bf28..3a3af0f 100755
--- a/netconf/protoc_plugins/rpc_gw_gen.py
+++ b/netconf/protoc_plugins/rpc_gw_gen.py
@@ -22,6 +22,7 @@
     MethodOptions
 from jinja2 import Template
 from simplejson import dumps
+import yang_options_pb2
 
 from netconf.protos.third_party.google.api import annotations_pb2, http_pb2
 
@@ -73,6 +74,15 @@
     log.info('{{ method_name }}', **out_data)
     returnValue(out_data)
 
+def get_xml_tag_{{ method_name }}():
+    return '{{ method['xml_tag'] }}'
+
+def get_list_items_name_{{ method_name }}():
+    return '{{ method['list_item_name'] }}'
+
+def get_return_type_{{ method_name }}():
+    return '{{ type_map[method['output_type']] }}'
+
 {% endfor %}
 
 """, trim_blocks=True, lstrip_blocks=True)
@@ -92,13 +102,27 @@
             if output_type.startswith('.'):
                 output_type = output_type[1:]
 
+            # Process any specific yang option
+            xml_tag = ''
+            list_item_name = ''
+            options = method.options
+            assert isinstance(options, MethodOptions)
+            for fd, yang_tag in options.ListFields():
+                if fd.full_name == 'voltha.yang_xml_tag':
+                    if yang_tag.xml_tag:
+                        xml_tag = yang_tag.xml_tag
+                    if yang_tag.list_items_name:
+                        list_item_name = yang_tag.list_items_name
+
             data = {
                 'package': package,
                 'filename': proto_file.name,
                 'service': proto_file.package + '.' + service.name,
                 'method': method.name,
                 'input_type': input_type,
-                'output_type': output_type
+                'output_type': output_type,
+                'xml_tag': xml_tag,
+                'list_item_name': list_item_name
             }
 
             yield data