This commit adds a new method called ofp_instruction_from_str,
which takes an ovs-ofctl style action string and returns a list of
OFPInstructionActions. Currently only a few action strings are
understood.

Signed-off-by: IWAMOTO Toshihiro <iwam...@valinux.co.jp>
---
 ryu/exception.py                   |   4 ++
 ryu/ofproto/nicira_ext.py          |   4 ++
 ryu/ofproto/nx_actions.py          |  94 ++++++++++++++++++++++++++-
 ryu/ofproto/nx_match.py            |  39 +++++++++++
 ryu/ofproto/ofproto_parser.py      | 128 ++++++++++++++++++++++++++++++++++++-
 ryu/ofproto/ofproto_v1_0_parser.py |  11 ++++
 ryu/ofproto/ofproto_v1_3_parser.py |  11 ++++
 ryu/ofproto/ofproto_v1_4.py        |  18 ++++++
 ryu/ofproto/ofproto_v1_4_parser.py |  52 +++++++++++++++
 ryu/ofproto/ofproto_v1_5.py        |  19 ++++++
 ryu/ofproto/ofproto_v1_5_parser.py |  52 +++++++++++++++
 11 files changed, 430 insertions(+), 2 deletions(-)

diff --git a/ryu/exception.py b/ryu/exception.py
index 1be4ba1..93fba6c 100644
--- a/ryu/exception.py
+++ b/ryu/exception.py
@@ -52,6 +52,10 @@ class OFPTruncatedMessage(RyuException):
         super(OFPTruncatedMessage, self).__init__(msg, **kwargs)
 
 
+class OFPInvalidActionString(RyuException):
+    message = 'unable to parse: %(action_str)s'
+
+
 class NetworkNotFound(RyuException):
     message = 'no such network id %(network_id)s'
 
diff --git a/ryu/ofproto/nicira_ext.py b/ryu/ofproto/nicira_ext.py
index 416c625..0ac7d1b 100644
--- a/ryu/ofproto/nicira_ext.py
+++ b/ryu/ofproto/nicira_ext.py
@@ -708,3 +708,7 @@ oxm_types = [
     oxm_fields.NiciraExtended1('xxreg3', 114, type_desc.Int16),
 
 ]
+
+oxm_field_ofctl_aliases = {
+    'tun_src': 'tun_ipv4_src'
+}
diff --git a/ryu/ofproto/nx_actions.py b/ryu/ofproto/nx_actions.py
index 8921007..3954034 100644
--- a/ryu/ofproto/nx_actions.py
+++ b/ryu/ofproto/nx_actions.py
@@ -16,14 +16,19 @@
 
 import six
 
+import re
 import struct
 
+import ryu.exception
 from ryu import utils
 from ryu.lib import type_desc
 from ryu.ofproto import nicira_ext
+from ryu.ofproto import nx_match
 from ryu.ofproto import ofproto_common
+from ryu.lib.ofctl_utils import str_to_int
 from ryu.lib.pack_utils import msg_pack_into
-from ryu.ofproto.ofproto_parser import StringifyMixin
+from ryu.ofproto.ofproto_parser import StringifyMixin, \
+    ofp_instruction_from_str, tokenize_ofp_instruction_arg
 
 
 def generate(ofp_name, ofpp_name):
@@ -785,6 +790,7 @@ def generate(ofp_name, ofpp_name):
                           self.in_port)
             return data
 
+    @ofpp._register_ofctl_action_str('resubmit')
     class NXActionResubmitTable(NXAction):
         """
         Resubmit action
@@ -839,6 +845,26 @@ def generate(ofp_name, ofpp_name):
                           self.in_port, self.table_id)
             return data
 
+        @classmethod
+        def from_string(cls, dp, action):
+            arg = action[len("resubmit"):]
+            kwargs = {}
+            try:
+                if arg[0] == ':':
+                    kwargs['in_port'] = str_to_int(arg[1:])
+                elif arg[0] == '(' and arg[-1] == ')':
+                    in_port, table_id = arg[1:-1].split(',')
+                    if in_port:
+                        kwargs['in_port'] = str_to_int(in_port)
+                    if table_id:
+                        kwargs['table_id'] = str_to_int(table_id)
+                else:
+                    raise Exception
+                return cls(**kwargs)
+            except Exception:
+                raise ryu.exception.OFPInvalidActionString(
+                    action_str=action)
+
     class NXActionOutputReg(NXAction):
         """
         Add output action
@@ -2278,6 +2304,7 @@ def generate(ofp_name, ofpp_name):
                           self.fin_hard_timeout)
             return data
 
+    @ofpp._register_ofctl_action_str('conjunction')
     class NXActionConjunction(NXAction):
         """
         Conjunctive matches action
@@ -2341,6 +2368,21 @@ def generate(ofp_name, ofpp_name):
                           self.id)
             return data
 
+        @classmethod
+        def from_string(cls, dp, action_str):
+            try:
+                assert action_str.startswith('conjunction(')
+                assert action_str[-1] == ')'
+                args = action_str[len('conjunction('):-1].split(',')
+                assert len(args) == 2
+                id_ = str_to_int(args[0])
+                clauses = list(map(str_to_int, args[1].split('/')))
+                assert len(clauses) == 2
+                return cls(clauses[0] - 1, clauses[1], id_)
+            except Exception:
+                raise ryu.exception.OFPInvalidActionString(
+                    action_str=action_str)
+
     class NXActionMultipath(NXAction):
         """
         Select multipath link action
@@ -2641,6 +2683,7 @@ def generate(ofp_name, ofpp_name):
                 algorithm, fields, basis, slave_type, n_slaves,
                 ofs_nbits, dst, slaves)
 
+    @ofpp._register_ofctl_action_str('ct')
     class NXActionCT(NXAction):
         """
         Pass traffic to the connection tracker action
@@ -2766,6 +2809,55 @@ def generate(ofp_name, ofpp_name):
                 a.serialize(data, len(data))
             return data
 
+        @classmethod
+        def from_string(cls, dp, action_str):
+            str_to_port = {'ftp': 21, 'tftp': 69}
+            flags = 0
+            zone_src = ""
+            zone_ofs_nbits = 0
+            recirc_table = nicira_ext.NX_CT_RECIRC_NONE
+            alg = 0
+            ct_actions = []
+
+            if len(action_str) > 2:
+                if (not action_str.startswith('ct(') or
+                        action_str[-1] != ')'):
+                    raise ryu.exception.OFPInvalidActionString(
+                        action_str=action_str)
+                rest = tokenize_ofp_instruction_arg(action_str[len('ct('):-1])
+            else:
+                rest = []
+            for arg in rest:
+                if arg == 'commit':
+                    flags |= nicira_ext.NX_CT_F_COMMIT
+                    rest = rest[len('commit'):]
+                elif arg == 'force':
+                    flags |= nicira_ext.NX_CT_F_FORCE
+                elif arg.startswith('exec('):
+                    ct_actions = ofp_instruction_from_str(
+                        dp, arg[len('exec('):-1], encap=False)
+                else:
+                    try:
+                        k, v = arg.split('=', 1)
+                        if k == 'table':
+                            recirc_table = str_to_int(v)
+                        elif k == 'zone':
+                            m = re.search('\[(\d*)\.\.(\d*)\]', v)
+                            if m:
+                                zone_ofs_nbits = nicira_ext.ofs_nbits(
+                                    int(m.group(1)), int(m.group(2)))
+                                zone_src = nx_match.nxm_field_name_to_ryu(
+                                    v[:m.start(0)])
+                            else:
+                                zone_ofs_nbits = str_to_int(v)
+                        elif k == 'alg':
+                            alg = str_to_port[arg[len('alg='):]]
+                    except Exception:
+                        raise ryu.exception.OFPInvalidActionString(
+                            action_str=action_str)
+            return cls(flags, zone_src, zone_ofs_nbits, recirc_table,
+                       alg, ct_actions)
+
     class NXActionNAT(NXAction):
         """
         Network address translation action
diff --git a/ryu/ofproto/nx_match.py b/ryu/ofproto/nx_match.py
index 77803cc..081b7af 100644
--- a/ryu/ofproto/nx_match.py
+++ b/ryu/ofproto/nx_match.py
@@ -1226,3 +1226,42 @@ class NXMatch(object):
         msg_pack_into(ofproto_v1_0.NXM_HEADER_PACK_STRING,
                       buf, offset, self.header)
         return struct.calcsize(ofproto_v1_0.NXM_HEADER_PACK_STRING)
+
+
+_NXM_FIELD_MAP = dict([(key, key + '_nxm') for key in [
+    'arp_sha', 'arp_tha', 'ipv6_src', 'ipv6_dst',
+    'icmpv6_type', 'icmpv6_code', 'ip_ecn', 'tcp_flags']])
+_NXM_FIELD_MAP.update({
+    'tun_id': 'tunnel_id_nxm', 'ip_ttl': 'nw_ttl'})
+
+_NXM_OF_FIELD_MAP = dict([(key, key + '_nxm') for key in [
+    'in_port', 'eth_dst', 'eth_src', 'eth_type', 'ip_proto',
+    'tcp_src', 'tcp_dst', 'udp_src', 'udp_dst',
+    'arp_op', 'arp_spa', 'arp_tpa']])
+_NXM_OF_FIELD_MAP.update({
+    'ip_src': 'ipv4_src_nxm', 'ip_dst': 'ipv4_dst_nxm',
+    'icmp_type': 'icmpv4_type_nxm', 'icmp_code': 'icmpv4_code_nxm'})
+
+
+def nxm_field_name_to_ryu(field):
+    """
+    Convert an ovs-ofctl style NXM_/OXM_ field name to
+    a ryu match field name.
+    """
+    prefix = field[:7]
+    field = field[7:].lower()
+    mapped_result = None
+
+    # TODO: handle "_W" suffix.
+    if prefix == 'NXM_NX_':
+        mapped_result = _NXM_FIELD_MAP.get(field)
+    elif field.startswith("NXM_OF_"):
+        mapped_result = _NXM_OF_FIELD_MAP.get(field)
+    elif field.startswith("OXM_OF_"):
+        # no mapping needed
+        pass
+    else:
+        raise ValueError
+    if mapped_result is not None:
+        return mapped_result
+    return field
diff --git a/ryu/ofproto/ofproto_parser.py b/ryu/ofproto/ofproto_parser.py
index b68c538..b51526c 100644
--- a/ryu/ofproto/ofproto_parser.py
+++ b/ryu/ofproto/ofproto_parser.py
@@ -18,9 +18,10 @@ import six
 
 import base64
 import collections
+import functools
 import logging
+import re
 import struct
-import functools
 
 from ryu import exception
 from ryu import utils
@@ -125,6 +126,131 @@ def ofp_msg_from_jsondict(dp, jsondict):
         return cls.from_jsondict(v, datapath=dp)
 
 
+def ofp_instruction_from_str(dp, action_str, encap=True):
+    """
+    Parse an ovs-ofctl style action string and return a list of
+    OFPInstructionActions.
+
+    Please note that this is for making transition from ovs-ofctl
+    easier. Please consider using OFPAction constructors when writing
+    new codes.
+
+    This function takes the following arguments.
+
+    ========== =================================================
+    Argument   Description
+    ========== =================================================
+    dp         An instance of ryu.controller.Datapath.
+    action_str An action string.
+    encap      Encapsulate OFPAction into OFPInstructionActions.
+               Must be false for OF10 and nested actions.
+    ========== =================================================
+    """
+    proto = dp.ofproto
+    parser = dp.ofproto_parser
+    action_re = re.compile("([a-z_]+)(\([^)]*\)|[^a-z_,()][^,()]*)*")
+    result = []
+    while len(action_str):
+        m = action_re.match(action_str)
+        if not m:
+            raise exception.OFPInvalidActionString(action_str=action_str)
+        action_name = m.group(1)
+        this_action = m.group(0)
+        paren_level = this_action.count('(') - this_action.count(')')
+        assert paren_level >= 0
+        try:
+            # Parens can be nested. Look for as many ')'s as '('s.
+            if paren_level > 0:
+                this_action, rest = _tokenize_paren_block(action_str, m.end(0))
+            else:
+                rest = action_str[m.end(0):]
+            if len(rest):
+                assert rest[0] == ','
+                rest = rest[1:]
+        except Exception:
+            raise exception.OFPInvalidActionString(action_str=action_str)
+        if action_name == 'drop':
+            assert this_action == 'drop'
+            assert len(result) == 0 and rest == ''
+            return []
+        result.append(parser._ACTION_STRINGS[action_name].from_string(
+            dp, this_action))
+        action_str = rest
+
+    if not encap:
+        return result
+    # If any of result is OFPAction, it must be encapsulated
+    insts = []
+    actions = []
+    result.append(None)  # sentinel
+    for act_or_inst in result:
+        if isinstance(act_or_inst, parser.OFPAction):
+            actions.append(act_or_inst)
+        else:
+            if actions:
+                insts.append(parser.OFPInstructionActions(
+                    proto.OFPIT_APPLY_ACTIONS, actions))
+                actions = []
+            if act_or_inst is not None:
+                insts.append(act_or_inst)
+    return insts
+
+
+def _tokenize_paren_block(string, pos):
+    paren_re = re.compile("[()]")
+    paren_level = string[:pos].count('(') - string[:pos].count(')')
+    while paren_level > 0:
+        m = paren_re.search(string[pos:])
+        if m.group(0) == '(':
+            paren_level += 1
+        else:
+            paren_level -= 1
+        pos += m.end(0)
+    return string[:pos], string[pos:]
+
+
+def tokenize_ofp_instruction_arg(arg):
+    """
+    Tokenize an argument portion of ovs-ofctl style action string.
+    """
+    arg_re = re.compile("[^,()]*")
+    try:
+        rest = arg
+        result = []
+        while len(rest):
+            m = arg_re.match(rest)
+            if m.end(0) == len(rest):
+                result.append(rest)
+                return result
+            if rest[m.end(0)] == '(':
+                this_block, rest = _tokenize_paren_block(
+                    rest, m.end(0) + 1)
+                result.append(this_block)
+            elif rest[m.end(0)] == ',':
+                result.append(m.group(0))
+                rest = rest[m.end(0):]
+            else:  # is ')'
+                raise Exception
+            if len(rest):
+                assert rest[0] == ','
+                rest = rest[1:]
+        return result
+    except Exception:
+        raise ryu.exception.OFPInvalidActionString(action_str=arg)
+
+
+def ofp_ofctl_field_name_to_ryu(ofp, field):
+    """Convert an ovs-ofctl field name to ryu equivalent."""
+    mapped = ofp.oxm_field_ofctl_aliases.get(field)
+    if mapped:
+        return mapped
+    if field.endswith("_dst"):
+        mapped = ofp.oxm_field_ofctl_aliases.get(field[:-3] + "src")
+        if mapped:
+            return mapped[:-3] + "dst"
+    return field
+
+
 class StringifyMixin(stringify.StringifyMixin):
     _class_prefixes = ["OFP", "ONF", "MT", "NX"]
 
diff --git a/ryu/ofproto/ofproto_v1_0_parser.py 
b/ryu/ofproto/ofproto_v1_0_parser.py
index 1c10bda..8a21949 100644
--- a/ryu/ofproto/ofproto_v1_0_parser.py
+++ b/ryu/ofproto/ofproto_v1_0_parser.py
@@ -40,6 +40,7 @@ from ryu import utils
 import logging
 LOG = logging.getLogger('ryu.ofproto.ofproto_v1_0_parser')
 
+_ACTION_STRINGS = {}
 _MSG_PARSERS = {}
 
 
@@ -59,6 +60,16 @@ def _register_parser(cls):
     return cls
 
 
+def _register_ofctl_action_str(action_str):
+    """class decorator to register ovs-ofctl style action string"""
+    assert action_str not in _ACTION_STRINGS
+
+    def wraps(cls):
+        _ACTION_STRINGS[action_str] = cls
+        return cls
+    return wraps
+
+
 @ofproto_parser.register_msg_parser(ofproto.OFP_VERSION)
 def msg_parser(datapath, version, msg_type, msg_len, xid, buf):
     parser = _MSG_PARSERS.get(msg_type)
diff --git a/ryu/ofproto/ofproto_v1_3_parser.py 
b/ryu/ofproto/ofproto_v1_3_parser.py
index c298c99..b92540e 100644
--- a/ryu/ofproto/ofproto_v1_3_parser.py
+++ b/ryu/ofproto/ofproto_v1_3_parser.py
@@ -61,6 +61,7 @@ from ryu.ofproto import ofproto_v1_3 as ofproto
 import logging
 LOG = logging.getLogger('ryu.ofproto.ofproto_v1_3_parser')
 
+_ACTION_STRINGS = {}
 _MSG_PARSERS = {}
 
 
@@ -79,6 +80,16 @@ def _register_parser(cls):
     return cls
 
 
+def _register_ofctl_action_str(action_str):
+    """class decorator to register ovs-ofctl style action string"""
+    assert action_str not in _ACTION_STRINGS
+
+    def wraps(cls):
+        _ACTION_STRINGS[action_str] = cls
+        return cls
+    return wraps
+
+
 @ofproto_parser.register_msg_parser(ofproto.OFP_VERSION)
 def msg_parser(datapath, version, msg_type, msg_len, xid, buf):
     parser = _MSG_PARSERS.get(msg_type)
diff --git a/ryu/ofproto/ofproto_v1_4.py b/ryu/ofproto/ofproto_v1_4.py
index d1c4822..7aa8cbe 100644
--- a/ryu/ofproto/ofproto_v1_4.py
+++ b/ryu/ofproto/ofproto_v1_4.py
@@ -400,6 +400,24 @@ oxm_types = [
 
 oxm_fields.generate(__name__)
 
+oxm_field_ofctl_aliases = {
+    'tun_id': 'tunnel_id',
+    'in_port': 'in_port_nxm',
+    'in_port_oxm': 'in_port',
+    'dl_src': 'eth_src',
+    'dl_type': 'eth_type',
+    'nw_src': 'ipv4_src',
+    'ip_src': 'ipv4_src',
+    'nw_proto': 'ip_proto',
+    'nw_ecn': 'ip_ecn',
+    'tp_src': 'tcp_src',
+    'icmp_type': 'icmpv4_type',
+    'icmp_code': 'icmpv4_code',
+    'nd_target': 'ipv6_nd_target',
+    'nd_sll': 'ipv6_nd_sll',
+    'nd_tll': 'ipv6_nd_tll'
+}
+oxm_field_ofctl_aliases.update(nicira_ext.oxm_field_ofctl_aliases)
 
 # enum ofp_action_type
 OFPAT_OUTPUT = 0            # Output to switch port.
diff --git a/ryu/ofproto/ofproto_v1_4_parser.py 
b/ryu/ofproto/ofproto_v1_4_parser.py
index 31379c8..d228fdf 100644
--- a/ryu/ofproto/ofproto_v1_4_parser.py
+++ b/ryu/ofproto/ofproto_v1_4_parser.py
@@ -24,6 +24,7 @@ import base64
 import six
 
 from ryu.lib import addrconv
+from ryu.lib.ofctl_utils import str_to_int
 from ryu.lib.pack_utils import msg_pack_into
 from ryu.lib.packet import packet
 from ryu import utils
@@ -34,6 +35,7 @@ from ryu.ofproto import ofproto_parser
 from ryu.ofproto import ofproto_common
 from ryu.ofproto import ofproto_v1_4 as ofproto
 
+_ACTION_STRINGS = {}
 _MSG_PARSERS = {}
 
 
@@ -52,6 +54,16 @@ def _register_parser(cls):
     return cls
 
 
+def _register_ofctl_action_str(action_str):
+    """class decorator to register ovs-ofctl style action string"""
+    assert action_str not in _ACTION_STRINGS
+
+    def wraps(cls):
+        _ACTION_STRINGS[action_str] = cls
+        return cls
+    return wraps
+
+
 @ofproto_parser.register_msg_parser(ofproto.OFP_VERSION)
 def msg_parser(datapath, version, msg_type, msg_len, xid, buf):
     parser = _MSG_PARSERS.get(msg_type)
@@ -4399,6 +4411,7 @@ class OFPInstruction(StringifyMixin):
         return cls_.parser(buf, offset)
 
 
+@_register_ofctl_action_str('goto_table')
 @OFPInstruction.register_instruction_type([ofproto.OFPIT_GOTO_TABLE])
 class OFPInstructionGotoTable(OFPInstruction):
     """
@@ -4429,6 +4442,12 @@ class OFPInstructionGotoTable(OFPInstruction):
         msg_pack_into(ofproto.OFP_INSTRUCTION_GOTO_TABLE_PACK_STR,
                       buf, offset, self.type, self.len, self.table_id)
 
+    @classmethod
+    def from_string(cls, dp, action_str):
+        assert action_str.startswith('goto_table:')
+        table_id = str_to_int(action_str[len('goto_table:'):])
+        return cls(table_id)
+
 
 @OFPInstruction.register_instruction_type([ofproto.OFPIT_WRITE_METADATA])
 class OFPInstructionWriteMetadata(OFPInstruction):
@@ -4595,6 +4614,8 @@ class OFPAction(OFPActionHeader):
         return cls_.parser(buf, offset)
 
 
+@_register_ofctl_action_str('normal')
+@_register_ofctl_action_str('output')
 @OFPAction.register_action_type(ofproto.OFPAT_OUTPUT,
                                 ofproto.OFP_ACTION_OUTPUT_SIZE)
 class OFPActionOutput(OFPAction):
@@ -4626,6 +4647,15 @@ class OFPActionOutput(OFPAction):
         msg_pack_into(ofproto.OFP_ACTION_OUTPUT_PACK_STR, buf,
                       offset, self.type, self.len, self.port, self.max_len)
 
+    @classmethod
+    def from_string(cls, dp, action_str):
+        if action_str == 'normal':
+            port = ofproto.OFPP_NORMAL
+        else:
+            assert action_str.startswith('output:')
+            port = str_to_int(action_str[len('output:'):])
+        return cls(port)
+
 
 @OFPAction.register_action_type(ofproto.OFPAT_GROUP,
                                 ofproto.OFP_ACTION_GROUP_SIZE)
@@ -4876,6 +4906,7 @@ class OFPActionPushMpls(OFPAction):
                       self.type, self.len, self.ethertype)
 
 
+@_register_ofctl_action_str('pop_vlan')
 @OFPAction.register_action_type(ofproto.OFPAT_POP_VLAN,
                                 ofproto.OFP_ACTION_HEADER_SIZE)
 class OFPActionPopVlan(OFPAction):
@@ -4893,6 +4924,10 @@ class OFPActionPopVlan(OFPAction):
             ofproto.OFP_ACTION_HEADER_PACK_STR, buf, offset)
         return cls()
 
+    @classmethod
+    def from_string(cls, dp, action):
+        return cls()
+
 
 @OFPAction.register_action_type(ofproto.OFPAT_POP_MPLS,
                                 ofproto.OFP_ACTION_POP_MPLS_SIZE)
@@ -4917,6 +4952,7 @@ class OFPActionPopMpls(OFPAction):
                       self.type, self.len, self.ethertype)
 
 
+@_register_ofctl_action_str('set_field')
 @OFPAction.register_action_type(ofproto.OFPAT_SET_FIELD,
                                 ofproto.OFP_ACTION_SET_FIELD_SIZE)
 class OFPActionSetField(OFPAction):
@@ -4976,6 +5012,22 @@ class OFPActionSetField(OFPAction):
     def stringify_attrs(self):
         yield (self.key, self.value)
 
+    @classmethod
+    def from_string(cls, dp, action_str):
+        try:
+            assert action_str.startswith("set_field:")
+            value, key = action_str[len("set_field:"):].split("->", 1)
+            key = ofproto_parser.ofp_ofctl_field_name_to_ryu(
+                ofproto, key)
+            m = value.find('/')
+            if m >= 0:
+                value = (str_to_int(value[:m]), str_to_int(value[m + 1:]))
+            else:
+                value = str_to_int(value)
+        except Exception:
+            raise exception.OFPInvalidActionString(action_str=action_str)
+        return cls(**{key: value})
+
 
 @OFPAction.register_action_type(ofproto.OFPAT_PUSH_PBB,
                                 ofproto.OFP_ACTION_PUSH_SIZE)
diff --git a/ryu/ofproto/ofproto_v1_5.py b/ryu/ofproto/ofproto_v1_5.py
index 8353184..5b8f60f 100644
--- a/ryu/ofproto/ofproto_v1_5.py
+++ b/ryu/ofproto/ofproto_v1_5.py
@@ -435,6 +435,25 @@ oxm_types = [
 
 oxm_fields.generate(__name__)
 
+oxm_field_ofctl_aliases = {
+    'tun_id': 'tunnel_id',
+    'in_port': 'in_port_nxm',
+    'in_port_oxm': 'in_port',
+    'dl_src': 'eth_src',
+    'dl_type': 'eth_type',
+    'nw_src': 'ipv4_src',
+    'ip_src': 'ipv4_src',
+    'nw_proto': 'ip_proto',
+    'nw_ecn': 'ip_ecn',
+    'tp_src': 'tcp_src',
+    'icmp_type': 'icmpv4_type',
+    'icmp_code': 'icmpv4_code',
+    'nd_target': 'ipv6_nd_target',
+    'nd_sll': 'ipv6_nd_sll',
+    'nd_tll': 'ipv6_nd_tll'
+}
+oxm_field_ofctl_aliases.update(nicira_ext.oxm_field_ofctl_aliases)
+
 # struct ofp_stats
 _OFP_STATS_PACK_STR = 'HH4x'
 OFP_STATS_PACK_STR = '!' + _OFP_STATS_PACK_STR
diff --git a/ryu/ofproto/ofproto_v1_5_parser.py 
b/ryu/ofproto/ofproto_v1_5_parser.py
index be2e862..99d94ce 100644
--- a/ryu/ofproto/ofproto_v1_5_parser.py
+++ b/ryu/ofproto/ofproto_v1_5_parser.py
@@ -24,6 +24,7 @@ import base64
 import six
 
 from ryu.lib import addrconv
+from ryu.lib.ofctl_utils import str_to_int
 from ryu.lib.pack_utils import msg_pack_into
 from ryu.lib.packet import packet
 from ryu import exception
@@ -35,6 +36,7 @@ from ryu.ofproto import ofproto_parser
 from ryu.ofproto import ofproto_common
 from ryu.ofproto import ofproto_v1_5 as ofproto
 
+_ACTION_STRINGS = {}
 _MSG_PARSERS = {}
 
 
@@ -53,6 +55,16 @@ def _register_parser(cls):
     return cls
 
 
+def _register_ofctl_action_str(action_str):
+    """class decorator to register ovs-ofctl style action string"""
+    assert action_str not in _ACTION_STRINGS
+
+    def wraps(cls):
+        _ACTION_STRINGS[action_str] = cls
+        return cls
+    return wraps
+
+
 @ofproto_parser.register_msg_parser(ofproto.OFP_VERSION)
 def msg_parser(datapath, version, msg_type, msg_len, xid, buf):
     parser = _MSG_PARSERS.get(msg_type)
@@ -5256,6 +5268,7 @@ class OFPInstruction(StringifyMixin):
         return cls_.parser(buf, offset)
 
 
+@_register_ofctl_action_str('goto_table')
 @OFPInstruction.register_instruction_type([ofproto.OFPIT_GOTO_TABLE])
 class OFPInstructionGotoTable(OFPInstruction):
     """
@@ -5286,6 +5299,12 @@ class OFPInstructionGotoTable(OFPInstruction):
         msg_pack_into(ofproto.OFP_INSTRUCTION_GOTO_TABLE_PACK_STR,
                       buf, offset, self.type, self.len, self.table_id)
 
+    @classmethod
+    def from_string(cls, dp, action_str):
+        assert action_str.startswith('goto_table:')
+        table_id = str_to_int(action_str[len('goto_table:'):])
+        return cls(table_id)
+
 
 @OFPInstruction.register_instruction_type([ofproto.OFPIT_WRITE_METADATA])
 class OFPInstructionWriteMetadata(OFPInstruction):
@@ -5466,6 +5485,8 @@ class OFPAction(OFPActionHeader):
         return cls_.parser(buf, offset)
 
 
+@_register_ofctl_action_str('normal')
+@_register_ofctl_action_str('output')
 @OFPAction.register_action_type(ofproto.OFPAT_OUTPUT,
                                 ofproto.OFP_ACTION_OUTPUT_SIZE)
 class OFPActionOutput(OFPAction):
@@ -5497,6 +5518,15 @@ class OFPActionOutput(OFPAction):
         msg_pack_into(ofproto.OFP_ACTION_OUTPUT_PACK_STR, buf,
                       offset, self.type, self.len, self.port, self.max_len)
 
+    @classmethod
+    def from_string(cls, dp, action_str):
+        if action_str == 'normal':
+            port = ofproto.OFPP_NORMAL
+        else:
+            assert action_str.startswith('output:')
+            port = str_to_int(action_str[len('output:'):])
+        return cls(port)
+
 
 @OFPAction.register_action_type(ofproto.OFPAT_GROUP,
                                 ofproto.OFP_ACTION_GROUP_SIZE)
@@ -5747,6 +5777,7 @@ class OFPActionPushMpls(OFPAction):
                       self.type, self.len, self.ethertype)
 
 
+@_register_ofctl_action_str('pop_vlan')
 @OFPAction.register_action_type(ofproto.OFPAT_POP_VLAN,
                                 ofproto.OFP_ACTION_HEADER_SIZE)
 class OFPActionPopVlan(OFPAction):
@@ -5764,6 +5795,10 @@ class OFPActionPopVlan(OFPAction):
             ofproto.OFP_ACTION_HEADER_PACK_STR, buf, offset)
         return cls()
 
+    @classmethod
+    def from_string(cls, dp, action):
+        return cls()
+
 
 @OFPAction.register_action_type(ofproto.OFPAT_POP_MPLS,
                                 ofproto.OFP_ACTION_POP_MPLS_SIZE)
@@ -5788,6 +5823,7 @@ class OFPActionPopMpls(OFPAction):
                       self.type, self.len, self.ethertype)
 
 
+@_register_ofctl_action_str('set_field')
 @OFPAction.register_action_type(ofproto.OFPAT_SET_FIELD,
                                 ofproto.OFP_ACTION_SET_FIELD_SIZE)
 class OFPActionSetField(OFPAction):
@@ -5849,6 +5885,22 @@ class OFPActionSetField(OFPAction):
     def stringify_attrs(self):
         yield (self.key, self.value)
 
+    @classmethod
+    def from_string(cls, dp, action_str):
+        try:
+            assert action_str.startswith("set_field:")
+            value, key = action_str[len("set_field:"):].split("->", 1)
+            key = ofproto_parser.ofp_ofctl_field_name_to_ryu(
+                ofproto, key)
+            m = value.find('/')
+            if m >= 0:
+                value = (str_to_int(value[:m]), str_to_int(value[m + 1:]))
+            else:
+                value = str_to_int(value)
+        except Exception:
+            raise exception.OFPInvalidActionString(action_str=action_str)
+        return cls(**{key: value})
+
 
 @OFPAction.register_action_type(ofproto.OFPAT_PUSH_PBB,
                                 ofproto.OFP_ACTION_PUSH_SIZE)
-- 
2.1.4


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Ryu-devel mailing list
Ryu-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/ryu-devel

Reply via email to