summaryrefslogtreecommitdiffhomepage
path: root/tools/pyang_plugins/ryu.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/pyang_plugins/ryu.py')
-rw-r--r--tools/pyang_plugins/ryu.py180
1 files changed, 180 insertions, 0 deletions
diff --git a/tools/pyang_plugins/ryu.py b/tools/pyang_plugins/ryu.py
new file mode 100644
index 00000000..9c0fed09
--- /dev/null
+++ b/tools/pyang_plugins/ryu.py
@@ -0,0 +1,180 @@
+# Copyright (C) 2013,2014 Nippon Telegraph and Telephone Corporation.
+# Copyright (C) 2013,2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
+#
+# 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.
+
+# this is a pyang plugin to generate ryu/lib/of_config/generated_classes.py
+# usage example:
+# PYTHONPATH=. ./bin/pyang --plugindir ~/git/ryu/tools/pyang_plugins -f ryu ~/git/ryu/tools/of-config1.1.1.yang > ~/git/ryu/lib/of_config/generated_classes.py
+
+
+_COPYRIGHT_NOTICE = """
+# Copyright (C) 2013,2014 Nippon Telegraph and Telephone Corporation.
+# Copyright (C) 2013,2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
+#
+# 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 sys
+import StringIO
+import pyang
+from pyang import plugin
+
+
+def pyang_plugin_init():
+ plugin.register_plugin(RyuPlugin())
+
+
+class RyuPlugin(plugin.PyangPlugin):
+ def add_output_format(self, fmts):
+ fmts['ryu'] = self
+
+ def emit(self, ctx, modules, fd):
+ emit_ryu(ctx, modules[0], fd)
+
+
+def emit_ryu(ctx, module, fd):
+ ctx.i_ryu_queue = []
+ visit_children(ctx, module, fd, module.i_children)
+ ctx.i_ryu_queue.reverse()
+ generate_header(ctx)
+ done = set()
+ for s in ctx.i_ryu_queue:
+ name = generate_type_name(s)
+ if name in done:
+ continue
+ generate_class(s)
+ done.add(name)
+
+
+def visit_children(ctx, module, fd, children, prefix=''):
+ for c in children:
+ if not is_leaf(c):
+ ctx.i_ryu_queue.append(c)
+ if hasattr(c, 'i_children'):
+ visit_children(ctx, module, fd, c.i_children, prefix + ' ')
+
+
+def is_leaf(s):
+ return s.keyword in ['leaf', 'leaf-list']
+
+
+def generate_header(ctx):
+ print _COPYRIGHT_NOTICE
+ print '# do not edit.'
+ print '# this file was mechanically generated with:'
+ print '# pyang %s' % pyang.__version__
+ print '# ryu.tools.pyang_plugins.ryu'
+ for mod, ver in sorted(ctx.modules):
+ print '# %s@%s' % (mod, ver)
+ print ''
+ print 'from ryu.lib.of_config.base import _Base, _e, _ct'
+
+
+def generate_class_def(s):
+ try:
+ return s.i_ryu_class_def
+ except AttributeError:
+ pass
+ s.i_ryu_class_def = _generate_class_def(s)
+ return s.i_ryu_class_def
+
+
+def _generate_class_def(s):
+ if is_leaf(s):
+ return generate_type_name(s)
+ o = StringIO.StringIO()
+ print >> o, 'class %s(_Base):' % (generate_type_name(s),)
+ print >> o, ' _ELEMENTS = ['
+ for c in s.i_children:
+ if is_leaf(c):
+ print >> o, ' _e(\'%s\', is_list=%s), # %s' % \
+ (c.arg, c.keyword == 'leaf-list', generate_type_name(c),)
+ else:
+ print >> o, ' _ct(\'%s\',' % (c.arg,)
+ print >> o, ' %s,' % (generate_type_name(c),)
+ print >> o, ' is_list=%s),' % (c.keyword == 'list',)
+ print >> o, ' ]'
+ return o.getvalue()
+
+
+def generate_class(s):
+ print ''
+ print ''
+ sys.stdout.write(generate_class_def(s))
+
+
+def same_class_def(s1, s2):
+ return generate_class_def(s1) == generate_class_def(s2)
+
+
+# 'hoge-hoge' -> 'HogeHoge'
+def pythonify(name):
+ a = name.split('-')
+ a = map(lambda x: x.capitalize(), a) # XXX locale sensitive
+ return ''.join(a)
+
+
+def chop_suf(s, suf):
+ if not s.endswith(suf):
+ return s
+ return s[:-len(suf)]
+
+
+_classes = {}
+
+
+def generate_type_name(s):
+ try:
+ return s.i_ryu_class_name
+ except AttributeError:
+ pass
+ s.i_ryu_class_name = name = _generate_type_name(s)
+ assert (not name in _classes) or same_class_def(_classes[name], s)
+ _classes[name] = s
+ return name
+
+
+def _generate_type_name(s):
+ # 1. use the hardcoded name for the top level capable-switch.
+ if s.arg == 'capable-switch':
+ return 'OFCapableSwitchType'
+ # 2. pick the name from the first yang grouping.
+ # this relies on the way OF-Config yang specification is written.
+ t = s.search_one('uses')
+ if t:
+ return t.arg
+ # 3. pick the name of yang type.
+ t = s.search_one('type')
+ if t:
+ return t.arg
+ # 4. infer from the parent's name.
+ # if the parent is 'OFFooType' and our name is 'bar-baz',
+ # use 'OFFooBarBazType'.
+ # again, this relies on the way OF-Config yang specification is written.
+ return (chop_suf(generate_type_name(s.parent), 'Type')
+ + pythonify(s.arg)
+ + 'Type')