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