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