blob: 47b143056b92c8bcc678be73a41cad09a93974cb [file] [log] [blame]
Andreas Wundsam542a13c2013-11-15 13:28:55 -08001# Copyright 2013, Big Switch Networks, Inc.
2#
3# LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
4# the following special exception:
5#
6# LOXI Exception
7#
8# As a special exception to the terms of the EPL, you may distribute libraries
9# generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
10# that copyright and licensing notices generated by LoxiGen are not altered or removed
11# from the LoxiGen Libraries and the notice provided below is (i) included in
12# the LoxiGen Libraries, if distributed in source code form and (ii) included in any
13# documentation for the LoxiGen Libraries, if distributed in binary form.
14#
15# Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
16#
17# You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
18# a copy of the EPL at:
19#
20# http://www.eclipse.org/legal/epl-v10.html
21#
22# Unless required by applicable law or agreed to in writing, software
23# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
24# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
25# EPL for the specific language governing permissions and limitations
26# under the EPL.
27
28"""
29@brief Utilities involving LOXI naming conventions
30
31Utility functions for OpenFlow class generation
32
33These may need to be sorted out into language specific functions
34"""
35
36import sys
37import c_gen.of_g_legacy as of_g
38import tenjin
39from generic_utils import find, memoize
40
41def class_signature(members):
42 """
43 Generate a signature string for a class in canonical form
44
45 @param cls The class whose signature is to be generated
46 """
47 return ";".join([",".join([x["m_type"], x["name"], str(x["offset"])])
48 for x in members])
49
50def type_dec_to_count_base(m_type):
51 """
52 Resolve a type declaration like uint8_t[4] to a count (4) and base_type
53 (uint8_t)
54
55 @param m_type The string type declaration to process
56 """
57 count = 1
58 chk_ar = m_type.split('[')
59 if len(chk_ar) > 1:
60 count_str = chk_ar[1].split(']')[0]
61 if count_str in of_g.ofp_constants:
62 count = of_g.ofp_constants[count_str]
63 else:
64 count = int(count_str)
65 base_type = chk_ar[0]
66 else:
67 base_type = m_type
68 return count, base_type
69
70##
71# Class types:
72#
73# Virtual
74# A virtual class is one which does not have an explicit wire
75# representation. For example, an inheritance super class
76# or a list type.
77#
78# List
79# A list of objects of some other type
80#
81# TLV16
82# The wire represenation starts with 16-bit type and length fields
83#
84# OXM
85# An extensible match object
86#
87# Message
88# A top level OpenFlow message
89#
90#
91
92def class_is_message(cls):
93 """
94 Return True if cls is a message object based on info in unified
95 """
96 return "xid" in of_g.unified[cls]["union"] and cls != "of_header"
97
98def class_is_tlv16(cls):
99 """
100 Return True if cls_name is an object which uses uint16 for type and length
101 """
102 if cls.find("of_action") == 0: # Includes of_action_id classes
103 return True
104 if cls.find("of_instruction") == 0:
105 return True
106 if cls.find("of_queue_prop") == 0:
107 return True
108 if cls.find("of_table_feature_prop") == 0:
109 return True
110 # *sigh*
111 if cls.find("of_meter_band_stats") == 0: # NOT A TLV
112 return False
113 if cls.find("of_meter_band") == 0:
114 return True
115 if cls.find("of_hello_elem") == 0:
116 return True
117 if cls == "of_match_v3":
118 return True
119 if cls == "of_match_v4":
120 return True
121 return False
122
123def class_is_u16_len(cls):
124 """
125 Return True if cls_name is an object which uses initial uint16 length
126 """
127 return cls in ["of_group_desc_stats_entry", "of_group_stats_entry",
128 "of_flow_stats_entry", "of_bucket", "of_table_features"]
129
130def class_is_oxm(cls):
131 """
132 Return True if cls_name is an OXM object
133 """
134 if cls.find("of_oxm") == 0:
135 return True
136 return False
137
138def class_is_action(cls):
139 """
140 Return True if cls_name is an action object
141
142 Note that action_id is not an action object, though it has
143 the same header. It looks like an action header, but the type
144 is used to identify a kind of action, it does not indicate the
145 type of the object following.
146 """
147 if cls.find("of_action_id") == 0:
148 return False
149 if cls.find("of_action") == 0:
150 return True
151
152 # For each vendor, check for vendor specific action
153 for exp in of_g.experimenter_name_to_id:
154 if cls.find("of_action" + exp) == 0:
155 return True
156
157 return False
158
159def class_is_action_id(cls):
160 """
161 Return True if cls_name is an action object
162
163 Note that action_id is not an action object, though it has
164 the same header. It looks like an action header, but the type
165 is used to identify a kind of action, it does not indicate the
166 type of the object following.
167 """
168 if cls.find("of_action_id") == 0:
169 return True
170
171 # For each vendor, check for vendor specific action
172 for exp in of_g.experimenter_name_to_id:
173 if cls.find("of_action_id_" + exp) == 0:
174 return True
175
176 return False
177
178def class_is_instruction(cls):
179 """
180 Return True if cls_name is an instruction object
181 """
182 if cls.find("of_instruction") == 0:
183 return True
184
185 # For each vendor, check for vendor specific action
186 for exp in of_g.experimenter_name_to_id:
187 if cls.find("of_instruction_" + exp) == 0:
188 return True
189
190 return False
191
192def class_is_meter_band(cls):
193 """
194 Return True if cls_name is an instruction object
195 """
196 # meter_band_stats is not a member of meter_band class hierarchy
197 if cls.find("of_meter_band_stats") == 0:
198 return False
199 if cls.find("of_meter_band") == 0:
200 return True
201 return False
202
203def class_is_hello_elem(cls):
204 """
205 Return True if cls_name is an instruction object
206 """
207 if cls.find("of_hello_elem") == 0:
208 return True
209 return False
210
211def class_is_queue_prop(cls):
212 """
213 Return True if cls_name is a queue_prop object
214 """
215 if cls.find("of_queue_prop") == 0:
216 return True
217
218 # For each vendor, check for vendor specific action
219 for exp in of_g.experimenter_name_to_id:
220 if cls.find("of_queue_prop_" + exp) == 0:
221 return True
222
223 return False
224
225def class_is_table_feature_prop(cls):
226 """
227 Return True if cls_name is a queue_prop object
228 """
229 if cls.find("of_table_feature_prop") == 0:
230 return True
231 return False
232
233def class_is_stats_message(cls):
234 """
235 Return True if cls_name is a message object based on info in unified
236 """
237
238 return "stats_type" in of_g.unified[cls]["union"]
239
240def class_is_list(cls):
241 """
242 Return True if cls_name is a list object
243 """
244 return (cls.find("of_list_") == 0)
245
246def type_is_of_object(m_type):
247 """
248 Return True if m_type is an OF object type
249 """
250 # Remove _t from the type id and see if key for unified class
251 if m_type[-2:] == "_t":
252 m_type = m_type[:-2]
253 return m_type in of_g.unified
254
255def list_to_entry_type(cls):
256 """
257 Return the entry type for a list
258 """
259 slen = len("of_list_")
260 return "of_" + cls[slen:]
261
262def type_to_short_name(m_type):
263 if m_type in of_g.of_base_types:
264 tname = of_g.of_base_types[m_type]["short_name"]
265 elif m_type in of_g.of_mixed_types:
266 tname = of_g.of_mixed_types[m_type]["short_name"]
267 else:
268 tname = "unknown"
269 return tname
270
271def type_to_name_type(cls, member_name):
272 """
273 Generate the root name of a member for accessor functions, etc
274 @param cls The class name
275 @param member_name The member name
276 """
277 members = of_g.unified[cls]["union"]
278 if not member_name in members:
279 debug("Error: %s is not in class %s for acc_name defn" %
280 (member_name, cls))
281 os.exit()
282
283 mem = members[member_name]
284 m_type = mem["m_type"]
285 id = mem["memid"]
286 tname = type_to_short_name(m_type)
287
288 return "o%d_m%d_%s" % (of_g.unified[cls]["object_id"], id, tname)
289
290
291def member_to_index(m_name, members):
292 """
293 Given a member name, return the index in the members dict
294 @param m_name The name of the data member to search for
295 @param members The dict of members
296 @return Index if found, -1 not found
297
298 Note we could generate an index when processing the original input
299 """
300 count = 0
301 for d in members:
302 if d["name"] == m_name:
303 return count
304 count += 1
305 return -1
306
307def member_base_type(cls, m_name):
308 """
309 Map a member to its of_ type
310 @param cls The class name
311 @param m_name The name of the member being gotten
312 @return The of_ type of the member
313 """
314 rv = of_g.unified[cls]["union"][m_name]["m_type"]
315 if rv[-2:] == "_t":
316 return rv
317 return rv + "_t"
318
319def member_type_is_octets(cls, m_name):
320 return member_base_type(cls, m_name) == "of_octets_t"
321
322def member_returns_val(cls, m_name):
323 """
324 Should get accessor return a value rather than void
325 @param cls The class name
326 @param m_name The member name
327 @return True if of_g config and the specific member allow a
328 return value. Otherwise False
329 """
330 m_type = of_g.unified[cls]["union"][m_name]["m_type"]
331 return (config_check("get_returns") =="value" and
332 m_type in of_g.of_scalar_types)
333
334def config_check(str, dictionary = of_g.code_gen_config):
335 """
336 Return config value if in dictionary; else return False.
337 @param str The lookup index
338 @param dictionary The dict to check; use code_gen_config if None
339 """
340
341 if str in dictionary:
342 return dictionary[str]
343
344 return False
345
346def h_file_to_define(name):
347 """
348 Convert a .h file name to the define used for the header
349 """
350 h_name = name[:-2].upper()
351 h_name = "_" + h_name + "_H_"
352 return h_name
353
354def type_to_cof_type(m_type):
355 if m_type in of_g.of_base_types:
356 if "cof_type" in of_g.of_base_types[m_type]:
357 return of_g.of_base_types[m_type]["cof_type"]
358 return m_type
359
360
361def member_is_scalar(cls, m_name):
362 return of_g.unified[cls]["union"][m_name]["m_type"] in of_g.of_scalar_types
363
364def type_is_scalar(m_type):
365 return m_type in of_g.of_scalar_types
366
367def skip_member_name(name):
368 return name.find("pad") == 0 or name in of_g.skip_members
369
370def enum_name(cls):
371 """
372 Return the name used for an enum identifier for the given class
373 @param cls The class name
374 """
375 return cls.upper()
376
377def class_in_version(cls, ver):
378 """
379 Return boolean indicating if cls is defined for wire version ver
380 """
381
382 return (cls, ver) in of_g.base_length
383
384def instance_to_class(instance, parent):
385 """
386 Return the name of the class for an instance of inheritance type parent
387 """
388 return parent + "_" + instance
389
390def sub_class_to_var_name(cls):
391 """
392 Given a subclass name like of_action_output, generate the
393 name of a variable like 'output'
394 @param cls The class name
395 """
396 pass
397
398def class_is_var_len(cls, version):
399 # Match is special case. Only version 1.2 (wire version 3) is var
400 if cls == "of_match":
401 return version == 3
402
403 return not (cls, version) in of_g.is_fixed_length
404
405def base_type_to_length(base_type, version):
406 if base_type + "_t" in of_g.of_base_types:
407 inst_len = of_g.of_base_types[base_type + "_t"]["bytes"]
408 else:
409 inst_len = of_g.base_length[(base_type, version)]
410
411def version_to_name(version):
412 """
413 Convert an integer version to the C macro name
414 """
415 return "OF_" + of_g.version_names[version]
416
417##
418# Is class a flow modify of some sort?
419
420def cls_is_flow_mod(cls):
421 return cls in ["of_flow_mod", "of_flow_modify", "of_flow_add", "of_flow_delete",
422 "of_flow_modify_strict", "of_flow_delete_strict"]
423
424
425def all_member_types_get(cls, version):
426 """
427 Get the members and list of types for members of a given class
428 @param cls The class name to process
429 @param version The version for the class
430 """
431 member_types = []
432
433 if not version in of_g.unified[cls]:
434 return ([], [])
435
436 if "use_version" in of_g.unified[cls][version]:
437 v = of_g.unified[cls][version]["use_version"]
438 members = of_g.unified[cls][v]["members"]
439 else:
440 members = of_g.unified[cls][version]["members"]
441 # Accumulate variables that are supported
442 for member in members:
443 m_type = member["m_type"]
444 m_name = member["name"]
445 if skip_member_name(m_name):
446 continue
447 if not m_type in member_types:
448 member_types.append(m_type)
449
450 return (members, member_types)
451
452def list_name_extract(list_type):
453 """
454 Return the base name for a list object of the given type
455 @param list_type The type of the list as appears in the input,
456 for example list(of_port_desc_t).
457 @return A pair, (list-name, base-type) where list-name is the
458 base name for the list, for example of_list_port_desc, and base-type
459 is the type of list elements like of_port_desc_t
460 """
461 base_type = list_type[5:-1]
462 list_name = base_type
463 if list_name.find("of_") == 0:
464 list_name = list_name[3:]
465 if list_name[-2:] == "_t":
466 list_name = list_name[:-2]
467 list_name = "of_list_" + list_name
468 return (list_name, base_type)
469
470def version_to_name(version):
471 """
472 Convert an integer version to the C macro name
473 """
474 return "OF_" + of_g.version_names[version]
475
476def gen_c_copy_license(out):
477 """
478 Generate the top comments for copyright and license
479 """
480 import c_gen.util
481 c_gen.util.render_template(out, '_copyright.c')
482
483def accessor_returns_error(a_type, m_type):
484 is_var_len = (not type_is_scalar(m_type)) and \
485 [x for x in of_g.of_version_range if class_is_var_len(m_type[:-2], x)] != []
486 if a_type == "set" and is_var_len:
487 return True
488 elif m_type == "of_match_t":
489 return True
490 else:
491 return False
492
493def render_template(out, name, path, context, prefix = None):
494 """
495 Render a template using tenjin.
496 out: a file-like object
497 name: name of the template
498 path: array of directories to search for the template
499 context: dictionary of variables to pass to the template
500 prefix: optional prefix to use for embedding (for other languages than python)
501 """
502 pp = [ tenjin.PrefixedLinePreprocessor(prefix=prefix) if prefix else tenjin.PrefixedLinePreprocessor() ] # support "::" syntax
503 template_globals = { "to_str": str, "escape": str } # disable HTML escaping
504 engine = TemplateEngine(path=path, pp=pp)
505 out.write(engine.render(name, context, template_globals))
506
507def render_static(out, name, path):
508 """
509 Write out a static template.
510 out: a file-like object
511 name: name of the template
512 path: array of directories to search for the template
513 """
514 # Reuse the tenjin logic for finding the template
515 template_filename = tenjin.FileSystemLoader().find(name, path)
516 if not template_filename:
517 raise ValueError("template %s not found" % name)
518 with open(template_filename) as infile:
519 out.write(infile.read())
520
521@memoize
522def lookup_ir_wiretype(oftype, version):
523 """ if of is a reference to an enum in ir, resolve it to the wiretype
524 declared in that enum. Else return oftype """
525 enums = of_g.ir[version].enums
526 enum = find(lambda e: e.name == oftype, enums)
527 if enum and 'wire_type' in enum.params:
528 return enum.params['wire_type']
529 else:
530 return oftype
531
532class TemplateEngine(tenjin.Engine):
533 def include(self, template_name, **kwargs):
534 """
535 Tenjin has an issue with nested includes that use the same local variable
536 names, because it uses the same context dict for each level of nesting.
537 The fix is to copy the context.
538 """
539 frame = sys._getframe(1)
540 locals = frame.f_locals
541 globals = frame.f_globals
542 context = locals["_context"].copy()
543 context.update(kwargs)
544 template = self.get_template(template_name, context, globals)
545 return template.render(context, globals, _buf=locals["_buf"])