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