backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / ofproto / ofproto_parser.py
1 # Copyright (C) 2011, 2012 Nippon Telegraph and Telephone Corporation.
2 # Copyright (C) 2011 Isaku Yamahata <yamahata at valinux co jp>
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 #    http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13 # implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 import six
18
19 import base64
20 import collections
21 import logging
22 import struct
23 import functools
24
25 from ryu import exception
26 from ryu import utils
27 from ryu.lib import stringify
28
29 from ryu.ofproto import ofproto_common
30
31 LOG = logging.getLogger('ryu.ofproto.ofproto_parser')
32
33 # This is merely for API compatibility on python2
34 if six.PY3:
35     buffer = bytes
36
37
38 def header(buf):
39     assert len(buf) >= ofproto_common.OFP_HEADER_SIZE
40     # LOG.debug('len %d bufsize %d', len(buf), ofproto.OFP_HEADER_SIZE)
41     return struct.unpack_from(ofproto_common.OFP_HEADER_PACK_STR,
42                               six.binary_type(buf))
43
44
45 _MSG_PARSERS = {}
46
47
48 def register_msg_parser(version):
49     def register(msg_parser):
50         _MSG_PARSERS[version] = msg_parser
51         return msg_parser
52     return register
53
54
55 def msg(datapath, version, msg_type, msg_len, xid, buf):
56     exp = None
57     try:
58         assert len(buf) >= msg_len
59     except AssertionError as e:
60         exp = e
61
62     msg_parser = _MSG_PARSERS.get(version)
63     if msg_parser is None:
64         raise exception.OFPUnknownVersion(version=version)
65
66     try:
67         msg = msg_parser(datapath, version, msg_type, msg_len, xid, buf)
68     except exception.OFPTruncatedMessage as e:
69         raise e
70     except:
71         LOG.exception(
72             'Encountered an error while parsing OpenFlow packet from switch. '
73             'This implies the switch sent a malformed OpenFlow packet. '
74             'version 0x%02x msg_type %d msg_len %d xid %d buf %s',
75             version, msg_type, msg_len, xid, utils.hex_array(buf))
76         msg = None
77     if exp:
78         raise exp
79     return msg
80
81
82 def create_list_of_base_attributes(f):
83     @functools.wraps(f)
84     def wrapper(self, *args, **kwargs):
85         ret = f(self, *args, **kwargs)
86         cls = self.__class__
87         # hasattr(cls, '_base_attributes') doesn't work because super class
88         # may already have the attribute.
89         if '_base_attributes' not in cls.__dict__:
90             cls._base_attributes = set(dir(self))
91         return ret
92     return wrapper
93
94
95 def ofp_msg_from_jsondict(dp, jsondict):
96     """
97     This function instanticates an appropriate OpenFlow message class
98     from the given JSON style dictionary.
99     The objects created by following two code fragments are equivalent.
100
101     Code A::
102
103         jsonstr = '{ "OFPSetConfig": { "flags": 0, "miss_send_len": 128 } }'
104         jsondict = json.loads(jsonstr)
105         o = ofp_msg_from_jsondict(dp, jsondict)
106
107     Code B::
108
109         o = dp.ofproto_parser.OFPSetConfig(flags=0, miss_send_len=128)
110
111     This function takes the following arguments.
112
113     ======== =======================================
114     Argument Description
115     ======== =======================================
116     dp       An instance of ryu.controller.Datapath.
117     jsondict A JSON style dict.
118     ======== =======================================
119     """
120     parser = dp.ofproto_parser
121     assert len(jsondict) == 1
122     for k, v in jsondict.items():
123         cls = getattr(parser, k)
124         assert issubclass(cls, MsgBase)
125         return cls.from_jsondict(v, datapath=dp)
126
127
128 def ofp_instruction_from_jsondict(dp, jsonlist, encap=True):
129     """
130     This function is intended to be used with
131     ryu.lib.ofctl_string.ofp_instruction_from_str.
132     It is very similar to ofp_msg_from_jsondict, but works on
133     a list of OFPInstructions/OFPActions. It also encapsulates
134     OFPAction into OFPInstructionActions, as >OF1.0 OFPFlowMod
135     requires that.
136
137     This function takes the following arguments.
138
139     ======== ==================================================
140     Argument Description
141     ======== ==================================================
142     dp       An instance of ryu.controller.Datapath.
143     jsonlist A list of JSON style dictionaries.
144     encap    Encapsulate OFPAction into OFPInstructionActions.
145              Must be false for OF10.
146     ======== ==================================================
147     """
148     proto = dp.ofproto
149     parser = dp.ofproto_parser
150     actions = []
151     result = []
152     for jsondict in jsonlist:
153         assert len(jsondict) == 1
154         k, v = list(jsondict.items())[0]
155         cls = getattr(parser, k)
156         if issubclass(cls, parser.OFPAction):
157             if encap:
158                 actions.append(cls.from_jsondict(v))
159                 continue
160         else:
161             ofpinst = getattr(parser, 'OFPInstruction', None)
162             if not ofpinst or not issubclass(cls, ofpinst):
163                 raise ValueError("Supplied jsondict is of wrong type: %s",
164                                  jsondict)
165         result.append(cls.from_jsondict(v))
166
167     if not encap:
168         return result
169
170     if actions:
171         # Although the OpenFlow spec says Apply Actions is executed first,
172         # let's place it in the head as a precaution.
173         result = [parser.OFPInstructionActions(
174             proto.OFPIT_APPLY_ACTIONS, actions)] + result
175     return result
176
177
178 class StringifyMixin(stringify.StringifyMixin):
179     _class_prefixes = ["OFP", "ONF", "MT", "NX"]
180
181     @classmethod
182     def cls_from_jsondict_key(cls, k):
183         obj_cls = super(StringifyMixin, cls).cls_from_jsondict_key(k)
184         return obj_cls
185
186
187 class MsgBase(StringifyMixin):
188     """
189     This is a base class for OpenFlow message classes.
190
191     An instance of this class has at least the following attributes.
192
193     ========= ==============================
194     Attribute Description
195     ========= ==============================
196     datapath  A ryu.controller.controller.Datapath instance for this message
197     version   OpenFlow protocol version
198     msg_type  Type of OpenFlow message
199     msg_len   Length of the message
200     xid       Transaction id
201     buf       Raw data
202     ========= ==============================
203     """
204
205     @create_list_of_base_attributes
206     def __init__(self, datapath):
207         super(MsgBase, self).__init__()
208         self.datapath = datapath
209         self.version = None
210         self.msg_type = None
211         self.msg_len = None
212         self.xid = None
213         self.buf = None
214
215     def set_headers(self, version, msg_type, msg_len, xid):
216         assert msg_type == self.cls_msg_type
217
218         self.version = version
219         self.msg_type = msg_type
220         self.msg_len = msg_len
221         self.xid = xid
222
223     def set_xid(self, xid):
224         assert self.xid is None
225         self.xid = xid
226
227     def set_buf(self, buf):
228         self.buf = buffer(buf)
229
230     def __str__(self):
231         def hexify(x):
232             return hex(x) if isinstance(x, six.integer_types) else x
233         buf = 'version=%s,msg_type=%s,msg_len=%s,xid=%s,' %\
234               (hexify(self.version), hexify(self.msg_type),
235                hexify(self.msg_len), hexify(self.xid))
236         return buf + StringifyMixin.__str__(self)
237
238     @classmethod
239     def parser(cls, datapath, version, msg_type, msg_len, xid, buf):
240         msg_ = cls(datapath)
241         msg_.set_headers(version, msg_type, msg_len, xid)
242         msg_.set_buf(buf)
243         return msg_
244
245     def _serialize_pre(self):
246         self.version = self.datapath.ofproto.OFP_VERSION
247         self.msg_type = self.cls_msg_type
248         self.buf = bytearray(self.datapath.ofproto.OFP_HEADER_SIZE)
249
250     def _serialize_header(self):
251         # buffer length is determined after trailing data is formated.
252         assert self.version is not None
253         assert self.msg_type is not None
254         assert self.buf is not None
255         assert len(self.buf) >= self.datapath.ofproto.OFP_HEADER_SIZE
256
257         self.msg_len = len(self.buf)
258         if self.xid is None:
259             self.xid = 0
260
261         struct.pack_into(self.datapath.ofproto.OFP_HEADER_PACK_STR,
262                          self.buf, 0,
263                          self.version, self.msg_type, self.msg_len, self.xid)
264
265     def _serialize_body(self):
266         pass
267
268     def serialize(self):
269         self._serialize_pre()
270         self._serialize_body()
271         self._serialize_header()
272
273
274 class MsgInMsgBase(MsgBase):
275     @classmethod
276     def _decode_value(cls, k, json_value, decode_string=base64.b64decode,
277                       **additional_args):
278         return cls._get_decoder(k, decode_string)(json_value,
279                                                   **additional_args)
280
281
282 def namedtuple(typename, fields, **kwargs):
283     class _namedtuple(StringifyMixin,
284                       collections.namedtuple(typename, fields, **kwargs)):
285         pass
286     return _namedtuple
287
288
289 def msg_str_attr(msg_, buf, attr_list=None):
290     if attr_list is None:
291         attr_list = stringify.obj_attrs(msg_)
292     for attr in attr_list:
293         val = getattr(msg_, attr, None)
294         if val is not None:
295             buf += ' %s %s' % (attr, val)
296
297     return buf