blob: fc177b77be10bd76809bcc1f675efa5496b303bb [file] [log] [blame]
Khen Nursimulu4c435252016-11-03 23:21:32 -04001#!/usr/bin/env python
2#
3# Copyright 2016 the original author or authors.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18"""pyang plugin to convert a yang schema to a protobuf schema
19
Khen Nursimuluf8abbc92016-11-04 19:56:45 -040020 - basic support for leaf, leaf-list, containers, list
21
22 - this plugin requires pyang to be present and is run using pyang as
23 follows:
24
25 $ pyang --plugindir /voltha/experiments/plugin -f protobuf -o
26 <protofile> -p /voltha/tests/utests/netconf/yang
27 /voltha/tests/utests/netconf/yang/<yang file>
28
29 - pyang validates the yanhg definition first and then invoke this plugin
30 to convert the yang model into protobuf.
Khen Nursimulu4c435252016-11-03 23:21:32 -040031
32"""
33
34from pyang import plugin, statements, error
35from pyang.util import unique_prefixes
36
Khen Nursimuluf8abbc92016-11-04 19:56:45 -040037
Khen Nursimulu4c435252016-11-03 23:21:32 -040038# Register the Protobuf plugin
39def pyang_plugin_init():
40 plugin.register_plugin(ProtobufPlugin())
41
42
43class Protobuf():
44 def __init__(self):
45 self.tree = {}
46 self.containers = []
47 self.ylist = []
48 self.messages = []
49 self.enums = []
50 self.headers = []
51 self.services = []
52 self.rpcs = []
53
54 def set_headers(self, module_name):
55 self.headers.append('syntax = "proto3";')
Khen Nursimuluf8abbc92016-11-04 19:56:45 -040056 self.headers.append('package {};'.format(module_name.replace('-',
57 '_')))
Khen Nursimulu4c435252016-11-03 23:21:32 -040058
59 def _print_container(self, container, out, level=0):
60 spaces = ' ' * level
61 out.append(''.join([spaces, 'message {} '.format(container.name)]))
62 out.append(''.join('{\n'))
Khen Nursimuluf8abbc92016-11-04 19:56:45 -040063 self._print_leaf(container.leafs, out, spaces)
64
65 for l in container.ylist:
66 self._print_list(l, out, level)
Khen Nursimulu4c435252016-11-03 23:21:32 -040067
68 for inner in container.containers:
69 self._print_container(inner, out, level + 1)
70
71 out.append(''.join([spaces, '}\n']))
72
Khen Nursimuluf8abbc92016-11-04 19:56:45 -040073 def _print_list(self, ylist, out, level=0):
74 spaces = ' ' * level
Khen Nursimulu4c435252016-11-03 23:21:32 -040075 out.append('message {} '.format(ylist.name))
76 out.append('{\n')
Khen Nursimuluf8abbc92016-11-04 19:56:45 -040077 self._print_leaf(ylist.leafs, out, spaces)
78
79 for l in ylist.ylist:
80 self._print_list(l, out, level + 1)
81
Khen Nursimulu4c435252016-11-03 23:21:32 -040082 out.append('}\n')
83
Khen Nursimuluf8abbc92016-11-04 19:56:45 -040084 def _print_leaf(self, leafs, out, spaces):
85 for idx, l in enumerate(leafs):
86 leafspaces = ''.join([spaces, ' '])
87 if l.type == "enum":
88 out.append(''.join([leafspaces, 'enum {}\n'.format(l.name)]))
89 out.append(''.join([leafspaces, '{\n']))
90 self._print_enumeration(l.enumeration, out, leafspaces)
91 out.append(''.join([leafspaces, '}\n']))
92 else:
93 out.append(''.join([leafspaces, '{}{} {} = {} ;\n'.format(
94 'repeated ' if l.leaf_list else '',
95 l.type,
96 l.name,
97 idx + 1)]))
98
99 def _print_enumeration(self, yang_enum, out, spaces):
100 enumspaces = ''.join([spaces, ' '])
101 for idx, e in enumerate(yang_enum):
102 out.append(''.join([enumspaces, '{}\n'.format(e)]))
103
Khen Nursimulu4c435252016-11-03 23:21:32 -0400104 def print_proto(self):
105 out = []
106 for h in self.headers:
107 out.append('{}\n'.format(h))
108 out.append('\n')
109 for m in self.messages:
110 out.append('{}\n'.format(m))
Khen Nursimuluf8abbc92016-11-04 19:56:45 -0400111 if self.messages:
112 out.append('\n')
Khen Nursimulu4c435252016-11-03 23:21:32 -0400113 for l in self.ylist:
114 self._print_list(l, out)
Khen Nursimuluf8abbc92016-11-04 19:56:45 -0400115 if self.ylist:
116 out.append('\n')
Khen Nursimulu4c435252016-11-03 23:21:32 -0400117 for c in self.containers:
118 self._print_container(c, out)
Khen Nursimuluf8abbc92016-11-04 19:56:45 -0400119 if self.containers:
120 out.append('\n')
Khen Nursimulu4c435252016-11-03 23:21:32 -0400121
122 return out
123
124
125class YangContainer():
126 def __init__(self):
127 self.name = None
128 self.containers = []
129 self.enums = []
130 self.leafs = []
Khen Nursimuluf8abbc92016-11-04 19:56:45 -0400131 self.ylist = []
Khen Nursimulu4c435252016-11-03 23:21:32 -0400132
133
134class YangList():
135 def __init__(self):
136 self.name = None
137 self.leafs = []
Khen Nursimuluf8abbc92016-11-04 19:56:45 -0400138 self.containers = []
139 self.ylist = []
Khen Nursimulu4c435252016-11-03 23:21:32 -0400140
141
142class YangLeaf():
143 def __init__(self):
144 self.name = None
145 self.type = None
146 self.leaf_list = False
Khen Nursimuluf8abbc92016-11-04 19:56:45 -0400147 self.enumeration = []
Khen Nursimulu4c435252016-11-03 23:21:32 -0400148 self.description = None
149
150
Khen Nursimuluf8abbc92016-11-04 19:56:45 -0400151class YangEnumeration():
152 def __init__(self):
153 self.value = []
154
155
Khen Nursimulu4c435252016-11-03 23:21:32 -0400156class ProtobufPlugin(plugin.PyangPlugin):
157 def add_output_format(self, fmts):
158 self.multiple_modules = True
159 fmts['protobuf'] = self
160
161 def setup_fmt(self, ctx):
162 ctx.implicit_errors = False
163
164 def emit(self, ctx, modules, fd):
165 """Main control function.
166 """
167 self.real_prefix = unique_prefixes(ctx)
168
169 for m in modules:
Khen Nursimuluf8abbc92016-11-04 19:56:45 -0400170 # m.pprint()
171 # statements.print_tree(m)
Khen Nursimulu4c435252016-11-03 23:21:32 -0400172 proto = Protobuf()
173 proto.set_headers(m.i_modulename)
Khen Nursimuluf8abbc92016-11-04 19:56:45 -0400174 self.process_substatements(m, proto, None)
Khen Nursimulu4c435252016-11-03 23:21:32 -0400175 self.process_children(m, proto, None)
176 out = proto.print_proto()
177 for i in out:
178 fd.write(i)
179
Khen Nursimuluf8abbc92016-11-04 19:56:45 -0400180 def process_substatements(self, node, parent, pmod):
181 """Process all substmts.
182 """
183 for st in node.substmts:
184 if st.keyword in ["rpc", "notification"]: continue
185 if st.keyword in ["choice", "case"]:
186 self.process_substatements(st, parent, pmod)
187 continue
188
189 if st.i_module.i_modulename == pmod:
190 nmod = pmod
191 else:
192 nmod = st.i_module.i_modulename
193
194 if st.keyword in ["container", "grouping"]:
195 c = YangContainer()
196 c.name = st.arg
197 self.process_substatements(st, c, nmod)
198 parent.containers.append(c)
199 elif st.keyword == "list":
200 l = YangList()
201 l.name = st.arg
202 self.process_substatements(st, l, nmod)
203 parent.ylist.append(l)
204 elif st.keyword in ["leaf", "leaf-list"]:
205 self.process_leaf(st, parent, st.keyword == "leaf-list")
206
Khen Nursimulu4c435252016-11-03 23:21:32 -0400207 def process_children(self, node, parent, pmod):
208 """Process all children of `node`, except "rpc" and "notification".
209 """
210 for ch in node.i_children:
211 if ch.keyword in ["rpc", "notification"]: continue
212 if ch.keyword in ["choice", "case"]:
213 self.process_children(ch, parent, pmod)
214 continue
215 if ch.i_module.i_modulename == pmod:
216 nmod = pmod
Khen Nursimulu4c435252016-11-03 23:21:32 -0400217 else:
218 nmod = ch.i_module.i_modulename
Khen Nursimuluf8abbc92016-11-04 19:56:45 -0400219 if ch.keyword in ["container", "grouping"]:
Khen Nursimulu4c435252016-11-03 23:21:32 -0400220 c = YangContainer()
221 c.name = ch.arg
222 self.process_children(ch, c, nmod)
223 parent.containers.append(c)
224 # self.process_container(ch, p, nmod)
225 elif ch.keyword == "list":
226 l = YangList()
227 l.name = ch.arg
228 self.process_children(ch, l, nmod)
229 parent.ylist.append(l)
230 elif ch.keyword in ["leaf", "leaf-list"]:
231 self.process_leaf(ch, parent, ch.keyword == "leaf-list")
232
233 def process_leaf(self, node, parent, leaf_list=False):
234 # Leaf have specific sub statements
235 leaf = YangLeaf()
236 leaf.name = node.arg
237 leaf.type = self.get_protobuf_type(node.search_one("type"))
Khen Nursimuluf8abbc92016-11-04 19:56:45 -0400238 if leaf.type == "enum":
239 self.process_enumeration(node, leaf)
Khen Nursimulu4c435252016-11-03 23:21:32 -0400240 # leaf.type = self.base_type(node.search_one("type"))
241 leaf.description = node.search_one("description")
242 leaf.leaf_list = leaf_list
243 parent.leafs.append(leaf)
244
Khen Nursimuluf8abbc92016-11-04 19:56:45 -0400245 def process_enumeration(self, node, leaf):
246 enumeration_dict = {}
247 start_node = None
248 for child in node.substmts:
249 if child.keyword == "type":
250 start_node = child;
251 break
252
253 for enum in start_node.search('enum'):
254 val = enum.search_one('value')
255 if val is not None:
256 enumeration_dict[enum.arg] = int(val.arg)
257 else:
258 enumeration_dict[enum.arg] = '0'
259
260 for key, value in enumerate(enumeration_dict):
261 leaf.enumeration.append('{} = {} ;'.format(value, key))
262
Khen Nursimulu4c435252016-11-03 23:21:32 -0400263 def get_protobuf_type(self, type):
Khen Nursimulu4c435252016-11-03 23:21:32 -0400264 type = self.base_type(type)
Khen Nursimuluf8abbc92016-11-04 19:56:45 -0400265 if type in self.protobuf_types_map.keys():
266 return self.protobuf_types_map[type]
Khen Nursimulu4c435252016-11-03 23:21:32 -0400267 else:
268 return type
269
270 def base_type(self, type):
271 """Return the base type of `type`."""
272 while 1:
273 if type.arg == "leafref":
274 node = type.i_type_spec.i_target_node
275 elif type.i_typedef is None:
276 break
277 else:
278 node = type.i_typedef
279 type = node.search_one("type")
280 if type.arg == "decimal64":
281 return [type.arg, int(type.search_one("fraction-digits").arg)]
282 elif type.arg == "union":
Khen Nursimuluf8abbc92016-11-04 19:56:45 -0400283 # TODO convert union properly
284 return type.arg
285 # return [type.arg,
286 # [self.base_type(x) for x in type.i_type_spec.types]]
Khen Nursimulu4c435252016-11-03 23:21:32 -0400287 else:
288 return type.arg
Khen Nursimuluf8abbc92016-11-04 19:56:45 -0400289
290 protobuf_types_map = dict(
291 binary='Any',
292 bits='bytes',
293 boolean='bool',
294 decimal64='sint64',
295 empty='string',
296 int8='int32',
297 int16='int32',
298 int32='int32',
299 int64='int64',
300 string='string',
301 uint8='uint32',
302 uint16='uint32',
303 uint32='uint32',
304 uint64='uint64',
305 union='string', # TODO : not correct mapping
306 enumeration='enum'
307 )