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