From 172d9702ac9f28ecaadf338a12d8baeaf1590df1 Mon Sep 17 00:00:00 2001 From: Satoshi Kobayashi Date: Mon, 6 Apr 2015 14:14:17 +0900 Subject: [PATCH] Show warning when Datapath#ports is read Datapath#ports is kept for compatibility with the previous openflow versions (< 1.3). Datapath#ports is not updated when received EventOFPPortStatus. This behavior may confuse users. Wherefore, showing warning message when the user accesses to Datapath#ports with the currently openflow versions (>= 1.3) is friendly. Signed-off-by: Satoshi Kobayashi Signed-off-by: FUJITA Tomonori --- ryu/controller/controller.py | 22 +++++- ryu/controller/ofp_handler.py | 7 +- ryu/tests/unit/controller/__init__.py | 0 ryu/tests/unit/controller/test_controller.py | 83 ++++++++++++++++++++ 4 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 ryu/tests/unit/controller/__init__.py create mode 100644 ryu/tests/unit/controller/test_controller.py diff --git a/ryu/controller/controller.py b/ryu/controller/controller.py index 23418f5f..225c21f4 100644 --- a/ryu/controller/controller.py +++ b/ryu/controller/controller.py @@ -31,6 +31,7 @@ import traceback import random import ssl from socket import IPPROTO_TCP, TCP_NODELAY +import warnings import ryu.base.app_manager @@ -120,11 +121,30 @@ class Datapath(ofproto_protocol.ProtocolDesc): self.xid = random.randint(0, self.ofproto.MAX_XID) self.id = None # datapath_id is unknown yet - self.ports = None + self._ports = None self.flow_format = ofproto_v1_0.NXFF_OPENFLOW10 self.ofp_brick = ryu.base.app_manager.lookup_service_brick('ofp_event') self.set_state(handler.HANDSHAKE_DISPATCHER) + def _get_ports(self): + if (self.ofproto_parser is not None and + self.ofproto_parser.ofproto.OFP_VERSION >= 0x04): + message = ( + 'Datapath#ports is kept for compatibility with the previous ' + 'openflow versions (< 1.3). ' + 'This not be updated by EventOFPPortStatus message. ' + 'If you want to be updated, you can use ' + '\'ryu.controller.dpset\' or \'ryu.topology.switches\'.' + ) + warnings.warn(message, stacklevel=2) + return self._ports + + def _set_ports(self, ports): + self._ports = ports + + # To show warning when Datapath#ports is read + ports = property(_get_ports, _set_ports) + def close(self): self.set_state(handler.DEAD_DISPATCHER) diff --git a/ryu/controller/ofp_handler.py b/ryu/controller/ofp_handler.py index 95f9a12d..553e9f43 100644 --- a/ryu/controller/ofp_handler.py +++ b/ryu/controller/ofp_handler.py @@ -20,6 +20,7 @@ Basic OpenFlow handling including negotiation. import itertools import logging +import warnings import ryu.base.app_manager @@ -224,8 +225,10 @@ class OFPHandler(ryu.base.app_manager.RyuApp): def multipart_reply_handler(self, ev): msg = ev.msg datapath = msg.datapath - for port in msg.body: - datapath.ports[port.port_no] = port + with warnings.catch_warnings(): + warnings.simplefilter('ignore') + for port in msg.body: + datapath.ports[port.port_no] = port if msg.flags & datapath.ofproto.OFPMPF_REPLY_MORE: return diff --git a/ryu/tests/unit/controller/__init__.py b/ryu/tests/unit/controller/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ryu/tests/unit/controller/test_controller.py b/ryu/tests/unit/controller/test_controller.py new file mode 100644 index 00000000..ca5a64ae --- /dev/null +++ b/ryu/tests/unit/controller/test_controller.py @@ -0,0 +1,83 @@ +# Copyright (C) 2015 Stratosphere Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +try: + import mock # Python 2 +except ImportError: + from unittest import mock # Python 3 + +import warnings +import unittest +import logging + +import nose +from nose.tools import assert_equal +from nose.tools import assert_true + +from ryu.base import app_manager # To suppress cyclic import +from ryu.controller import controller +from ryu.ofproto import ofproto_v1_3_parser +from ryu.ofproto import ofproto_v1_2_parser +from ryu.ofproto import ofproto_v1_0_parser + + +LOG = logging.getLogger('test_controller') + + +class Test_Datapath(unittest.TestCase): + + """ Test case for Datapath + """ + + def _test_ports_accessibility(self, ofproto_parser, msgs_len): + with mock.patch('ryu.controller.controller.Datapath.set_state'): + + # Ignore warnings + with warnings.catch_warnings(record=True) as msgs: + warnings.simplefilter('always') + + # Test target + sock_mock = mock.Mock() + addr_mock = mock.Mock() + dp = controller.Datapath(sock_mock, addr_mock) + dp.ofproto_parser = ofproto_parser + + # Create + dp.ports = {} + + # Update + port_mock = mock.Mock() + dp.ports[0] = port_mock + + # Read & Delete + del dp.ports[0] + + assert_equal(len(msgs), msgs_len) + for msg in msgs: + assert_true(issubclass(msg.category, UserWarning)) + + def test_ports_accessibility_v13(self): + self._test_ports_accessibility(ofproto_v1_3_parser, 2) + + def test_ports_accessibility_v12(self): + self._test_ports_accessibility(ofproto_v1_2_parser, 0) + + def test_ports_accessibility_v10(self): + self._test_ports_accessibility(ofproto_v1_0_parser, 0) + + +if __name__ == '__main__': + nose.main(argv=['nosetests', '-s', '-v'], defaultTest=__file__)