ofctl_rest: support group-related messages

this patch makes ofctl_rest possible to:

  - support the GROUP action in the FlowMod message
  - support the GroupMod message
  - support the GroupStats message
  - support the GroupFeatures message
  - support the GroupDesc message

see following examples.

FlowMod with the group action:

  e.g.)

    curl -X POST -d '{"dpid": 1,
                      "match": {},
                      "actions": [{"type": "GROUP,
                                   "group_id": 1}]}' http://localhost:8080/stats/flowentry/add

GroupMod:

  usage)

    URI:    /stats/groupentry/{cmd}
    METHOD: POST

      NOTE: the value of 'cmd' is one of follows: 'add', 'modify', or 'delete'.

    the message body is as follows:

      type      Group types. 'ALL', 'SELECT', 'INDIRECT', or 'FF'.
      group_id  Group Identifier. (default: 0)
      buckets   a list of buckets.

    where each bucket has the following members:

      weight       Relative weight of bucket. (default: 0)
      watch_port   Port whose state affects whether this bucket is live. (default: OFPP_ANY)
      watch_group  Group whose state affects whether this bucket is live. (default: OFPG_ANY)
      actions      a list of actions. the format is the same as that of FlowMod.

  e.g.)

    curl -X POST -d '{"dpid": 1,
                      "type": "FF",
                      "group_id": 1,
                      "buckets": [{"watch_port": 2,
                                   "actions": [{"type": "OUTPUT",
                                                "port": 3}]}]}' http://localhost:8080/stats/groupentry/add

GroupStats:

  usage)

    URI:    /stats/group/{dpid}
    METHOD: GET

  e.g.)

    curl http://localhost:8080/stats/group/1
    {
      "1": [
        {
          "bucket_stats": [
            {
              "packet_count": 0,
              "byte_count": 0
            }
          ],
          "byte_count": 0,
          "ref_count": 0,
          "duration_nsec": 231000000,
          "packet_count": 0,
          "duration_sec": 11238,
          "group_id": 1
        }
      ]
    }

GroupFeatures:

  usage)

    URI:    /stats/groupfeatures/{dpid}
    METHOD: GET

  e.g.)

    curl http://localhost:8080/stats/groupfeatures/1
    {
      "1": [
        {
          "actions": [
            {"ALL": ["OUTPUT", "COPY_TTL_OUT", "COPY_TTL_IN", "SET_MPLS_TTL", "DEC_MPLS_TTL", "PUSH_VLAN", "POP_VLAN", "PUSH_MPLS", "POP_MPLS", "SET_QUEUE", "GROUP", "SET_NW_TTL", "DEC_NW_TTL", "SET_FIELD"]},
            {"SELECT": []},
            {"INDIRECT": []},
            {"FF": []}
          ],
          "max_groups": [
            {"ALL": 4294967040},
            {"SELECT": 4294967040},
            {"INDIRECT": 4294967040},
            {"FF": 4294967040}
          ],
          "capabilities": ["SELECT_WEIGHT", "SELECT_LIVENESS", "CHAINING"],
          "types": []
        }
      ]
    }

GroupDesc:

  usage)

    URI:    /stats/groupdesc/{dpid}
    METHOD: GET

  e.g.)

    curl http://localhost:8080/stats/groupdesc/1
    {
      "1": [
        {
          "buckets": [
            {
              "actions": ["OUTPUT:2"],
              "watch_group": 4294967295,
              "weight": 0,
              "watch_port": 3
            }
          ],
          "group_id": 1,
          "type": "FF"
        }
      ]
    }

Signed-off-by: Yuichi Ito <ito.yuichi0@gmail.com>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
This commit is contained in:
Yuichi Ito 2014-01-08 14:29:21 +09:00 committed by FUJITA Tomonori
parent cd8cf7d4a5
commit 8b2097b352
3 changed files with 721 additions and 131 deletions

View File

@ -59,6 +59,16 @@ LOG = logging.getLogger('ryu.app.ofctl_rest')
# get meters stats of the switch
# GET /stats/meter/<dpid>
#
# get group features stats of the switch
# GET /stats/groupfeatures/<dpid>
#
# get groups desc stats of the switch
# GET /stats/groupdesc/<dpid>
#
# get groups stats of the switch
# GET /stats/group/<dpid>
#
#
## Update the switch stats
#
# add a flow entry
@ -82,6 +92,15 @@ LOG = logging.getLogger('ryu.app.ofctl_rest')
# delete a meter entry
# POST /stats/meterentry/delete
#
# add a group entry
# POST /stats/groupentry/add
#
# modify a group entry
# POST /stats/groupentry/modify
#
# delete a group entry
# POST /stats/groupentry/delete
#
#
# send a experimeter message
# POST /stats/experimenter/<dpid>
@ -194,6 +213,54 @@ class StatsController(ControllerBase):
body = json.dumps(meters)
return (Response(content_type='application/json', body=body))
def get_group_features(self, req, dpid, **_kwargs):
dp = self.dpset.get(int(dpid))
if dp is None:
return Response(status=404)
if dp.ofproto.OFP_VERSION == ofproto_v1_2.OFP_VERSION:
groups = ofctl_v1_2.get_group_features(dp, self.waiters)
elif dp.ofproto.OFP_VERSION == ofproto_v1_3.OFP_VERSION:
groups = ofctl_v1_3.get_group_features(dp, self.waiters)
else:
LOG.debug('Unsupported OF protocol')
return Response(status=501)
body = json.dumps(groups)
return Response(content_type='application/json', body=body)
def get_group_desc(self, req, dpid, **_kwargs):
dp = self.dpset.get(int(dpid))
if dp is None:
return Response(status=404)
if dp.ofproto.OFP_VERSION == ofproto_v1_2.OFP_VERSION:
groups = ofctl_v1_2.get_group_desc(dp, self.waiters)
elif dp.ofproto.OFP_VERSION == ofproto_v1_3.OFP_VERSION:
groups = ofctl_v1_3.get_group_desc(dp, self.waiters)
else:
LOG.debug('Unsupported OF protocol')
return Response(status=501)
body = json.dumps(groups)
return Response(content_type='application/json', body=body)
def get_group_stats(self, req, dpid, **_kwargs):
dp = self.dpset.get(int(dpid))
if dp is None:
return Response(status=404)
if dp.ofproto.OFP_VERSION == ofproto_v1_2.OFP_VERSION:
groups = ofctl_v1_2.get_group_stats(dp, self.waiters)
elif dp.ofproto.OFP_VERSION == ofproto_v1_3.OFP_VERSION:
groups = ofctl_v1_3.get_group_stats(dp, self.waiters)
else:
LOG.debug('Unsupported OF protocol')
return Response(status=501)
body = json.dumps(groups)
return Response(content_type='application/json', body=body)
def mod_flow_entry(self, req, cmd, **_kwargs):
try:
flow = eval(req.body)
@ -278,6 +345,41 @@ class StatsController(ControllerBase):
return Response(status=200)
def mod_group_entry(self, req, cmd, **_kwargs):
try:
group = eval(req.body)
except SyntaxError:
LOG.debug('invalid syntax %s', req.body)
return Response(status=400)
dpid = group.get('dpid')
dp = self.dpset.get(int(dpid))
if dp is None:
return Response(status=404)
if dp.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION:
LOG.debug('Unsupported OF protocol')
return Response(status=501)
if cmd == 'add':
cmd = dp.ofproto.OFPGC_ADD
elif cmd == 'modify':
cmd = dp.ofproto.OFPGC_MODIFY
elif cmd == 'delete':
cmd = dp.ofproto.OFPGC_DELETE
else:
return Response(status=404)
if dp.ofproto.OFP_VERSION == ofproto_v1_2.OFP_VERSION:
ofctl_v1_2.mod_group_entry(dp, group, cmd)
elif dp.ofproto.OFP_VERSION == ofproto_v1_3.OFP_VERSION:
ofctl_v1_3.mod_group_entry(dp, group, cmd)
else:
LOG.debug('Unsupported OF protocol')
return Response(status=501)
return Response(status=200)
def send_experimenter(self, req, dpid, **_kwargs):
dp = self.dpset.get(int(dpid))
if dp is None:
@ -356,6 +458,21 @@ class RestStatsApi(app_manager.RyuApp):
controller=StatsController, action='get_meter_stats',
conditions=dict(method=['GET']))
uri = path + '/groupfeatures/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_group_features',
conditions=dict(method=['GET']))
uri = path + '/groupdesc/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_group_desc',
conditions=dict(method=['GET']))
uri = path + '/group/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='get_group_stats',
conditions=dict(method=['GET']))
uri = path + '/flowentry/{cmd}'
mapper.connect('stats', uri,
controller=StatsController, action='mod_flow_entry',
@ -371,6 +488,11 @@ class RestStatsApi(app_manager.RyuApp):
controller=StatsController, action='mod_meter_entry',
conditions=dict(method=['POST']))
uri = path + '/groupentry/{cmd}'
mapper.connect('stats', uri,
controller=StatsController, action='mod_group_entry',
conditions=dict(method=['POST']))
uri = path + '/experimenter/{dpid}'
mapper.connect('stats', uri,
controller=StatsController, action='send_experimenter',
@ -428,3 +550,15 @@ class RestStatsApi(app_manager.RyuApp):
@set_ev_cls(ofp_event.EventOFPMeterConfigStatsReply, MAIN_DISPATCHER)
def meter_config_stats_reply_handler(self, ev):
self.stats_reply_handler(ev)
@set_ev_cls(ofp_event.EventOFPGroupStatsReply, MAIN_DISPATCHER)
def group_stats_reply_handler(self, ev):
self.stats_reply_handler(ev)
@set_ev_cls(ofp_event.EventOFPGroupFeaturesStatsReply, MAIN_DISPATCHER)
def group_features_stats_reply_handler(self, ev):
self.stats_reply_handler(ev)
@set_ev_cls(ofp_event.EventOFPGroupDescStatsReply, MAIN_DISPATCHER)
def group_desc_stats_reply_handler(self, ev):
self.stats_reply_handler(ev)

View File

@ -31,41 +31,176 @@ LOG = logging.getLogger('ryu.lib.ofctl_v1_2')
DEFAULT_TIMEOUT = 1.0
def str_to_int(src):
if isinstance(src, str):
if src.startswith("0x") or src.startswith("0X"):
dst = int(src, 16)
else:
dst = int(src)
else:
dst = src
return dst
def to_action(dp, dic):
ofp = dp.ofproto
parser = dp.ofproto_parser
result = None
action_type = dic.get('type')
if action_type == 'OUTPUT':
out_port = int(dic.get('port', ofp.OFPP_ANY))
max_len = int(dic.get('max_len', ofp.OFPCML_MAX))
result = parser.OFPActionOutput(out_port, max_len)
elif action_type == 'COPY_TTL_OUT':
result = parser.OFPActionCopyTtlOut()
elif action_type == 'COPY_TTL_IN':
result = parser.OFPActionCopyTtlIn()
elif action_type == 'SET_MPLS_TTL':
mpls_ttl = int(dic.get('mpls_ttl'))
result = parser.OFPActionSetMplsTtl(mpls_ttl)
elif action_type == 'DEC_MPLS_TTL':
result = parser.OFPActionDecMplsTtl()
elif action_type == 'PUSH_VLAN':
ethertype = int(dic.get('ethertype'))
result = parser.OFPActionPushVlan(ethertype)
elif action_type == 'POP_VLAN':
result = parser.OFPActionPopVlan()
elif action_type == 'PUSH_MPLS':
ethertype = int(dic.get('ethertype'))
result = parser.OFPActionPushMpls(ethertype)
elif action_type == 'POP_MPLS':
ethertype = int(dic.get('ethertype'))
result = parser.OFPActionPopMpls(ethertype)
elif action_type == 'SET_QUEUE':
queue_id = int(dic.get('queue_id'))
result = parser.OFPActionSetQueue(queue_id)
elif action_type == 'GROUP':
group_id = int(dic.get('group_id'))
result = parser.OFPActionGroup(group_id)
elif action_type == 'SET_NW_TTL':
nw_ttl = int(dic.get('nw_ttl'))
result = parser.OFPActionSetNwTtl(nw_ttl)
elif action_type == 'DEC_NW_TTL':
result = parser.OFPActionDecNwTtl()
elif action_type == 'SET_FIELD':
field = dic.get('field')
value = dic.get('value')
if field == 'eth_dst':
field = ofp.OXM_OF_ETH_DST
value = mac.haddr_to_bin(str(value))
elif field == 'eth_src':
field = ofp.OXM_OF_ETH_SRC
value = mac.haddr_to_bin(str(value))
elif field == 'vlan_vid':
field = ofp.OXM_OF_VLAN_VID
value = int(value)
elif field == 'mpls_label':
field = ofp.OXM_OF_MPLS_LABEL
value = int(value)
else:
LOG.debug('Unknown field: %s' % field)
return None
f = parser.OFPMatchField.make(field, value)
result = parser.OFPActionSetField(f)
else:
LOG.debug('Unknown action type: %s' % action_type)
return result
def to_actions(dp, acts):
inst = []
actions = []
ofp = dp.ofproto
parser = dp.ofproto_parser
for a in acts:
action_type = a.get('type')
if action_type == 'OUTPUT':
out_port = int(a.get('port', ofproto_v1_2.OFPP_ANY))
max_len = int(a.get('max_len', ofproto_v1_2.OFPCML_MAX))
actions = [dp.ofproto_parser.OFPActionOutput(
out_port, max_len=max_len)]
inst_type = dp.ofproto.OFPIT_APPLY_ACTIONS
inst = [dp.ofproto_parser.OFPInstructionActions(
inst_type, actions)]
action = to_action(dp, a)
if action is not None:
actions.append(action)
else:
LOG.debug('Unknown action type')
action_type = a.get('type')
if action_type == 'GOTO_TABLE':
table_id = int(a.get('table_id'))
inst.append(parser.OFPInstructionGotoTable(table_id))
elif action_type == 'WRITE_METADATA':
metadata = str_to_int(a.get('metadata'))
metadata_mask = (str_to_int(a['metadata_mask'])
if 'metadata_mask' in a
else parser.UINT64_MAX)
inst.append(
parser.OFPInstructionWriteMetadata(
metadata, metadata_mask))
else:
LOG.debug('Unknown action type: %s' % action_type)
inst.append(parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS,
actions))
return inst
def action_to_str(act):
action_type = act.cls_action_type
if action_type == ofproto_v1_2.OFPAT_OUTPUT:
buf = 'OUTPUT:' + str(act.port)
elif action_type == ofproto_v1_2.OFPAT_COPY_TTL_OUT:
buf = 'COPY_TTL_OUT'
elif action_type == ofproto_v1_2.OFPAT_COPY_TTL_IN:
buf = 'COPY_TTL_IN'
elif action_type == ofproto_v1_2.OFPAT_SET_MPLS_TTL:
buf = 'SET_MPLS_TTL:' + str(act.mpls_ttl)
elif action_type == ofproto_v1_2.OFPAT_DEC_MPLS_TTL:
buf = 'DEC_MPLS_TTL'
elif action_type == ofproto_v1_2.OFPAT_PUSH_VLAN:
buf = 'PUSH_VLAN:' + str(act.ethertype)
elif action_type == ofproto_v1_2.OFPAT_POP_VLAN:
buf = 'POP_VLAN'
elif action_type == ofproto_v1_2.OFPAT_PUSH_MPLS:
buf = 'PUSH_MPLS:' + str(act.ethertype)
elif action_type == ofproto_v1_2.OFPAT_POP_MPLS:
buf = 'POP_MPLS'
elif action_type == ofproto_v1_2.OFPAT_OFPAT_SET_QUEUE:
buf = 'SET_QUEUE:' + str(act.queue_id)
elif action_type == ofproto_v1_2.OFPAT_GROUP:
buf = 'GROUP:' + str(act.group_id)
elif action_type == ofproto_v1_2.OFPAT_SET_NW_TTL:
buf = 'SET_NW_TTL:' + str(act.nw_ttl)
elif action_type == ofproto_v1_2.OFPAT_DEC_NW_TTL:
buf = 'DEC_NW_TTL'
elif action_type == ofproto_v1_2.OFPAT_SET_FIELD:
buf = 'SET_FIELD: {%s:%s}' % (act.field, act.value)
else:
buf = 'UNKNOWN'
return buf
def actions_to_str(instructions):
actions = []
for instruction in instructions:
if not isinstance(instruction,
ofproto_v1_2_parser.OFPInstructionActions):
continue
for a in instruction.actions:
action_type = a.cls_action_type
if isinstance(instruction,
ofproto_v1_2_parser.OFPInstructionActions):
for a in instruction.actions:
actions.append(action_to_str(a))
if action_type == ofproto_v1_2.OFPAT_OUTPUT:
buf = 'OUTPUT:' + str(a.port)
else:
buf = 'UNKNOWN'
elif isinstance(instruction,
ofproto_v1_2_parser.OFPInstructionGotoTable):
buf = 'GOTO_TABLE:' + str(instruction.table_id)
actions.append(buf)
elif isinstance(instruction,
ofproto_v1_2_parser.OFPInstructionWriteMetadata):
buf = ('WRITE_METADATA:0x%x/0x%x' % (instruction.metadata,
instruction.metadata_mask)
if instruction.metadata_mask
else 'WRITE_METADATA:0x%x' % instruction.metadata)
actions.append(buf)
else:
continue
return actions
@ -348,6 +483,122 @@ def get_port_stats(dp, waiters):
return ports
def get_group_stats(dp, waiters):
stats = dp.ofproto_parser.OFPGroupStatsRequest(
dp, dp.ofproto.OFPG_ALL, 0)
msgs = []
send_stats_request(dp, stats, waiters, msgs)
groups = []
for msg in msgs:
for stats in msg.body:
bucket_counters = []
for bucket_counter in stats.bucket_counters:
c = {'packet_count': bucket_counter.packet_count,
'byte_count': bucket_counter.byte_count}
bucket_counters.append(c)
g = {'group_id': stats.group_id,
'ref_count': stats.ref_count,
'packet_count': stats.packet_count,
'byte_count': stats.byte_count,
'bucket_stats': bucket_counters}
groups.append(g)
groups = {str(dp.id): groups}
return groups
def get_group_features(dp, waiters):
ofp = dp.ofproto
type_convert = {ofp.OFPGT_ALL: 'ALL',
ofp.OFPGT_SELECT: 'SELECT',
ofp.OFPGT_INDIRECT: 'INDIRECT',
ofp.OFPGT_FF: 'FF'}
cap_convert = {ofp.OFPGFC_SELECT_WEIGHT: 'SELECT_WEIGHT',
ofp.OFPGFC_SELECT_LIVENESS: 'SELECT_LIVENESS',
ofp.OFPGFC_CHAINING: 'CHAINING',
ofp.OFPGFC_CHAINING_CHECKS: 'CHAINING_CHCEKS'}
act_convert = {ofp.OFPAT_OUTPUT: 'OUTPUT',
ofp.OFPAT_COPY_TTL_OUT: 'COPY_TTL_OUT',
ofp.OFPAT_COPY_TTL_IN: 'COPY_TTL_IN',
ofp.OFPAT_SET_MPLS_TTL: 'SET_MPLS_TTL',
ofp.OFPAT_DEC_MPLS_TTL: 'DEC_MPLS_TTL',
ofp.OFPAT_PUSH_VLAN: 'PUSH_VLAN',
ofp.OFPAT_POP_VLAN: 'POP_VLAN',
ofp.OFPAT_PUSH_MPLS: 'PUSH_MPLS',
ofp.OFPAT_POP_MPLS: 'POP_MPLS',
ofp.OFPAT_SET_QUEUE: 'SET_QUEUE',
ofp.OFPAT_GROUP: 'GROUP',
ofp.OFPAT_SET_NW_TTL: 'SET_NW_TTL',
ofp.OFPAT_DEC_NW_TTL: 'DEC_NW_TTL',
ofp.OFPAT_SET_FIELD: 'SET_FIELD'}
stats = dp.ofproto_parser.OFPGroupFeaturesStatsRequest(dp, 0)
msgs = []
send_stats_request(dp, stats, waiters, msgs)
features = []
for msg in msgs:
feature = msg.body
types = []
for k, v in type_convert.items():
if (1 << k) & feature.types:
types.append(v)
capabilities = []
for k, v in cap_convert.items():
if k & feature.capabilities:
capabilities.append(v)
max_groups = []
for k, v in type_convert.items():
max_groups.append({v: feature.max_groups[k]})
actions = []
for k1, v1 in type_convert.items():
acts = []
for k2, v2 in act_convert.items():
if (1 << k2) & feature.actions[k1]:
acts.append(v2)
actions.append({v1: acts})
f = {'types': types,
'capabilities': capabilities,
'max_groups': max_groups,
'actions': actions}
features.append(f)
features = {str(dp.id): features}
return features
def get_group_desc(dp, waiters):
type_convert = {dp.ofproto.OFPGT_ALL: 'ALL',
dp.ofproto.OFPGT_SELECT: 'SELECT',
dp.ofproto.OFPGT_INDIRECT: 'INDIRECT',
dp.ofproto.OFPGT_FF: 'FF'}
stats = dp.ofproto_parser.OFPGroupDescStatsRequest(dp, 0)
msgs = []
send_stats_request(dp, stats, waiters, msgs)
descs = []
for msg in msgs:
for stats in msg.body:
buckets = []
for bucket in stats.buckets:
actions = []
for action in bucket.actions:
actions.append(action_to_str(action))
b = {'weight': bucket.weight,
'watch_port': bucket.watch_port,
'watch_group': bucket.watch_group,
'actions': actions}
buckets.append(b)
d = {'type': type_convert.get(stats.type),
'group_id': stats.group_id,
'buckets': buckets}
descs.append(d)
descs = {str(dp.id): descs}
return descs
def mod_flow_entry(dp, flow, cmd):
cookie = int(flow.get('cookie', 0))
cookie_mask = int(flow.get('cookie_mask', 0))
@ -370,6 +621,38 @@ def mod_flow_entry(dp, flow, cmd):
dp.send_msg(flow_mod)
def mod_group_entry(dp, group, cmd):
type_convert = {'ALL': dp.ofproto.OFPGT_ALL,
'SELECT': dp.ofproto.OFPGT_SELECT,
'INDIRECT': dp.ofproto.OFPGT_INDIRECT,
'FF': dp.ofproto.OFPGT_FF}
type_ = type_convert.get(group.get('type'))
if not type_:
LOG.debug('Unknown type: %s', group.get('type'))
group_id = int(group.get('group_id', 0))
buckets = []
for bucket in group.get('buckets', []):
weight = int(bucket.get('weight', 0))
watch_port = int(bucket.get('watch_port', dp.ofproto.OFPP_ANY))
watch_group = int(bucket.get('watch_group', dp.ofproto.OFPG_ANY))
actions = []
for dic in bucket.get('actions', []):
action = to_action(dp, dic)
if action is not None:
actions.append(action)
buckets.append(dp.ofproto_parser.OFPBucket(
weight, watch_port, watch_group, actions))
group_mod = dp.ofproto_parser.OFPGroupMod(
dp, cmd, type_, group_id, buckets)
dp.send_msg(group_mod)
def send_experimenter(dp, exp):
experimenter = exp.get('experimenter', 0)
exp_type = exp.get('exp_type', 0)

View File

@ -42,6 +42,78 @@ def str_to_int(src):
return dst
def to_action(dp, dic):
ofp = dp.ofproto
parser = dp.ofproto_parser
result = None
action_type = dic.get('type')
if action_type == 'OUTPUT':
out_port = int(dic.get('port', ofp.OFPP_ANY))
max_len = int(dic.get('max_len', ofp.OFPCML_MAX))
result = parser.OFPActionOutput(out_port, max_len)
elif action_type == 'COPY_TTL_OUT':
result = parser.OFPActionCopyTtlOut()
elif action_type == 'COPY_TTL_IN':
result = parser.OFPActionCopyTtlIn()
elif action_type == 'SET_MPLS_TTL':
mpls_ttl = int(dic.get('mpls_ttl'))
result = parser.OFPActionSetMplsTtl(mpls_ttl)
elif action_type == 'DEC_MPLS_TTL':
result = parser.OFPActionDecMplsTtl()
elif action_type == 'PUSH_VLAN':
ethertype = int(dic.get('ethertype'))
result = parser.OFPActionPushVlan(ethertype)
elif action_type == 'POP_VLAN':
result = parser.OFPActionPopVlan()
elif action_type == 'PUSH_MPLS':
ethertype = int(dic.get('ethertype'))
result = parser.OFPActionPushMpls(ethertype)
elif action_type == 'POP_MPLS':
ethertype = int(dic.get('ethertype'))
result = parser.OFPActionPopMpls(ethertype)
elif action_type == 'SET_QUEUE':
queue_id = int(dic.get('queue_id'))
result = parser.OFPActionSetQueue(queue_id)
elif action_type == 'GROUP':
group_id = int(dic.get('group_id'))
result = parser.OFPActionGroup(group_id)
elif action_type == 'SET_NW_TTL':
nw_ttl = int(dic.get('nw_ttl'))
result = parser.OFPActionSetNwTtl(nw_ttl)
elif action_type == 'DEC_NW_TTL':
result = parser.OFPActionDecNwTtl()
elif action_type == 'SET_FIELD':
field = dic.get('field')
value = dic.get('value')
if field == 'eth_dst':
field = ofp.OXM_OF_ETH_DST
value = mac.haddr_to_bin(str(value))
elif field == 'eth_src':
field = ofp.OXM_OF_ETH_SRC
value = mac.haddr_to_bin(str(value))
elif field == 'vlan_vid':
field = ofp.OXM_OF_VLAN_VID
value = int(value)
elif field == 'mpls_label':
field = ofp.OXM_OF_MPLS_LABEL
value = int(value)
else:
LOG.debug('Unknown field: %s' % field)
return None
f = parser.OFPMatchField.make(field, value)
result = parser.OFPActionSetField(f)
elif action_type == 'PUSH_PBB':
ethertype = int(dic.get('ethertype'))
result = parser.OFPActionPushPbb(ethertype)
elif action_type == 'POP_PBB':
result = parser.OFPActionPopPbb()
else:
LOG.debug('Unknown action type: %s' % action_type)
return result
def to_actions(dp, acts):
inst = []
actions = []
@ -49,88 +121,73 @@ def to_actions(dp, acts):
parser = dp.ofproto_parser
for a in acts:
action_type = a.get('type')
if action_type == 'OUTPUT':
out_port = int(a.get('port', ofproto_v1_3.OFPP_ANY))
max_len = int(a.get('max_len', ofproto_v1_3.OFPCML_MAX))
actions.append((parser.OFPActionOutput(out_port,
max_len)))
elif action_type == 'COPY_TTL_OUT':
actions.append(parser.OFPActionCopyTtlOut())
elif action_type == 'COPY_TTL_IN':
actions.append(parser.OFPActionCopyTtlIn())
elif action_type == 'SET_MPLS_TTL':
mpls_ttl = int(a.get('mpls_ttl'))
actions.append((parser.OFPActionSetMplsTtl(mpls_ttl)))
elif action_type == 'DEC_MPLS_TTL':
actions.append((parser.OFPActionDecMplsTtl()))
elif action_type == 'PUSH_VLAN':
ethertype = int(a.get('ethertype'))
actions.append((parser.OFPActionPushVlan(ethertype)))
elif action_type == 'POP_VLAN':
actions.append(parser.OFPActionPopVlan())
elif action_type == 'PUSH_MPLS':
ethertype = int(a.get('ethertype'))
actions.append(parser.OFPActionPushMpls(ethertype))
elif action_type == 'POP_MPLS':
ethertype = int(a.get('ethertype'))
actions.append(parser.OFPActionPopMpls(ethertype))
elif action_type == 'SET_QUEUE':
queue_id = int(a.get('queue_id'))
actions.append(parser.OFPActionSetQueue(queue_id))
elif action_type == 'GROUP':
pass
elif action_type == 'SET_NW_TTL':
nw_ttl = int(a.get('nw_ttl'))
actions.append(parser.OFPActionSetNwTtl(nw_ttl))
elif action_type == 'DEC_NW_TTL':
actions.append(parser.OFPActionDecNwTtl())
elif action_type == 'SET_FIELD':
field = a.get('field')
value = a.get('value')
if field == 'eth_dst':
field = ofp.OXM_OF_ETH_DST
value = mac.haddr_to_bin(str(value))
elif field == 'eth_src':
field = ofp.OXM_OF_ETH_SRC
value = mac.haddr_to_bin(str(value))
elif field == 'vlan_vid':
field = ofp.OXM_OF_VLAN_VID
value = int(value)
elif field == 'mpls_label':
field = ofp.OXM_OF_MPLS_LABEL
value = int(value)
else:
LOG.debug('Unknown field: %s' % field)
continue
f = parser.OFPMatchField.make(field, value)
actions.append(parser.OFPActionSetField(f))
elif action_type == 'PUSH_PBB':
ethertype = int(a.get('ethertype'))
actions.append(parser.OFPActionPushPbb(ethertype))
elif action_type == 'POP_PBB':
actions.append(parser.OFPActionPopPbb())
elif action_type == 'GOTO_TABLE':
table_id = int(a.get('table_id'))
inst.append(parser.OFPInstructionGotoTable(table_id))
elif action_type == 'WRITE_METADATA':
metadata = str_to_int(a.get('metadata'))
metadata_mask = (str_to_int(a['metadata_mask'])
if 'metadata_mask' in a
else ofproto_v1_3_parser.UINT64_MAX)
inst.append(
parser.OFPInstructionWriteMetadata(metadata, metadata_mask))
elif action_type == 'METER':
meter_id = int(a.get('meter_id'))
inst.append(parser.OFPInstructionMeter(meter_id))
action = to_action(dp, a)
if action is not None:
actions.append(action)
else:
LOG.debug('Unknown action type: %s' % action_type)
action_type = a.get('type')
if action_type == 'GOTO_TABLE':
table_id = int(a.get('table_id'))
inst.append(parser.OFPInstructionGotoTable(table_id))
elif action_type == 'WRITE_METADATA':
metadata = str_to_int(a.get('metadata'))
metadata_mask = (str_to_int(a['metadata_mask'])
if 'metadata_mask' in a
else parser.UINT64_MAX)
inst.append(
parser.OFPInstructionWriteMetadata(
metadata, metadata_mask))
elif action_type == 'METER':
meter_id = int(a.get('meter_id'))
inst.append(parser.OFPInstructionMeter(meter_id))
else:
LOG.debug('Unknown action type: %s' % action_type)
inst.append(parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS,
actions))
return inst
def action_to_str(act):
action_type = act.cls_action_type
if action_type == ofproto_v1_3.OFPAT_OUTPUT:
buf = 'OUTPUT:' + str(act.port)
elif action_type == ofproto_v1_3.OFPAT_COPY_TTL_OUT:
buf = 'COPY_TTL_OUT'
elif action_type == ofproto_v1_3.OFPAT_COPY_TTL_IN:
buf = 'COPY_TTL_IN'
elif action_type == ofproto_v1_3.OFPAT_SET_MPLS_TTL:
buf = 'SET_MPLS_TTL:' + str(act.mpls_ttl)
elif action_type == ofproto_v1_3.OFPAT_DEC_MPLS_TTL:
buf = 'DEC_MPLS_TTL'
elif action_type == ofproto_v1_3.OFPAT_PUSH_VLAN:
buf = 'PUSH_VLAN:' + str(act.ethertype)
elif action_type == ofproto_v1_3.OFPAT_POP_VLAN:
buf = 'POP_VLAN'
elif action_type == ofproto_v1_3.OFPAT_PUSH_MPLS:
buf = 'PUSH_MPLS:' + str(act.ethertype)
elif action_type == ofproto_v1_3.OFPAT_POP_MPLS:
buf = 'POP_MPLS'
elif action_type == ofproto_v1_3.OFPAT_OFPAT_SET_QUEUE:
buf = 'SET_QUEUE:' + str(act.queue_id)
elif action_type == ofproto_v1_3.OFPAT_GROUP:
pass
elif action_type == ofproto_v1_3.OFPAT_SET_NW_TTL:
buf = 'SET_NW_TTL:' + str(act.nw_ttl)
elif action_type == ofproto_v1_3.OFPAT_DEC_NW_TTL:
buf = 'DEC_NW_TTL'
elif action_type == ofproto_v1_3.OFPAT_SET_FIELD:
buf = 'SET_FIELD: {%s:%s}' % (act.field, act.value)
elif action_type == ofproto_v1_3.OFPAT_PUSH_PBB:
buf = 'PUSH_PBB:' + str(act.ethertype)
elif action_type == ofproto_v1_3.OFPAT_POP_PBB:
buf = 'POP_PBB'
else:
buf = 'UNKNOWN'
return buf
def actions_to_str(instructions):
actions = []
@ -138,43 +195,7 @@ def actions_to_str(instructions):
if isinstance(instruction,
ofproto_v1_3_parser.OFPInstructionActions):
for a in instruction.actions:
action_type = a.cls_action_type
if action_type == ofproto_v1_3.OFPAT_OUTPUT:
buf = 'OUTPUT:' + str(a.port)
elif action_type == ofproto_v1_3.OFPAT_COPY_TTL_OUT:
buf = 'COPY_TTL_OUT'
elif action_type == ofproto_v1_3.OFPAT_COPY_TTL_IN:
buf = 'COPY_TTL_IN'
elif action_type == ofproto_v1_3.OFPAT_SET_MPLS_TTL:
buf = 'SET_MPLS_TTL:' + str(a.mpls_ttl)
elif action_type == ofproto_v1_3.OFPAT_DEC_MPLS_TTL:
buf = 'DEC_MPLS_TTL'
elif action_type == ofproto_v1_3.OFPAT_PUSH_VLAN:
buf = 'PUSH_VLAN:' + str(a.ethertype)
elif action_type == ofproto_v1_3.OFPAT_POP_VLAN:
buf = 'POP_VLAN'
elif action_type == ofproto_v1_3.OFPAT_PUSH_MPLS:
buf = 'PUSH_MPLS:' + str(a.ethertype)
elif action_type == ofproto_v1_3.OFPAT_POP_MPLS:
buf = 'POP_MPLS'
elif action_type == ofproto_v1_3.OFPAT_OFPAT_SET_QUEUE:
buf = 'SET_QUEUE:' + str(a.queue_id)
elif action_type == ofproto_v1_3.OFPAT_GROUP:
pass
elif action_type == ofproto_v1_3.OFPAT_SET_NW_TTL:
buf = 'SET_NW_TTL:' + str(a.nw_ttl)
elif action_type == ofproto_v1_3.OFPAT_DEC_NW_TTL:
buf = 'DEC_NW_TTL'
elif action_type == ofproto_v1_3.OFPAT_SET_FIELD:
buf = 'SET_FIELD: {%s:%s}' % (a.field, a.value)
elif action_type == ofproto_v1_3.OFPAT_PUSH_PBB:
buf = 'PUSH_PBB:' + str(a.ethertype)
elif action_type == ofproto_v1_3.OFPAT_POP_PBB:
buf = 'POP_PBB'
else:
buf = 'UNKNOWN'
actions.append(buf)
actions.append(action_to_str(a))
elif isinstance(instruction,
ofproto_v1_3_parser.OFPInstructionGotoTable):
@ -581,6 +602,126 @@ def get_meter_config(dp, waiters):
return configs
def get_group_stats(dp, waiters):
stats = dp.ofproto_parser.OFPGroupStatsRequest(
dp, 0, dp.ofproto.OFPG_ALL)
msgs = []
send_stats_request(dp, stats, waiters, msgs)
groups = []
for msg in msgs:
for stats in msg.body:
bucket_stats = []
for bucket_stat in stats.bucket_stats:
c = {'packet_count': bucket_stat.packet_count,
'byte_count': bucket_stat.byte_count}
bucket_stats.append(c)
g = {'group_id': stats.group_id,
'ref_count': stats.ref_count,
'packet_count': stats.packet_count,
'byte_count': stats.byte_count,
'duration_sec': stats.duration_sec,
'duration_nsec': stats.duration_nsec,
'bucket_stats': bucket_stats}
groups.append(g)
groups = {str(dp.id): groups}
return groups
def get_group_features(dp, waiters):
ofp = dp.ofproto
type_convert = {ofp.OFPGT_ALL: 'ALL',
ofp.OFPGT_SELECT: 'SELECT',
ofp.OFPGT_INDIRECT: 'INDIRECT',
ofp.OFPGT_FF: 'FF'}
cap_convert = {ofp.OFPGFC_SELECT_WEIGHT: 'SELECT_WEIGHT',
ofp.OFPGFC_SELECT_LIVENESS: 'SELECT_LIVENESS',
ofp.OFPGFC_CHAINING: 'CHAINING',
ofp.OFPGFC_CHAINING_CHECKS: 'CHAINING_CHCEKS'}
act_convert = {ofp.OFPAT_OUTPUT: 'OUTPUT',
ofp.OFPAT_COPY_TTL_OUT: 'COPY_TTL_OUT',
ofp.OFPAT_COPY_TTL_IN: 'COPY_TTL_IN',
ofp.OFPAT_SET_MPLS_TTL: 'SET_MPLS_TTL',
ofp.OFPAT_DEC_MPLS_TTL: 'DEC_MPLS_TTL',
ofp.OFPAT_PUSH_VLAN: 'PUSH_VLAN',
ofp.OFPAT_POP_VLAN: 'POP_VLAN',
ofp.OFPAT_PUSH_MPLS: 'PUSH_MPLS',
ofp.OFPAT_POP_MPLS: 'POP_MPLS',
ofp.OFPAT_SET_QUEUE: 'SET_QUEUE',
ofp.OFPAT_GROUP: 'GROUP',
ofp.OFPAT_SET_NW_TTL: 'SET_NW_TTL',
ofp.OFPAT_DEC_NW_TTL: 'DEC_NW_TTL',
ofp.OFPAT_SET_FIELD: 'SET_FIELD',
ofp.OFPAT_PUSH_PBB: 'PUSH_PBB',
ofp.OFPAT_POP_PBB: 'POP_PBB'}
stats = dp.ofproto_parser.OFPGroupFeaturesStatsRequest(dp, 0)
msgs = []
send_stats_request(dp, stats, waiters, msgs)
features = []
for msg in msgs:
feature = msg.body
types = []
for k, v in type_convert.items():
if (1 << k) & feature.types:
types.append(v)
capabilities = []
for k, v in cap_convert.items():
if k & feature.capabilities:
capabilities.append(v)
max_groups = []
for k, v in type_convert.items():
max_groups.append({v: feature.max_groups[k]})
actions = []
for k1, v1 in type_convert.items():
acts = []
for k2, v2 in act_convert.items():
if (1 << k2) & feature.actions[k1]:
acts.append(v2)
actions.append({v1: acts})
f = {'types': types,
'capabilities': capabilities,
'max_groups': max_groups,
'actions': actions}
features.append(f)
features = {str(dp.id): features}
return features
def get_group_desc(dp, waiters):
type_convert = {dp.ofproto.OFPGT_ALL: 'ALL',
dp.ofproto.OFPGT_SELECT: 'SELECT',
dp.ofproto.OFPGT_INDIRECT: 'INDIRECT',
dp.ofproto.OFPGT_FF: 'FF'}
stats = dp.ofproto_parser.OFPGroupDescStatsRequest(dp, 0)
msgs = []
send_stats_request(dp, stats, waiters, msgs)
descs = []
for msg in msgs:
for stats in msg.body:
buckets = []
for bucket in stats.buckets:
actions = []
for action in bucket.actions:
actions.append(action_to_str(action))
b = {'weight': bucket.weight,
'watch_port': bucket.watch_port,
'watch_group': bucket.watch_group,
'actions': actions}
buckets.append(b)
d = {'type': type_convert.get(stats.type),
'group_id': stats.group_id,
'buckets': buckets}
descs.append(d)
descs = {str(dp.id): descs}
return descs
def mod_flow_entry(dp, flow, cmd):
cookie = int(flow.get('cookie', 0))
cookie_mask = int(flow.get('cookie_mask', 0))
@ -643,6 +784,38 @@ def mod_meter_entry(dp, flow, cmd):
dp.send_msg(meter_mod)
def mod_group_entry(dp, group, cmd):
type_convert = {'ALL': dp.ofproto.OFPGT_ALL,
'SELECT': dp.ofproto.OFPGT_SELECT,
'INDIRECT': dp.ofproto.OFPGT_INDIRECT,
'FF': dp.ofproto.OFPGT_FF}
type_ = type_convert.get(group.get('type'))
if not type_:
LOG.debug('Unknown type: %s', group.get('type'))
group_id = int(group.get('group_id', 0))
buckets = []
for bucket in group.get('buckets', []):
weight = int(bucket.get('weight', 0))
watch_port = int(bucket.get('watch_port', dp.ofproto.OFPP_ANY))
watch_group = int(bucket.get('watch_group', dp.ofproto.OFPG_ANY))
actions = []
for dic in bucket.get('actions', []):
action = to_action(dp, dic)
if action is not None:
actions.append(action)
buckets.append(dp.ofproto_parser.OFPBucket(
weight, watch_port, watch_group, actions))
group_mod = dp.ofproto_parser.OFPGroupMod(
dp, cmd, type_, group_id, buckets)
dp.send_msg(group_mod)
def send_experimenter(dp, exp):
experimenter = exp.get('experimenter', 0)
exp_type = exp.get('exp_type', 0)