blob: 42e83c308c3283491e28657e98c48c25b45499c6 [file] [log] [blame]
Matteo Scandolod2044a42017-08-07 16:08:28 -07001
2# Copyright 2017-present Open Networking Foundation
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16
Sapan Bhatia3cfdf632017-06-08 05:14:03 +020017import plyxproto.model as m
Sapan Bhatiadb183c22017-06-23 02:47:42 -070018from plyxproto.helpers import Visitor
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -070019import argparse
Sapan Bhatia3cfdf632017-06-08 05:14:03 +020020import plyxproto.parser as plyxproto
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -070021import traceback
22import sys
23import jinja2
24import os
Sapan Bhatia1e397df2017-05-24 12:17:28 +020025import copy
Sapan Bhatiad3fcb662017-07-25 21:13:48 -040026import pdb
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -070027
Sapan Bhatiad3fcb662017-07-25 21:13:48 -040028class MissingPolicyException(Exception):
29 pass
30
31def find_missing_policy_calls(name, policies, policy):
32 if isinstance(policy, dict):
33 (k, lst), = policy.items()
34 if k=='policy':
35 policy_name = lst[0]
36 if policy_name not in policies: raise MissingPolicyException("Policy %s invoked missing policy %s"%(name, policy_name))
37 else:
38 for p in lst:
39 find_missing_policy_calls(name, policies, p)
40 elif isinstance(policy, list):
41 for p in lst:
42 find_missing_policy_calls(name, policies, p)
Matteo Scandolo67654fa2017-06-09 09:33:17 -070043
Sapan Bhatia3cfdf632017-06-08 05:14:03 +020044def dotname_to_fqn(dotname):
45 b_names = [part.pval for part in dotname]
46 package = '.'.join(b_names[:-1])
47 name = b_names[-1]
48 if package:
49 fqn = package + '.' + name
50 else:
51 fqn = name
Matteo Scandolo67654fa2017-06-09 09:33:17 -070052 return {'name': name, 'fqn': fqn, 'package': package}
53
Sapan Bhatia3cfdf632017-06-08 05:14:03 +020054
55def dotname_to_name(dotname):
56 b_names = [part.pval for part in dotname]
57 return '.'.join(b_names)
58
59
Sapan Bhatia504cc972017-04-27 01:56:28 +020060def count_messages(body):
61 count = 0
62 for e in body:
Matteo Scandolo67654fa2017-06-09 09:33:17 -070063 if (type(e) == m.MessageDefinition):
64 count += 1
Sapan Bhatia504cc972017-04-27 01:56:28 +020065 return count
66
Matteo Scandolo67654fa2017-06-09 09:33:17 -070067
Sapan Bhatia504cc972017-04-27 01:56:28 +020068def count_fields(body):
69 count = 0
70 for e in body:
Matteo Scandolo67654fa2017-06-09 09:33:17 -070071 if (type(e) in [m.LinkDefinition, m.FieldDefinition, m.LinkSpec]):
72 count += 1
Sapan Bhatia504cc972017-04-27 01:56:28 +020073 return count
74
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +020075def compute_rlinks(messages, message_dict):
Sapan Bhatia1e397df2017-05-24 12:17:28 +020076 rev_links = {}
77
78 link_opposite = {
Matteo Scandolo67654fa2017-06-09 09:33:17 -070079 'manytomany': 'manytomany',
80 'manytoone': 'onetomany',
81 'onetoone': 'onetoone',
82 'onetomany': 'manytoone'
Sapan Bhatia1e397df2017-05-24 12:17:28 +020083 }
84
85 for m in messages:
86 for l in m['links']:
87 rlink = copy.deepcopy(l)
Sapan Bhatia3cfdf632017-06-08 05:14:03 +020088
Matteo Scandolo67654fa2017-06-09 09:33:17 -070089 rlink['_type'] = 'rlink' # An implicit link, not declared in the model
Sapan Bhatia1e397df2017-05-24 12:17:28 +020090 rlink['src_port'] = l['dst_port']
91 rlink['dst_port'] = l['src_port']
Matteo Scandolo67654fa2017-06-09 09:33:17 -070092 rlink['peer'] = {'name': m['name'], 'package': m['package'], 'fqn': m['fqn']}
Sapan Bhatia1e397df2017-05-24 12:17:28 +020093 rlink['link_type'] = link_opposite[l['link_type']]
94
95 try:
Sapan Bhatia3cfdf632017-06-08 05:14:03 +020096 try:
97 rev_links[l['peer']['fqn']].append(rlink)
98 except TypeError:
Sapan Bhatia3cfdf632017-06-08 05:14:03 +020099 pass
Sapan Bhatia1e397df2017-05-24 12:17:28 +0200100 except KeyError:
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200101 rev_links[l['peer']['fqn']] = [rlink]
Sapan Bhatia1e397df2017-05-24 12:17:28 +0200102
103 for m in messages:
104 try:
105 m['rlinks'] = rev_links[m['name']]
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200106 message_dict[m['name']]['rlinks'] = m['rlinks']
Sapan Bhatia1e397df2017-05-24 12:17:28 +0200107 except KeyError:
108 pass
109
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700110
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200111def name_to_value(obj):
112 try:
113 value = obj.value.value.pval
114 except AttributeError:
115 try:
116 value = obj.value.value
117 except AttributeError:
118 value = obj.value.pval
119
120 return value
121
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700122
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700123class Stack(list):
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700124 def push(self, x):
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700125 self.append(x)
126
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700127
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700128''' XOS2Jinja overrides the underlying visitor pattern to transform the tree
129 in addition to traversing it '''
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700130
Sapan Bhatiadb183c22017-06-23 02:47:42 -0700131class XOS2Jinja(Visitor):
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700132 def __init__(self):
133 super(XOS2Jinja, self).__init__()
134
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700135 self.stack = Stack()
136 self.models = {}
137 self.options = {}
138 self.package = None
139 self.message_options = {}
140 self.count_stack = Stack()
Sapan Bhatiadb183c22017-06-23 02:47:42 -0700141 self.policies = {}
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700142 self.content = ""
143 self.offset = 0
144 self.current_message_name = None
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700145 self.verbose = 0
146 self.first_field = True
147 self.first_method = True
148
Sapan Bhatiadb183c22017-06-23 02:47:42 -0700149 def visit_PolicyDefinition(self, obj):
150 if self.package:
151 pname = '.'.join([self.package, obj.name.value.pval])
152 else:
153 pname = obj.name.value.pval
154
155 self.policies[pname] = obj.body
Sapan Bhatiad3fcb662017-07-25 21:13:48 -0400156 find_missing_policy_calls(pname, self.policies, obj.body)
Sapan Bhatia3e3c1cd2017-07-15 01:35:44 -0400157
158 return True
159
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700160 def visit_PackageStatement(self, obj):
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200161 dotlist = obj.name.value
162 dotlist2 = [f.pval for f in dotlist]
163 dotstr = '.'.join(dotlist2)
164 self.package = dotstr
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700165 return True
166
167 def visit_ImportStatement(self, obj):
168 '''Ignore'''
169 return True
170
171 def visit_OptionStatement(self, obj):
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700172 if not hasattr(obj, 'mark_for_deletion'):
Sapan Bhatiaae9645c2017-05-05 15:35:54 +0200173 if (self.current_message_name):
174 self.message_options[obj.name.value.pval] = obj.value.value.pval
175 else:
176 self.options[obj.name.value.pval] = obj.value.value.pval
Sapan Bhatia504cc972017-04-27 01:56:28 +0200177
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700178 return True
179
180 def visit_LU(self, obj):
181 return True
182
183 def visit_default(self, obj):
184 return True
185
186 def visit_FieldDirective(self, obj):
187 return True
188
189 def visit_FieldDirective_post(self, obj):
190
191 try:
192 name = obj.name.value.pval
193 except AttributeError:
194 name = obj.name.value
195
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700196 if type(obj.value) == list:
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200197 value = dotname_to_name(obj.value)
198 else:
199 value = name_to_value(obj)
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700200
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700201 self.stack.push([name, value])
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700202 return True
203
204 def visit_FieldType(self, obj):
205 '''Field type, if type is name, then it may need refactoring consistent with refactoring rules according to the table'''
206 return True
207
208 def visit_LinkDefinition(self, obj):
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700209 s = {}
Sapan Bhatia8a57c662017-04-11 10:39:47 -0700210
Sapan Bhatiaae9645c2017-05-05 15:35:54 +0200211 try:
212 s['link_type'] = obj.link_type.pval
213 except AttributeError:
214 s['link_type'] = obj.link_type
215
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700216 s['src_port'] = obj.src_port.value.pval
217 s['name'] = obj.src_port.value.pval
Sapan Bhatiadb183c22017-06-23 02:47:42 -0700218 try:
219 s['policy'] = obj.policy.pval
220 except AttributeError:
221 s['policy'] = None
Sapan Bhatiac4f803f2017-04-21 11:50:39 +0200222
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700223 try:
224 s['dst_port'] = obj.dst_port.value.pval
225 except AttributeError:
Sapan Bhatiaae9645c2017-05-05 15:35:54 +0200226 s['dst_port'] = obj.dst_port
Sapan Bhatiac4f803f2017-04-21 11:50:39 +0200227
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700228 if type(obj.through) == list:
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200229 s['through'] = dotname_to_fqn(obj.through)
230 else:
231 try:
232 s['through'] = obj.through.pval
233 except AttributeError:
234 s['through'] = obj.through
Sapan Bhatiac4f803f2017-04-21 11:50:39 +0200235
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700236 if type(obj.name) == list:
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200237 s['peer'] = dotname_to_fqn(obj.name)
238 else:
239 try:
240 s['peer'] = obj.name.pval
241 except AttributeError:
242 s['peer'] = obj.name
Sapan Bhatiaae9645c2017-05-05 15:35:54 +0200243
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700244 s['_type'] = 'link'
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700245 s['options'] = {'modifier': 'optional'}
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700246
247 self.stack.push(s)
248 return True
249
250 def visit_FieldDefinition(self, obj):
251 self.count_stack.push(len(obj.fieldDirective))
252 return True
253
254 def visit_FieldDefinition_post(self, obj):
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700255 s = {}
256
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700257 if isinstance(obj.ftype, m.Name):
258 s['type'] = obj.ftype.value
259 else:
260 s['type'] = obj.ftype.name.pval
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200261
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700262 s['name'] = obj.name.value.pval
Sapan Bhatiadb183c22017-06-23 02:47:42 -0700263
264 try:
265 s['policy'] = obj.policy.pval
266 except AttributeError:
267 s['policy'] = None
268
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700269 s['modifier'] = obj.field_modifier.pval
270 s['id'] = obj.fieldId.pval
271
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700272 opts = {'modifier': s['modifier']}
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700273 n = self.count_stack.pop()
274 for i in range(0, n):
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700275 k, v = self.stack.pop()
Sapan Bhatia5769d932017-05-17 11:10:54 +0200276
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700277 # The two lines below may be added to eliminate "" around an option.
Sapan Bhatia5769d932017-05-17 11:10:54 +0200278 # Right now, this is handled in targets. FIXME
279 #
280 # if (v.startswith('"') and v.endswith('"')):
281 # v = v[1:-1]
282
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700283 opts[k] = v
284
285 s['options'] = opts
Sapan Bhatia8a57c662017-04-11 10:39:47 -0700286 try:
287 last_link = self.stack[-1]['_type']
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700288 if (last_link == 'link'):
Sapan Bhatia8a57c662017-04-11 10:39:47 -0700289 s['link'] = True
290 except:
291 pass
292 s['_type'] = 'field'
293
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700294 self.stack.push(s)
295 return True
296
297 def visit_EnumFieldDefinition(self, obj):
298 if self.verbose > 4:
299 print "\tEnumField: name=%s, %s" % (obj.name, obj)
300
301 return True
302
303 def visit_EnumDefinition(self, obj):
304 '''New enum definition, refactor name'''
305 if self.verbose > 3:
306 print "Enum, [%s] body=%s\n\n" % (obj.name, obj.body)
307
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700308 return True
309
310 def visit_MessageDefinition(self, obj):
Sapan Bhatia504cc972017-04-27 01:56:28 +0200311 self.current_message_name = obj.name.value.pval
312 self.message_options = {}
313 self.count_stack.push(count_fields(obj.body))
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700314 return True
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700315
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700316 def visit_MessageDefinition_post(self, obj):
317 stack_num = self.count_stack.pop()
318 fields = []
319 links = []
320 last_field = None
Sapan Bhatiaae9645c2017-05-05 15:35:54 +0200321 try:
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200322 obj.bases = map(dotname_to_fqn, obj.bases)
Sapan Bhatiaae9645c2017-05-05 15:35:54 +0200323 except AttributeError:
324 pass
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700325
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200326 last_field = {}
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700327 for i in range(0, stack_num):
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700328 f = self.stack.pop()
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700329 if (f['_type'] == 'link'):
330 f['options'] = {i: d[i] for d in [f['options'], last_field['options']] for i in d}
331 assert (last_field == fields[0])
Sapan Bhatiad022aeb2017-06-07 15:49:55 +0200332 fields[0].setdefault('options', {})['link_type'] = f['link_type']
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700333 links.insert(0, f)
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700334 else:
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700335 fields.insert(0, f)
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700336 last_field = f
337
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200338 if self.package:
339 model_name = '.'.join([self.package, obj.name.value.pval])
340 else:
341 model_name = obj.name.value.pval
342
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200343 model_def = {'name':obj.name.value.pval,'fields':fields,'links':links, 'bases':obj.bases, 'options':self.message_options, 'package':self.package, 'fqn': model_name, 'rlinks': []}
Sapan Bhatiadb183c22017-06-23 02:47:42 -0700344 try:
345 model_def['policy'] = obj.policy.pval
346 except AttributeError:
347 model_def['policy'] = None
348
Sapan Bhatia943dad52017-05-19 18:41:01 +0200349 self.stack.push(model_def)
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200350
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200351 self.models[model_name] = model_def
352
353 # Set message options
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700354 for k, v in self.options.iteritems():
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200355 try:
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200356 if k not in self.message_options:
357 self.message_options[k] = v
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200358 except KeyError:
359 pass
360
Sapan Bhatia504cc972017-04-27 01:56:28 +0200361 self.current_message_name = None
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700362 return True
363
364 def visit_MessageExtension(self, obj):
365 return True
366
367 def visit_MethodDefinition(self, obj):
368 return True
369
370 def visit_ServiceDefinition(self, obj):
371 return True
372
373 def visit_ExtensionsDirective(self, obj):
374 return True
375
376 def visit_Literal(self, obj):
377 return True
378
379 def visit_Name(self, obj):
380 return True
381
382 def visit_DotName(self, obj):
383 return True
384
385 def visit_Proto(self, obj):
Sapan Bhatia504cc972017-04-27 01:56:28 +0200386 self.count_stack.push(count_messages(obj.body))
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700387 return True
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700388
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700389 def visit_Proto_post(self, obj):
390 count = self.count_stack.pop()
391 messages = []
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700392 for i in range(0, count):
Sapan Bhatiac4f803f2017-04-21 11:50:39 +0200393 try:
394 m = self.stack.pop()
395 except IndexError:
Sapan Bhatia504cc972017-04-27 01:56:28 +0200396 pass
Sapan Bhatiac4f803f2017-04-21 11:50:39 +0200397
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700398 messages.insert(0, m)
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700399
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200400 compute_rlinks(messages, self.models)
401
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700402 self.messages = messages
403 return True
404
405 def visit_LinkSpec(self, obj):
406 count = self.count_stack.pop()
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700407 self.count_stack.push(count + 1)
Sapan Bhatiadb183c22017-06-23 02:47:42 -0700408 return True