blob: 9abca52da22890fcbf22474b98e65281f836089c [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",
xinwuf08ef682013-12-05 18:29:20 -0800128 "of_flow_stats_entry", "of_bucket", "of_table_features",
129 "of_bsn_port_counter_stats_entry", "of_bsn_vlan_counter_stats_entry"]
Andreas Wundsam542a13c2013-11-15 13:28:55 -0800130
131def class_is_oxm(cls):
132 """
133 Return True if cls_name is an OXM object
134 """
135 if cls.find("of_oxm") == 0:
136 return True
137 return False
138
139def class_is_action(cls):
140 """
141 Return True if cls_name is an action object
142
143 Note that action_id is not an action object, though it has
144 the same header. It looks like an action header, but the type
145 is used to identify a kind of action, it does not indicate the
146 type of the object following.
147 """
148 if cls.find("of_action_id") == 0:
149 return False
150 if cls.find("of_action") == 0:
151 return True
152
153 # For each vendor, check for vendor specific action
154 for exp in of_g.experimenter_name_to_id:
155 if cls.find("of_action" + exp) == 0:
156 return True
157
158 return False
159
160def class_is_action_id(cls):
161 """
162 Return True if cls_name is an action object
163
164 Note that action_id is not an action object, though it has
165 the same header. It looks like an action header, but the type
166 is used to identify a kind of action, it does not indicate the
167 type of the object following.
168 """
169 if cls.find("of_action_id") == 0:
170 return True
171
172 # For each vendor, check for vendor specific action
173 for exp in of_g.experimenter_name_to_id:
174 if cls.find("of_action_id_" + exp) == 0:
175 return True
176
177 return False
178
179def class_is_instruction(cls):
180 """
181 Return True if cls_name is an instruction object
182 """
Rich Lane4def6972013-12-09 17:44:43 -0800183 if cls.find("of_instruction_id") == 0:
184 return False
Andreas Wundsam542a13c2013-11-15 13:28:55 -0800185 if cls.find("of_instruction") == 0:
186 return True
187
188 # For each vendor, check for vendor specific action
189 for exp in of_g.experimenter_name_to_id:
190 if cls.find("of_instruction_" + exp) == 0:
191 return True
192
193 return False
194
195def class_is_meter_band(cls):
196 """
197 Return True if cls_name is an instruction object
198 """
199 # meter_band_stats is not a member of meter_band class hierarchy
200 if cls.find("of_meter_band_stats") == 0:
201 return False
202 if cls.find("of_meter_band") == 0:
203 return True
204 return False
205
206def class_is_hello_elem(cls):
207 """
208 Return True if cls_name is an instruction object
209 """
210 if cls.find("of_hello_elem") == 0:
211 return True
212 return False
213
214def class_is_queue_prop(cls):
215 """
216 Return True if cls_name is a queue_prop object
217 """
218 if cls.find("of_queue_prop") == 0:
219 return True
220
221 # For each vendor, check for vendor specific action
222 for exp in of_g.experimenter_name_to_id:
223 if cls.find("of_queue_prop_" + exp) == 0:
224 return True
225
226 return False
227
228def class_is_table_feature_prop(cls):
229 """
230 Return True if cls_name is a queue_prop object
231 """
232 if cls.find("of_table_feature_prop") == 0:
233 return True
234 return False
235
236def class_is_stats_message(cls):
237 """
238 Return True if cls_name is a message object based on info in unified
239 """
240
241 return "stats_type" in of_g.unified[cls]["union"]
242
243def class_is_list(cls):
244 """
245 Return True if cls_name is a list object
246 """
247 return (cls.find("of_list_") == 0)
248
249def type_is_of_object(m_type):
250 """
251 Return True if m_type is an OF object type
252 """
253 # Remove _t from the type id and see if key for unified class
254 if m_type[-2:] == "_t":
255 m_type = m_type[:-2]
256 return m_type in of_g.unified
257
258def list_to_entry_type(cls):
259 """
260 Return the entry type for a list
261 """
262 slen = len("of_list_")
263 return "of_" + cls[slen:]
264
265def type_to_short_name(m_type):
266 if m_type in of_g.of_base_types:
267 tname = of_g.of_base_types[m_type]["short_name"]
268 elif m_type in of_g.of_mixed_types:
269 tname = of_g.of_mixed_types[m_type]["short_name"]
270 else:
271 tname = "unknown"
272 return tname
273
274def type_to_name_type(cls, member_name):
275 """
276 Generate the root name of a member for accessor functions, etc
277 @param cls The class name
278 @param member_name The member name
279 """
280 members = of_g.unified[cls]["union"]
281 if not member_name in members:
282 debug("Error: %s is not in class %s for acc_name defn" %
283 (member_name, cls))
284 os.exit()
285
286 mem = members[member_name]
287 m_type = mem["m_type"]
288 id = mem["memid"]
289 tname = type_to_short_name(m_type)
290
291 return "o%d_m%d_%s" % (of_g.unified[cls]["object_id"], id, tname)
292
293
294def member_to_index(m_name, members):
295 """
296 Given a member name, return the index in the members dict
297 @param m_name The name of the data member to search for
298 @param members The dict of members
299 @return Index if found, -1 not found
300
301 Note we could generate an index when processing the original input
302 """
303 count = 0
304 for d in members:
305 if d["name"] == m_name:
306 return count
307 count += 1
308 return -1
309
310def member_base_type(cls, m_name):
311 """
312 Map a member to its of_ type
313 @param cls The class name
314 @param m_name The name of the member being gotten
315 @return The of_ type of the member
316 """
317 rv = of_g.unified[cls]["union"][m_name]["m_type"]
318 if rv[-2:] == "_t":
319 return rv
320 return rv + "_t"
321
322def member_type_is_octets(cls, m_name):
323 return member_base_type(cls, m_name) == "of_octets_t"
324
325def member_returns_val(cls, m_name):
326 """
327 Should get accessor return a value rather than void
328 @param cls The class name
329 @param m_name The member name
330 @return True if of_g config and the specific member allow a
331 return value. Otherwise False
332 """
333 m_type = of_g.unified[cls]["union"][m_name]["m_type"]
334 return (config_check("get_returns") =="value" and
335 m_type in of_g.of_scalar_types)
336
337def config_check(str, dictionary = of_g.code_gen_config):
338 """
339 Return config value if in dictionary; else return False.
340 @param str The lookup index
341 @param dictionary The dict to check; use code_gen_config if None
342 """
343
344 if str in dictionary:
345 return dictionary[str]
346
347 return False
348
349def h_file_to_define(name):
350 """
351 Convert a .h file name to the define used for the header
352 """
353 h_name = name[:-2].upper()
354 h_name = "_" + h_name + "_H_"
355 return h_name
356
357def type_to_cof_type(m_type):
358 if m_type in of_g.of_base_types:
359 if "cof_type" in of_g.of_base_types[m_type]:
360 return of_g.of_base_types[m_type]["cof_type"]
361 return m_type
362
363
364def member_is_scalar(cls, m_name):
365 return of_g.unified[cls]["union"][m_name]["m_type"] in of_g.of_scalar_types
366
367def type_is_scalar(m_type):
368 return m_type in of_g.of_scalar_types
369
370def skip_member_name(name):
371 return name.find("pad") == 0 or name in of_g.skip_members
372
373def enum_name(cls):
374 """
375 Return the name used for an enum identifier for the given class
376 @param cls The class name
377 """
378 return cls.upper()
379
380def class_in_version(cls, ver):
381 """
382 Return boolean indicating if cls is defined for wire version ver
383 """
384
385 return (cls, ver) in of_g.base_length
386
387def instance_to_class(instance, parent):
388 """
389 Return the name of the class for an instance of inheritance type parent
390 """
391 return parent + "_" + instance
392
393def sub_class_to_var_name(cls):
394 """
395 Given a subclass name like of_action_output, generate the
396 name of a variable like 'output'
397 @param cls The class name
398 """
399 pass
400
401def class_is_var_len(cls, version):
402 # Match is special case. Only version 1.2 (wire version 3) is var
403 if cls == "of_match":
404 return version == 3
405
406 return not (cls, version) in of_g.is_fixed_length
407
408def base_type_to_length(base_type, version):
409 if base_type + "_t" in of_g.of_base_types:
410 inst_len = of_g.of_base_types[base_type + "_t"]["bytes"]
411 else:
412 inst_len = of_g.base_length[(base_type, version)]
413
414def version_to_name(version):
415 """
416 Convert an integer version to the C macro name
417 """
418 return "OF_" + of_g.version_names[version]
419
420##
421# Is class a flow modify of some sort?
422
423def cls_is_flow_mod(cls):
424 return cls in ["of_flow_mod", "of_flow_modify", "of_flow_add", "of_flow_delete",
425 "of_flow_modify_strict", "of_flow_delete_strict"]
426
427
428def all_member_types_get(cls, version):
429 """
430 Get the members and list of types for members of a given class
431 @param cls The class name to process
432 @param version The version for the class
433 """
434 member_types = []
435
436 if not version in of_g.unified[cls]:
437 return ([], [])
438
439 if "use_version" in of_g.unified[cls][version]:
440 v = of_g.unified[cls][version]["use_version"]
441 members = of_g.unified[cls][v]["members"]
442 else:
443 members = of_g.unified[cls][version]["members"]
444 # Accumulate variables that are supported
445 for member in members:
446 m_type = member["m_type"]
447 m_name = member["name"]
448 if skip_member_name(m_name):
449 continue
450 if not m_type in member_types:
451 member_types.append(m_type)
452
453 return (members, member_types)
454
455def list_name_extract(list_type):
456 """
457 Return the base name for a list object of the given type
458 @param list_type The type of the list as appears in the input,
459 for example list(of_port_desc_t).
460 @return A pair, (list-name, base-type) where list-name is the
461 base name for the list, for example of_list_port_desc, and base-type
462 is the type of list elements like of_port_desc_t
463 """
464 base_type = list_type[5:-1]
465 list_name = base_type
466 if list_name.find("of_") == 0:
467 list_name = list_name[3:]
468 if list_name[-2:] == "_t":
469 list_name = list_name[:-2]
470 list_name = "of_list_" + list_name
471 return (list_name, base_type)
472
473def version_to_name(version):
474 """
475 Convert an integer version to the C macro name
476 """
477 return "OF_" + of_g.version_names[version]
478
479def gen_c_copy_license(out):
480 """
481 Generate the top comments for copyright and license
482 """
483 import c_gen.util
484 c_gen.util.render_template(out, '_copyright.c')
485
486def accessor_returns_error(a_type, m_type):
487 is_var_len = (not type_is_scalar(m_type)) and \
488 [x for x in of_g.of_version_range if class_is_var_len(m_type[:-2], x)] != []
489 if a_type == "set" and is_var_len:
490 return True
491 elif m_type == "of_match_t":
492 return True
493 else:
494 return False
495
496def render_template(out, name, path, context, prefix = None):
497 """
498 Render a template using tenjin.
499 out: a file-like object
500 name: name of the template
501 path: array of directories to search for the template
502 context: dictionary of variables to pass to the template
503 prefix: optional prefix to use for embedding (for other languages than python)
504 """
505 pp = [ tenjin.PrefixedLinePreprocessor(prefix=prefix) if prefix else tenjin.PrefixedLinePreprocessor() ] # support "::" syntax
506 template_globals = { "to_str": str, "escape": str } # disable HTML escaping
507 engine = TemplateEngine(path=path, pp=pp)
508 out.write(engine.render(name, context, template_globals))
509
510def render_static(out, name, path):
511 """
512 Write out a static template.
513 out: a file-like object
514 name: name of the template
515 path: array of directories to search for the template
516 """
517 # Reuse the tenjin logic for finding the template
518 template_filename = tenjin.FileSystemLoader().find(name, path)
519 if not template_filename:
520 raise ValueError("template %s not found" % name)
521 with open(template_filename) as infile:
522 out.write(infile.read())