summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--ryu/app/ofctl_rest.py33
-rw-r--r--ryu/lib/ofctl_v1_3.py96
2 files changed, 129 insertions, 0 deletions
diff --git a/ryu/app/ofctl_rest.py b/ryu/app/ofctl_rest.py
index d9a8f4a0..e459b790 100644
--- a/ryu/app/ofctl_rest.py
+++ b/ryu/app/ofctl_rest.py
@@ -68,6 +68,9 @@ supported_ofctl = {
# get table stats of the switch
# GET /stats/table/<dpid>
#
+# get table features stats of the switch
+# GET /stats/tablefeatures/<dpid>
+#
# get ports stats of the switch
# GET /stats/port/<dpid>
#
@@ -270,6 +273,30 @@ class StatsController(ControllerBase):
body = json.dumps(ports)
return Response(content_type='application/json', body=body)
+ def get_table_features(self, req, dpid, **_kwargs):
+
+ if type(dpid) == str and not dpid.isdigit():
+ LOG.debug('invalid dpid %s', dpid)
+ return Response(status=400)
+
+ dp = self.dpset.get(int(dpid))
+
+ if dp is None:
+ return Response(status=404)
+
+ _ofp_version = dp.ofproto.OFP_VERSION
+
+ _ofctl = supported_ofctl.get(_ofp_version, None)
+ if _ofctl is not None:
+ ports = _ofctl.get_table_features(dp, self.waiters)
+
+ else:
+ LOG.debug('Unsupported OF protocol')
+ return Response(status=501)
+
+ body = json.dumps(ports)
+ return Response(content_type='application/json', body=body)
+
def get_port_stats(self, req, dpid, **_kwargs):
if type(dpid) == str and not dpid.isdigit():
@@ -773,6 +800,11 @@ class RestStatsApi(app_manager.RyuApp):
controller=StatsController, action='get_table_stats',
conditions=dict(method=['GET']))
+ uri = path + '/tablefeatures/{dpid}'
+ mapper.connect('stats', uri,
+ controller=StatsController, action='get_table_features',
+ conditions=dict(method=['GET']))
+
uri = path + '/port/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_port_stats',
@@ -853,6 +885,7 @@ class RestStatsApi(app_manager.RyuApp):
ofp_event.EventOFPFlowStatsReply,
ofp_event.EventOFPAggregateStatsReply,
ofp_event.EventOFPTableStatsReply,
+ ofp_event.EventOFPTableFeaturesStatsReply,
ofp_event.EventOFPPortStatsReply,
ofp_event.EventOFPQueueStatsReply,
ofp_event.EventOFPMeterStatsReply,
diff --git a/ryu/lib/ofctl_v1_3.py b/ryu/lib/ofctl_v1_3.py
index 16ba33e4..a3eae1f5 100644
--- a/ryu/lib/ofctl_v1_3.py
+++ b/ryu/lib/ofctl_v1_3.py
@@ -566,6 +566,102 @@ def get_table_stats(dp, waiters):
return desc
+def get_table_features(dp, waiters):
+ stats = dp.ofproto_parser.OFPTableFeaturesStatsRequest(dp, 0, [])
+ msgs = []
+ ofproto = dp.ofproto
+ send_stats_request(dp, stats, waiters, msgs)
+
+ prop_type = {ofproto.OFPTFPT_INSTRUCTIONS: 'INSTRUCTIONS',
+ ofproto.OFPTFPT_INSTRUCTIONS_MISS: 'INSTRUCTIONS_MISS',
+ ofproto.OFPTFPT_NEXT_TABLES: 'NEXT_TABLES',
+ ofproto.OFPTFPT_NEXT_TABLES_MISS: 'NEXT_TABLES_MISS',
+ ofproto.OFPTFPT_WRITE_ACTIONS: 'WRITE_ACTIONS',
+ ofproto.OFPTFPT_WRITE_ACTIONS_MISS: 'WRITE_ACTIONS_MISS',
+ ofproto.OFPTFPT_APPLY_ACTIONS: 'APPLY_ACTIONS',
+ ofproto.OFPTFPT_APPLY_ACTIONS_MISS: 'APPLY_ACTIONS_MISS',
+ ofproto.OFPTFPT_MATCH: 'MATCH',
+ ofproto.OFPTFPT_WILDCARDS: 'WILDCARDS',
+ ofproto.OFPTFPT_WRITE_SETFIELD: 'WRITE_SETFIELD',
+ ofproto.OFPTFPT_WRITE_SETFIELD_MISS: 'WRITE_SETFIELD_MISS',
+ ofproto.OFPTFPT_APPLY_SETFIELD: 'APPLY_SETFIELD',
+ ofproto.OFPTFPT_APPLY_SETFIELD_MISS: 'APPLY_SETFIELD_MISS',
+ ofproto.OFPTFPT_EXPERIMENTER: 'EXPERIMENTER',
+ ofproto.OFPTFPT_EXPERIMENTER_MISS: 'EXPERIMENTER_MISS'
+ }
+
+ p_type_instructions = [ofproto.OFPTFPT_INSTRUCTIONS,
+ ofproto.OFPTFPT_INSTRUCTIONS_MISS]
+
+ p_type_next_tables = [ofproto.OFPTFPT_NEXT_TABLES,
+ ofproto.OFPTFPT_NEXT_TABLES_MISS]
+
+ p_type_actions = [ofproto.OFPTFPT_WRITE_ACTIONS,
+ ofproto.OFPTFPT_WRITE_ACTIONS_MISS,
+ ofproto.OFPTFPT_APPLY_ACTIONS,
+ ofproto.OFPTFPT_APPLY_ACTIONS_MISS]
+
+ p_type_oxms = [ofproto.OFPTFPT_MATCH,
+ ofproto.OFPTFPT_WILDCARDS,
+ ofproto.OFPTFPT_WRITE_SETFIELD,
+ ofproto.OFPTFPT_WRITE_SETFIELD_MISS,
+ ofproto.OFPTFPT_APPLY_SETFIELD,
+ ofproto.OFPTFPT_APPLY_SETFIELD_MISS]
+
+ p_type_experimenter = [ofproto.OFPTFPT_EXPERIMENTER,
+ ofproto.OFPTFPT_EXPERIMENTER_MISS]
+
+ tables = []
+ for msg in msgs:
+ stats = msg.body
+ for stat in stats:
+ properties = []
+ for prop in stat.properties:
+ p = {'type': prop_type.get(prop.type, 'UNKNOWN')}
+ if prop.type in p_type_instructions:
+ instruction_ids = []
+ for id in prop.instruction_ids:
+ i = {'len': id.len,
+ 'type': id.type}
+ instruction_ids.append(i)
+ p['instruction_ids'] = instruction_ids
+ elif prop.type in p_type_next_tables:
+ table_ids = []
+ for id in prop.table_ids:
+ table_ids.append(id)
+ p['table_ids'] = table_ids
+ elif prop.type in p_type_actions:
+ action_ids = []
+ for id in prop.action_ids:
+ i = {'len': id.len,
+ 'type': id.type}
+ action_ids.append(i)
+ p['action_ids'] = action_ids
+ elif prop.type in p_type_oxms:
+ oxm_ids = []
+ for id in prop.oxm_ids:
+ i = {'hasmask': id.hasmask,
+ 'length': id.length,
+ 'type': id.type}
+ oxm_ids.append(i)
+ p['oxm_ids'] = oxm_ids
+ elif prop.type in p_type_experimenter:
+ pass
+ properties.append(p)
+ s = {'table_id': stat.table_id,
+ 'name': stat.name,
+ 'metadata_match': stat.metadata_match,
+ 'metadata_write': stat.metadata_write,
+ 'config': stat.config,
+ 'max_entries': stat.max_entries,
+ 'properties': properties,
+ }
+ tables.append(s)
+ desc = {str(dp.id): tables}
+
+ return desc
+
+
def get_port_stats(dp, waiters):
stats = dp.ofproto_parser.OFPPortStatsRequest(
dp, 0, dp.ofproto.OFPP_ANY)