Newer
Older
# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Build some context around a given P4 info file.
"""
from collections import Counter
import enum
from functools import partialmethod
@enum.unique
class P4Type(enum.Enum):
"""
P4 types.
"""
table = 1
action = 2
action_profile = 3
counter = 4
direct_counter = 5
meter = 6
direct_meter = 7
controller_packet_metadata = 8
P4Type.table.p4info_name = "tables"
P4Type.action.p4info_name = "actions"
P4Type.action_profile.p4info_name = "action_profiles"
P4Type.counter.p4info_name = "counters"
P4Type.direct_counter.p4info_name = "direct_counters"
P4Type.meter.p4info_name = "meters"
P4Type.direct_meter.p4info_name = "direct_meters"
P4Type.controller_packet_metadata.p4info_name = "controller_packet_metadata"
P4Type.digest.p4info_name = "digests"
for object_type in P4Type:
object_type.pretty_name = object_type.name.replace('_', ' ')
object_type.pretty_names = object_type.pretty_name + 's'
@enum.unique
class P4RuntimeEntity(enum.Enum):
"""
P4 runtime entities.
"""
table_entry = 1
action_profile_member = 2
action_profile_group = 3
counter_entry = 4
direct_counter_entry = 5
meter_entry = 6
direct_meter_entry = 7
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
class Context:
"""
P4 context.
"""
def __init__(self):
self.p4info = None
self.p4info_obj_map = {}
self.p4info_obj_map_by_id = {}
self.p4info_objs_by_type = {}
def set_p4info(self, p4info):
"""
Set a p4 info file.
:param p4info: p4 info file
:return: void
"""
self.p4info = p4info
self._import_p4info_names()
def get_obj(self, obj_type, name):
"""
Retrieve an object by type and name.
:param obj_type: P4 object type
:param name: P4 object name
:return: P4 object
"""
key = (obj_type, name)
return self.p4info_obj_map.get(key, None)
def get_obj_id(self, obj_type, name):
"""
Retrieve a P4 object's ID by type and name.
:param obj_type: P4 object type
:param name: P4 object name
:return: P4 object ID
"""
obj = self.get_obj(obj_type, name)
if obj is None:
return None
return obj.preamble.id
def get_param(self, action_name, name):
"""
Get an action parameter by action name.
:param action_name: P4 action name
:param name: action parameter name
:return: action parameter
"""
action = self.get_obj(P4Type.action, action_name)
if action is None:
return None
for param in action.params:
if param.name == name:
return param
return None
def get_mf(self, table_name, name):
"""
Get a table's match field by name.
:param table_name: P4 table name
:param name: match field name
:return: match field
"""
table = self.get_obj(P4Type.table, table_name)
if table is None:
return None
for match_field in table.match_fields:
if match_field.name == name:
return match_field
return None
def get_param_id(self, action_name, name):
"""
Get an action parameter ID by the action and parameter names.
:param action_name: P4 action name
:param name: action parameter name
:return: action parameter ID
"""
param = self.get_param(action_name, name)
return None if param is None else param.id
def get_mf_id(self, table_name, name):
"""
Get a table's match field ID by name.
:param table_name: P4 table name
:param name: match field name
:return: match field ID
"""
match_field = self.get_mf(table_name, name)
return None if match_field is None else match_field.id
def get_param_name(self, action_name, id_):
"""
Get an action parameter name by the action name and action ID.
:param action_name: P4 action name
:param id_: action parameter ID
:return: action parameter name
"""
action = self.get_obj(P4Type.action, action_name)
if action is None:
return None
for param in action.params:
if param.id == id_:
return param.name
return None
def get_mf_name(self, table_name, id_):
"""
Get a table's match field name by ID.
:param table_name: P4 table name
:param id_: match field ID
:return: match field name
"""
table = self.get_obj(P4Type.table, table_name)
if table is None:
return None
for match_field in table.match_fields:
if match_field.id == id_:
return match_field.name
return None
def get_objs(self, obj_type):
"""
Get P4 objects by type.
:param obj_type: P4 object type
:return: list of tuples (object name, object)
"""
objects = self.p4info_objs_by_type[obj_type]
for name, obj in objects.items():
yield name, obj
def get_name_from_id(self, id_):
"""
Get P4 object name by its ID.
:param id_: P4 object ID
:return: P4 object name
"""
return self.p4info_obj_map_by_id[id_].preamble.name
def get_obj_by_id(self, id_):
"""
Get P4 object by its ID.
:param id_: P4 object ID
:return: P4 object
"""
return self.p4info_obj_map_by_id[id_]
def get_packet_metadata_name_from_id(self, ctrl_pkt_md_name, id_):
"""
Get packet metadata name by ID.
:param ctrl_pkt_md_name: packet replication entity name
:param id_: packet metadata ID
:return: packet metadata name
"""
ctrl_pkt_md = self.get_obj(
P4Type.controller_packet_metadata, ctrl_pkt_md_name)
if not ctrl_pkt_md:
return None
for meta in ctrl_pkt_md.metadata:
if meta.id == id_:
return meta.name
return None
# We accept any suffix that uniquely identifies the object
# among p4info objects of the same type.
def _import_p4info_names(self):
"""
Import p4 info into memory.
:return: void
"""
suffix_count = Counter()
for obj_type in P4Type:
self.p4info_objs_by_type[obj_type] = {}
for obj in getattr(self.p4info, obj_type.p4info_name):
pre = obj.preamble
self.p4info_obj_map_by_id[pre.id] = obj
self.p4info_objs_by_type[obj_type][pre.name] = obj
suffix = None
for suf in reversed(pre.name.split(".")):
suffix = suf if suffix is None else suf + "." + suffix
key = (obj_type, suffix)
self.p4info_obj_map[key] = obj
suffix_count[key] += 1
for key, cnt in suffix_count.items():
if cnt > 1:
del self.p4info_obj_map[key]
# Add p4info object and object id "getters" for each object type;
# these are just wrappers around Context.get_obj and Context.get_obj_id.
# For example: get_table(x) and get_table_id(x) respectively call
# get_obj(P4Type.table, x) and get_obj_id(P4Type.table, x)
for object_type in P4Type:
object_name = "_".join(["get", object_type.name])
setattr(Context, object_name, partialmethod(
Context.get_obj, object_type))
object_name = "_".join(["get", object_type.name, "id"])
setattr(Context, object_name, partialmethod(
Context.get_obj_id, object_type))
for object_type in P4Type:
object_name = "_".join(["get", object_type.p4info_name])
setattr(Context, object_name, partialmethod(Context.get_objs, object_type))