blob: efcb995f97303e32f2e37f8f22bc7805fca9a8f1 [file] [log] [blame]
Sapan Bhatia3cfdf632017-06-08 05:14:03 +02001import plyxproto.model as m
Sapan Bhatiadb183c22017-06-23 02:47:42 -07002from plyxproto.helpers import Visitor
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -07003import argparse
Sapan Bhatia3cfdf632017-06-08 05:14:03 +02004import plyxproto.parser as plyxproto
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -07005import traceback
6import sys
7import jinja2
8import os
Sapan Bhatia1e397df2017-05-24 12:17:28 +02009import copy
Sapan Bhatiad3fcb662017-07-25 21:13:48 -040010import pdb
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -070011
Sapan Bhatiad3fcb662017-07-25 21:13:48 -040012class MissingPolicyException(Exception):
13 pass
14
15def find_missing_policy_calls(name, policies, policy):
16 if isinstance(policy, dict):
17 (k, lst), = policy.items()
18 if k=='policy':
19 policy_name = lst[0]
20 if policy_name not in policies: raise MissingPolicyException("Policy %s invoked missing policy %s"%(name, policy_name))
21 else:
22 for p in lst:
23 find_missing_policy_calls(name, policies, p)
24 elif isinstance(policy, list):
25 for p in lst:
26 find_missing_policy_calls(name, policies, p)
Matteo Scandolo67654fa2017-06-09 09:33:17 -070027
Sapan Bhatia3cfdf632017-06-08 05:14:03 +020028def dotname_to_fqn(dotname):
29 b_names = [part.pval for part in dotname]
30 package = '.'.join(b_names[:-1])
31 name = b_names[-1]
32 if package:
33 fqn = package + '.' + name
34 else:
35 fqn = name
Matteo Scandolo67654fa2017-06-09 09:33:17 -070036 return {'name': name, 'fqn': fqn, 'package': package}
37
Sapan Bhatia3cfdf632017-06-08 05:14:03 +020038
39def dotname_to_name(dotname):
40 b_names = [part.pval for part in dotname]
41 return '.'.join(b_names)
42
43
Sapan Bhatia504cc972017-04-27 01:56:28 +020044def count_messages(body):
45 count = 0
46 for e in body:
Matteo Scandolo67654fa2017-06-09 09:33:17 -070047 if (type(e) == m.MessageDefinition):
48 count += 1
Sapan Bhatia504cc972017-04-27 01:56:28 +020049 return count
50
Matteo Scandolo67654fa2017-06-09 09:33:17 -070051
Sapan Bhatia504cc972017-04-27 01:56:28 +020052def count_fields(body):
53 count = 0
54 for e in body:
Matteo Scandolo67654fa2017-06-09 09:33:17 -070055 if (type(e) in [m.LinkDefinition, m.FieldDefinition, m.LinkSpec]):
56 count += 1
Sapan Bhatia504cc972017-04-27 01:56:28 +020057 return count
58
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +020059def compute_rlinks(messages, message_dict):
Sapan Bhatia1e397df2017-05-24 12:17:28 +020060 rev_links = {}
61
62 link_opposite = {
Matteo Scandolo67654fa2017-06-09 09:33:17 -070063 'manytomany': 'manytomany',
64 'manytoone': 'onetomany',
65 'onetoone': 'onetoone',
66 'onetomany': 'manytoone'
Sapan Bhatia1e397df2017-05-24 12:17:28 +020067 }
68
69 for m in messages:
70 for l in m['links']:
71 rlink = copy.deepcopy(l)
Sapan Bhatia3cfdf632017-06-08 05:14:03 +020072
Matteo Scandolo67654fa2017-06-09 09:33:17 -070073 rlink['_type'] = 'rlink' # An implicit link, not declared in the model
Sapan Bhatia1e397df2017-05-24 12:17:28 +020074 rlink['src_port'] = l['dst_port']
75 rlink['dst_port'] = l['src_port']
Matteo Scandolo67654fa2017-06-09 09:33:17 -070076 rlink['peer'] = {'name': m['name'], 'package': m['package'], 'fqn': m['fqn']}
Sapan Bhatia1e397df2017-05-24 12:17:28 +020077 rlink['link_type'] = link_opposite[l['link_type']]
78
79 try:
Sapan Bhatia3cfdf632017-06-08 05:14:03 +020080 try:
81 rev_links[l['peer']['fqn']].append(rlink)
82 except TypeError:
Sapan Bhatia3cfdf632017-06-08 05:14:03 +020083 pass
Sapan Bhatia1e397df2017-05-24 12:17:28 +020084 except KeyError:
Sapan Bhatia3cfdf632017-06-08 05:14:03 +020085 rev_links[l['peer']['fqn']] = [rlink]
Sapan Bhatia1e397df2017-05-24 12:17:28 +020086
87 for m in messages:
88 try:
89 m['rlinks'] = rev_links[m['name']]
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +020090 message_dict[m['name']]['rlinks'] = m['rlinks']
Sapan Bhatia1e397df2017-05-24 12:17:28 +020091 except KeyError:
92 pass
93
Matteo Scandolo67654fa2017-06-09 09:33:17 -070094
Sapan Bhatia3cfdf632017-06-08 05:14:03 +020095def name_to_value(obj):
96 try:
97 value = obj.value.value.pval
98 except AttributeError:
99 try:
100 value = obj.value.value
101 except AttributeError:
102 value = obj.value.pval
103
104 return value
105
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700106
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700107class Stack(list):
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700108 def push(self, x):
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700109 self.append(x)
110
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700111
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700112''' XOS2Jinja overrides the underlying visitor pattern to transform the tree
113 in addition to traversing it '''
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700114
Sapan Bhatiadb183c22017-06-23 02:47:42 -0700115class XOS2Jinja(Visitor):
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700116 def __init__(self):
117 super(XOS2Jinja, self).__init__()
118
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700119 self.stack = Stack()
120 self.models = {}
121 self.options = {}
122 self.package = None
123 self.message_options = {}
124 self.count_stack = Stack()
Sapan Bhatiadb183c22017-06-23 02:47:42 -0700125 self.policies = {}
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700126 self.content = ""
127 self.offset = 0
128 self.current_message_name = None
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700129 self.verbose = 0
130 self.first_field = True
131 self.first_method = True
132
Sapan Bhatiadb183c22017-06-23 02:47:42 -0700133 def visit_PolicyDefinition(self, obj):
134 if self.package:
135 pname = '.'.join([self.package, obj.name.value.pval])
136 else:
137 pname = obj.name.value.pval
138
139 self.policies[pname] = obj.body
Sapan Bhatiad3fcb662017-07-25 21:13:48 -0400140 find_missing_policy_calls(pname, self.policies, obj.body)
Sapan Bhatia3e3c1cd2017-07-15 01:35:44 -0400141
142 return True
143
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700144 def visit_PackageStatement(self, obj):
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200145 dotlist = obj.name.value
146 dotlist2 = [f.pval for f in dotlist]
147 dotstr = '.'.join(dotlist2)
148 self.package = dotstr
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700149 return True
150
151 def visit_ImportStatement(self, obj):
152 '''Ignore'''
153 return True
154
155 def visit_OptionStatement(self, obj):
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700156 if not hasattr(obj, 'mark_for_deletion'):
Sapan Bhatiaae9645c2017-05-05 15:35:54 +0200157 if (self.current_message_name):
158 self.message_options[obj.name.value.pval] = obj.value.value.pval
159 else:
160 self.options[obj.name.value.pval] = obj.value.value.pval
Sapan Bhatia504cc972017-04-27 01:56:28 +0200161
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700162 return True
163
164 def visit_LU(self, obj):
165 return True
166
167 def visit_default(self, obj):
168 return True
169
170 def visit_FieldDirective(self, obj):
171 return True
172
173 def visit_FieldDirective_post(self, obj):
174
175 try:
176 name = obj.name.value.pval
177 except AttributeError:
178 name = obj.name.value
179
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700180 if type(obj.value) == list:
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200181 value = dotname_to_name(obj.value)
182 else:
183 value = name_to_value(obj)
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700184
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700185 self.stack.push([name, value])
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700186 return True
187
188 def visit_FieldType(self, obj):
189 '''Field type, if type is name, then it may need refactoring consistent with refactoring rules according to the table'''
190 return True
191
192 def visit_LinkDefinition(self, obj):
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700193 s = {}
Sapan Bhatia8a57c662017-04-11 10:39:47 -0700194
Sapan Bhatiaae9645c2017-05-05 15:35:54 +0200195 try:
196 s['link_type'] = obj.link_type.pval
197 except AttributeError:
198 s['link_type'] = obj.link_type
199
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700200 s['src_port'] = obj.src_port.value.pval
201 s['name'] = obj.src_port.value.pval
Sapan Bhatiadb183c22017-06-23 02:47:42 -0700202 try:
203 s['policy'] = obj.policy.pval
204 except AttributeError:
205 s['policy'] = None
Sapan Bhatiac4f803f2017-04-21 11:50:39 +0200206
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700207 try:
208 s['dst_port'] = obj.dst_port.value.pval
209 except AttributeError:
Sapan Bhatiaae9645c2017-05-05 15:35:54 +0200210 s['dst_port'] = obj.dst_port
Sapan Bhatiac4f803f2017-04-21 11:50:39 +0200211
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700212 if type(obj.through) == list:
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200213 s['through'] = dotname_to_fqn(obj.through)
214 else:
215 try:
216 s['through'] = obj.through.pval
217 except AttributeError:
218 s['through'] = obj.through
Sapan Bhatiac4f803f2017-04-21 11:50:39 +0200219
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700220 if type(obj.name) == list:
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200221 s['peer'] = dotname_to_fqn(obj.name)
222 else:
223 try:
224 s['peer'] = obj.name.pval
225 except AttributeError:
226 s['peer'] = obj.name
Sapan Bhatiaae9645c2017-05-05 15:35:54 +0200227
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700228 s['_type'] = 'link'
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700229 s['options'] = {'modifier': 'optional'}
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700230
231 self.stack.push(s)
232 return True
233
234 def visit_FieldDefinition(self, obj):
235 self.count_stack.push(len(obj.fieldDirective))
236 return True
237
238 def visit_FieldDefinition_post(self, obj):
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700239 s = {}
240
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700241 if isinstance(obj.ftype, m.Name):
242 s['type'] = obj.ftype.value
243 else:
244 s['type'] = obj.ftype.name.pval
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200245
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700246 s['name'] = obj.name.value.pval
Sapan Bhatiadb183c22017-06-23 02:47:42 -0700247
248 try:
249 s['policy'] = obj.policy.pval
250 except AttributeError:
251 s['policy'] = None
252
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700253 s['modifier'] = obj.field_modifier.pval
254 s['id'] = obj.fieldId.pval
255
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700256 opts = {'modifier': s['modifier']}
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700257 n = self.count_stack.pop()
258 for i in range(0, n):
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700259 k, v = self.stack.pop()
Sapan Bhatia5769d932017-05-17 11:10:54 +0200260
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700261 # The two lines below may be added to eliminate "" around an option.
Sapan Bhatia5769d932017-05-17 11:10:54 +0200262 # Right now, this is handled in targets. FIXME
263 #
264 # if (v.startswith('"') and v.endswith('"')):
265 # v = v[1:-1]
266
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700267 opts[k] = v
268
269 s['options'] = opts
Sapan Bhatia8a57c662017-04-11 10:39:47 -0700270 try:
271 last_link = self.stack[-1]['_type']
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700272 if (last_link == 'link'):
Sapan Bhatia8a57c662017-04-11 10:39:47 -0700273 s['link'] = True
274 except:
275 pass
276 s['_type'] = 'field'
277
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700278 self.stack.push(s)
279 return True
280
281 def visit_EnumFieldDefinition(self, obj):
282 if self.verbose > 4:
283 print "\tEnumField: name=%s, %s" % (obj.name, obj)
284
285 return True
286
287 def visit_EnumDefinition(self, obj):
288 '''New enum definition, refactor name'''
289 if self.verbose > 3:
290 print "Enum, [%s] body=%s\n\n" % (obj.name, obj.body)
291
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700292 return True
293
294 def visit_MessageDefinition(self, obj):
Sapan Bhatia504cc972017-04-27 01:56:28 +0200295 self.current_message_name = obj.name.value.pval
296 self.message_options = {}
297 self.count_stack.push(count_fields(obj.body))
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700298 return True
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700299
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700300 def visit_MessageDefinition_post(self, obj):
301 stack_num = self.count_stack.pop()
302 fields = []
303 links = []
304 last_field = None
Sapan Bhatiaae9645c2017-05-05 15:35:54 +0200305 try:
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200306 obj.bases = map(dotname_to_fqn, obj.bases)
Sapan Bhatiaae9645c2017-05-05 15:35:54 +0200307 except AttributeError:
308 pass
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700309
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200310 last_field = {}
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700311 for i in range(0, stack_num):
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700312 f = self.stack.pop()
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700313 if (f['_type'] == 'link'):
314 f['options'] = {i: d[i] for d in [f['options'], last_field['options']] for i in d}
315 assert (last_field == fields[0])
Sapan Bhatiad022aeb2017-06-07 15:49:55 +0200316 fields[0].setdefault('options', {})['link_type'] = f['link_type']
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700317 links.insert(0, f)
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700318 else:
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700319 fields.insert(0, f)
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700320 last_field = f
321
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200322 if self.package:
323 model_name = '.'.join([self.package, obj.name.value.pval])
324 else:
325 model_name = obj.name.value.pval
326
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200327 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 -0700328 try:
329 model_def['policy'] = obj.policy.pval
330 except AttributeError:
331 model_def['policy'] = None
332
Sapan Bhatia943dad52017-05-19 18:41:01 +0200333 self.stack.push(model_def)
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200334
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200335 self.models[model_name] = model_def
336
337 # Set message options
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700338 for k, v in self.options.iteritems():
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200339 try:
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200340 if k not in self.message_options:
341 self.message_options[k] = v
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200342 except KeyError:
343 pass
344
Sapan Bhatia504cc972017-04-27 01:56:28 +0200345 self.current_message_name = None
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700346 return True
347
348 def visit_MessageExtension(self, obj):
349 return True
350
351 def visit_MethodDefinition(self, obj):
352 return True
353
354 def visit_ServiceDefinition(self, obj):
355 return True
356
357 def visit_ExtensionsDirective(self, obj):
358 return True
359
360 def visit_Literal(self, obj):
361 return True
362
363 def visit_Name(self, obj):
364 return True
365
366 def visit_DotName(self, obj):
367 return True
368
369 def visit_Proto(self, obj):
Sapan Bhatia504cc972017-04-27 01:56:28 +0200370 self.count_stack.push(count_messages(obj.body))
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700371 return True
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700372
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700373 def visit_Proto_post(self, obj):
374 count = self.count_stack.pop()
375 messages = []
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700376 for i in range(0, count):
Sapan Bhatiac4f803f2017-04-21 11:50:39 +0200377 try:
378 m = self.stack.pop()
379 except IndexError:
Sapan Bhatia504cc972017-04-27 01:56:28 +0200380 pass
Sapan Bhatiac4f803f2017-04-21 11:50:39 +0200381
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700382 messages.insert(0, m)
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700383
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200384 compute_rlinks(messages, self.models)
385
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -0700386 self.messages = messages
387 return True
388
389 def visit_LinkSpec(self, obj):
390 count = self.count_stack.pop()
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700391 self.count_stack.push(count + 1)
Sapan Bhatiadb183c22017-06-23 02:47:42 -0700392 return True