Passed
Pull Request — master (#123)
by
unknown
02:31
created

build.main   F

Complexity

Total Complexity 81

Size/Duplication

Total Lines 566
Duplicated Lines 0 %

Test Coverage

Coverage 91.93%

Importance

Changes 0
Metric Value
wmc 81
eloc 364
dl 0
loc 566
rs 2
c 0
b 0
f 0
ccs 262
cts 285
cp 0.9193

26 Methods

Rating   Name   Duplication   Size   Complexity  
A Main._request_flow_list() 0 13 3
A Main.setup() 0 9 1
A Main.execute() 0 14 4
D Main.handle_raw_in() 0 76 13
A Main.fail_negotiation() 0 20 1
A Main._new_port_stats() 0 12 1
A Main.handle_features_reply() 0 26 4
A Main.handle_multipart_reply() 0 20 5
A Main.emit_message_in() 0 13 4
A Main.handle_features_request_sent() 0 5 2
A Main.shutdown() 0 3 1
A Main.handle_echo_request() 0 18 1
A Main.on_handshake_completed_request_flow_list() 0 9 1
A Main._is_multipart_reply_ours() 0 7 3
A Main._negotiate() 0 31 3
A Main.handle_openflow_in_hello_failed() 0 7 1
B Main.update_port_status() 0 71 5
A Main.handle_queued_openflow_echo_reply() 0 9 2
A Main.send_features_request() 0 7 1
A Main.update_links() 0 37 3
A Main.emit_message_out() 0 4 2
A Main._send_specific_port_mod() 0 21 5
A Main.handle_stats_reply() 0 28 4
A Main._handle_multipart_flow_stats() 0 15 3
A Main._update_switch_flows() 0 5 1
A Main._handle_multipart_port_stats() 0 10 3

2 Functions

Rating   Name   Duplication   Size   Complexity  
A _get_version_from_header() 0 4 2
A _get_version_from_bitmask() 0 7 2

How to fix   Complexity   

Complexity

Complex classes like build.main often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
"""NApp responsible for the main OpenFlow basic operations."""
2
3 1
from pyof.foundation.exceptions import UnpackException
4 1
from pyof.foundation.network_types import Ethernet, EtherType
5 1
from pyof.utils import PYOF_VERSION_LIBS, unpack
6 1
from pyof.v0x01.common.header import Type
7 1
from pyof.v0x01.controller2switch.common import StatsType
8 1
from pyof.v0x04.controller2switch.common import MultipartType
9
10 1
from kytos.core import KytosEvent, KytosNApp, log
11 1
from kytos.core.connection import ConnectionState
12 1
from kytos.core.helpers import listen_to
13 1
from kytos.core.interface import Interface
14 1
from napps.kytos.of_core import settings
15 1
from napps.kytos.of_core.utils import (GenericHello, NegotiationException,
16
                                       emit_message_in, emit_message_out,
17
                                       of_slicer)
18 1
from napps.kytos.of_core.v0x01 import utils as of_core_v0x01_utils
19 1
from napps.kytos.of_core.v0x01.flow import Flow as Flow01
20 1
from napps.kytos.of_core.v0x04 import utils as of_core_v0x04_utils
21 1
from napps.kytos.of_core.v0x04.flow import Flow as Flow04
22
23
24 1
class Main(KytosNApp):
25
    """Main class of the NApp responsible for OpenFlow basic operations."""
26
27
    # Keep track of multiple multipart replies from our own request only.
28
    # Assume that all replies are received before setting a new xid.
29 1
    _multipart_replies_xids = {}
30 1
    _multipart_replies_flows = {}
31 1
    _multipart_replies_ports = {}
32
33 1
    def setup(self):
34
        """App initialization (used instead of ``__init__``).
35
36
        The setup method is automatically called by the run method.
37
        Users shouldn't call this method directly.
38
        """
39 1
        self.of_core_version_utils = {0x01: of_core_v0x01_utils,
40
                                      0x04: of_core_v0x04_utils}
41 1
        self.execute_as_loop(settings.STATS_INTERVAL)
42
43 1
    def execute(self):
44
        """Run once on app 'start' or in a loop.
45
46
        The execute method is called by the run method of KytosNApp class.
47
        Users shouldn't call this method directly.
48
        """
49 1
        for switch in self.controller.switches.values():
50 1
            if switch.is_connected():
51 1
                self._request_flow_list(switch)
52 1
                if settings.SEND_ECHO_REQUESTS:
53 1
                    version_utils = \
54
                        self.of_core_version_utils[switch.
55
                                                   connection.protocol.version]
56 1
                    version_utils.send_echo(self.controller, switch)
57
58 1
    def _request_flow_list(self, switch):
59
        """Send flow stats request to a connected switch."""
60 1
        of_version = switch.connection.protocol.version
61 1
        if of_version == 0x01:
62 1
            of_core_v0x01_utils.update_flow_list(self.controller, switch)
63 1
            of_core_v0x01_utils.request_port_stats(self.controller, switch)
64 1
        elif of_version == 0x04:
65 1
            xid_flows = of_core_v0x04_utils.update_flow_list(self.controller,
66
                                                             switch)
67 1
            xid_ports = of_core_v0x04_utils.request_port_stats(self.controller,
68
                                                               switch)
69 1
            self._multipart_replies_xids[switch.id] = {'flows': xid_flows,
70
                                                       'ports': xid_ports}
71
72 1
    @listen_to('kytos/of_core.v0x01.messages.in.ofpt_stats_reply')
73
    def handle_stats_reply(self, event):
74
        """Handle stats replies for v0x01 switches.
75
76
        Args:
77
            event (:class:`~kytos.core.events.KytosEvent):
78
                Event with ofpt_stats_reply in message.
79
        """
80 1
        switch = event.source.switch
81 1
        msg = event.content['message']
82 1
        if msg.body_type == StatsType.OFPST_FLOW:
83 1
            switch.flows = [Flow01.from_of_flow_stats(f, switch)
84
                            for f in msg.body]
85 1
            event_raw = KytosEvent(
86
                name='kytos/of_core.flow_stats.received',
87
                content={'switch': switch})
88 1
            self.controller.buffers.app.put(event_raw)
89 1
        elif msg.body_type == StatsType.OFPST_PORT:
90
            port_stats = [of_port_stats for of_port_stats in msg.body]
91
            port_stats_event = KytosEvent(
92
                name=f"kytos/of_core.port_stats",
93
                content={
94
                    'switch': switch,
95
                    'port_stats': port_stats
96
                    })
97
            self.controller.buffers.app.put(port_stats_event)
98 1
        elif msg.body_type == StatsType.OFPST_DESC:
99 1
            switch.update_description(msg.body)
100
101 1
    @listen_to('kytos/of_core.v0x0[14].messages.in.ofpt_features_reply')
102
    def handle_features_reply(self, event):
103
        """Handle kytos/of_core.messages.in.ofpt_features_reply event.
104
105
        This is the end of the Handshake workflow of the OpenFlow Protocol.
106
107
        Args:
108
            event (KytosEvent): Event with features reply message.
109
        """
110 1
        connection = event.source
111 1
        version_utils = self.of_core_version_utils[connection.protocol.version]
112 1
        switch = version_utils.handle_features_reply(self.controller, event)
113
114 1
        if (connection.is_during_setup() and
115
                connection.protocol.state == 'waiting_features_reply'):
116 1
            connection.protocol.state = 'handshake_complete'
117 1
            connection.set_established_state()
118 1
            version_utils.send_desc_request(self.controller, switch)
119 1
            if settings.SEND_SET_CONFIG:
120 1
                version_utils.send_set_config(self.controller, switch)
121 1
            log.info('Connection %s, Switch %s: OPENFLOW HANDSHAKE COMPLETE',
122
                     connection.id, switch.dpid)
123 1
            event_raw = KytosEvent(
124
                name='kytos/of_core.handshake.completed',
125
                content={'switch': switch})
126 1
            self.controller.buffers.app.put(event_raw)
127
128 1
    @listen_to('kytos/of_core.handshake.completed')
129
    def on_handshake_completed_request_flow_list(self, event):
130
        """Request an flow list right after the handshake is completed.
131
132
        Args:
133
            event (KytosEvent): Event with the switch' handshake completed
134
        """
135 1
        switch = event.content['switch']
136 1
        self._request_flow_list(switch)
137
138 1
    @listen_to('kytos/of_core.v0x04.messages.in.ofpt_multipart_reply')
139
    def handle_multipart_reply(self, event):
140
        """Handle multipart replies for v0x04 switches.
141
142
        Args:
143
            event (:class:`~kytos.core.events.KytosEvent):
144
                Event with ofpt_multipart_reply in message.
145
        """
146 1
        reply = event.content['message']
147 1
        switch = event.source.switch
148
149 1
        if reply.multipart_type == MultipartType.OFPMP_FLOW:
150 1
            self._handle_multipart_flow_stats(reply, switch)
151 1
        elif reply.multipart_type == MultipartType.OFPMP_PORT_STATS:
152
            self._handle_multipart_port_stats(reply, switch)
153 1
        elif reply.multipart_type == MultipartType.OFPMP_PORT_DESC:
154 1
            of_core_v0x04_utils.handle_port_desc(self.controller, switch,
155
                                                 reply.body)
156 1
        elif reply.multipart_type == MultipartType.OFPMP_DESC:
157 1
            switch.update_description(reply.body)
158
159 1
    def _handle_multipart_flow_stats(self, reply, switch):
160
        """Update switch flows after all replies are received."""
161 1
        if self._is_multipart_reply_ours(reply, switch, 'flows'):
162
            # Get all flows from the reply
163 1
            flows = [Flow04.from_of_flow_stats(of_flow_stats, switch)
164
                     for of_flow_stats in reply.body]
165
            # Get existent flows from the same xid (or create an empty list)
166 1
            all_flows = self._multipart_replies_flows.setdefault(switch.id, [])
167 1
            all_flows.extend(flows)
168 1
            if reply.flags.value % 2 == 0:  # Last bit means more replies
169 1
                self._update_switch_flows(switch)
170 1
                event_raw = KytosEvent(
171
                    name='kytos/of_core.flow_stats.received',
172
                    content={'switch': switch})
173 1
                self.controller.buffers.app.put(event_raw)
174
175 1
    def _handle_multipart_port_stats(self, reply, switch):
176
        """Emit an event about new port stats."""
177 1
        if self._is_multipart_reply_ours(reply, switch, 'ports'):
178 1
            port_stats = [of_port_stats for of_port_stats in reply.body]
179 1
            all_port_stats = self._multipart_replies_ports.setdefault(
180
                switch.id, []
181
            )
182 1
            all_port_stats.extend(port_stats)
183 1
            if reply.flags.value % 2 == 0:
184 1
                self._new_port_stats(switch)
185
186 1
    def _update_switch_flows(self, switch):
187
        """Update controllers' switch flow list and clean resources."""
188 1
        switch.flows = self._multipart_replies_flows[switch.id]
189 1
        del self._multipart_replies_flows[switch.id]
190 1
        del self._multipart_replies_xids[switch.id]['flows']
191
192 1
    def _new_port_stats(self, switch):
193
        """Send an event with the new port stats and clean resources."""
194
        all_port_stats = self._multipart_replies_ports[switch.id]
195
        del self._multipart_replies_ports[switch.id]
196
        del self._multipart_replies_xids[switch.id]['ports']
197
        port_stats_event = KytosEvent(
198
            name=f"kytos/of_core.port_stats",
199
            content={
200
                'switch': switch,
201
                'port_stats': all_port_stats
202
                })
203
        self.controller.buffers.app.put(port_stats_event)
204
205 1
    def _is_multipart_reply_ours(self, reply, switch, stat):
206
        """Return whether we are expecting the reply."""
207 1
        if switch.id in self._multipart_replies_xids:
208 1
            sent_xid = self._multipart_replies_xids[switch.id].get(stat)
209 1
            if sent_xid == reply.header.xid:
210 1
                return True
211 1
        return False
212
213 1
    @listen_to('kytos/core.openflow.raw.in')
214
    def handle_raw_in(self, event):
215
        """Handle a RawEvent and generate a kytos/core.messages.in.* event.
216
217
        Args:
218
            event (KytosEvent): RawEvent with openflow message to be unpacked
219
        """
220
        # If the switch is already known to the controller, update the
221
        # 'lastseen' attribute
222 1
        switch = event.source.switch
223 1
        if switch:
224 1
            switch.update_lastseen()
225
226 1
        connection = event.source
227
228 1
        data = connection.remaining_data + event.content['new_data']
229 1
        packets, connection.remaining_data = of_slicer(data)
230 1
        if not packets:
231
            return
232
233 1
        unprocessed_packets = []
234
235 1
        for packet in packets:
236 1
            if not connection.is_alive():
237
                return
238
239 1
            if connection.is_new():
240 1
                try:
241 1
                    message = GenericHello(packet=packet)
242 1
                    self._negotiate(connection, message)
243 1
                except (UnpackException, NegotiationException) as err:
244 1
                    if isinstance(err, UnpackException):
245
                        log.error('Connection %s: Invalid hello message',
246
                                  connection.id)
247
                    else:
248 1
                        log.error('Connection %s: Negotiation Failed',
249
                                  connection.id)
250 1
                    connection.protocol.state = 'hello_failed'
251 1
                    connection.close()
252 1
                    connection.state = ConnectionState.FAILED
253 1
                    return
254 1
                connection.set_setup_state()
255 1
                continue
256
257 1
            try:
258 1
                message = connection.protocol.unpack(packet)
259 1
                if message.header.message_type == Type.OFPT_ERROR:
260
                    log.error("OFPT_ERROR: %s error code received from"
261
                              " switch %s with xid %s", message.code,
262
                              switch.dpid, message.header.xid)
263 1
            except (UnpackException, AttributeError) as err:
264 1
                log.error(err)
265 1
                if isinstance(err, AttributeError):
266 1
                    error_msg = 'connection closed before version negotiation'
267 1
                    log.error('Connection %s: %s', connection.id, error_msg)
268 1
                connection.close()
269 1
                return
270
271 1
            log.debug('Connection %s: IN OFP, version: %s, type: %s, xid: %s',
272
                      connection.id,
273
                      message.header.version,
274
                      message.header.message_type,
275
                      message.header.xid)
276
277 1
            waiting_features_reply = (
278
                str(message.header.message_type) == 'Type.OFPT_FEATURES_REPLY'
279
                and connection.protocol.state == 'waiting_features_reply')
280
281 1
            if connection.is_during_setup() and not waiting_features_reply:
282
                unprocessed_packets.append(packet)
283
                continue
284
285 1
            self.emit_message_in(connection, message)
286
287 1
        connection.remaining_data = b''.join(unprocessed_packets) + \
288
                                    connection.remaining_data
289
290 1
    def emit_message_in(self, connection, message):
291
        """Emit a KytosEvent for each incoming message.
292
293
        Also update links and port status.
294
        """
295 1
        if not connection.is_alive():
296
            return
297 1
        emit_message_in(self.controller, connection, message)
298 1
        msg_type = message.header.message_type.name.lower()
299 1
        if msg_type == 'ofpt_port_status':
300 1
            self.update_port_status(message, connection)
301 1
        elif msg_type == 'ofpt_packet_in':
302 1
            self.update_links(message, connection)
303
304 1
    def emit_message_out(self, connection, message):
305
        """Emit a KytosEvent for each outgoing message."""
306 1
        if connection.is_alive():
307 1
            emit_message_out(self.controller, connection, message)
308
309 1
    @listen_to('kytos/of_core.v0x0[14].messages.in.ofpt_echo_request')
310
    def handle_echo_request(self, event):
311
        """Handle Echo Request Messages.
312
313
        This method will get a echo request sent by client and generate a
314
        echo reply as answer.
315
316
        Args:
317
            event (:class:`~kytos.core.events.KytosEvent`):
318
                Event with echo request in message.
319
320
        """
321 1
        pyof_lib = PYOF_VERSION_LIBS[event.source.protocol.version]
322 1
        echo_request = event.message
323 1
        echo_reply = pyof_lib.symmetric.echo_reply.EchoReply(
324
            xid=echo_request.header.xid,
325
            data=echo_request.data)
326 1
        self.emit_message_out(event.source, echo_reply)
327
328 1
    def _negotiate(self, connection, message):
329
        """Handle hello messages.
330
331
        This method will handle the incoming hello message by client
332
        and deal with negotiation.
333
334
        Parameters:
335
            event (KytosMessageInHello): KytosMessageInHelloEvent
336
337
        """
338 1
        if message.versions:
339 1
            version = _get_version_from_bitmask(message.versions)
340
        else:
341 1
            version = _get_version_from_header(message.header.version)
342
343 1
        log.debug('connection %s: negotiated version - %s',
344
                  connection.id, str(version))
345
346 1
        if version is None:
347 1
            self.fail_negotiation(connection, message)
348 1
            raise NegotiationException()
349
350 1
        version_utils = self.of_core_version_utils[version]
351 1
        version_utils.say_hello(self.controller, connection)
352
353 1
        connection.protocol.name = 'openflow'
354 1
        connection.protocol.version = version
355 1
        connection.protocol.unpack = unpack
356 1
        connection.protocol.state = 'sending_features'
357 1
        self.send_features_request(connection)
358 1
        log.debug('Connection %s: Hello complete', connection.id)
359
360 1
    def fail_negotiation(self, connection, hello_message):
361
        """Send Error message and emit event upon negotiation failure."""
362 1
        log.warning('connection %s: version negotiation failed',
363
                    connection.id)
364 1
        connection.protocol.state = 'hello_failed'
365 1
        event_raw = KytosEvent(
366
            name='kytos/of_core.hello_failed',
367
            content={'source': connection})
368 1
        self.controller.buffers.app.put(event_raw)
369
370 1
        version = max(settings.OPENFLOW_VERSIONS)
371 1
        pyof_lib = PYOF_VERSION_LIBS[version]
372
373 1
        error_message = pyof_lib.asynchronous.error_msg.ErrorMsg(
374
            xid=hello_message.header.xid,
375
            error_type=pyof_lib.asynchronous.error_msg.
376
            ErrorType.OFPET_HELLO_FAILED,
377
            code=pyof_lib.asynchronous.error_msg.HelloFailedCode.
378
            OFPHFC_INCOMPATIBLE)
379 1
        self.emit_message_out(connection, error_message)
380
381
    # May be removed
382 1
    @listen_to('kytos/of_core.v0x0[14].messages.out.ofpt_echo_reply')
383
    def handle_queued_openflow_echo_reply(self, event):
384
        """Handle queued OpenFlow echo reply messages.
385
386
        Send a feature request message if SEND_FEATURES_REQUEST_ON_ECHO
387
        is True (default is False).
388
        """
389 1
        if settings.SEND_FEATURES_REQUEST_ON_ECHO:
390 1
            self.send_features_request(event.destination)
391
392 1
    def send_features_request(self, destination):
393
        """Send a feature request to the switch."""
394 1
        version = destination.protocol.version
395 1
        pyof_lib = PYOF_VERSION_LIBS[version]
396 1
        features_request = pyof_lib.controller2switch.\
397
            features_request.FeaturesRequest()
398 1
        self.emit_message_out(destination, features_request)
399
400
    # pylint: disable=no-self-use
401 1
    @listen_to('kytos/of_core.v0x0[14].messages.out.ofpt_features_request')
402
    def handle_features_request_sent(self, event):
403
        """Ensure request has actually been sent before changing state."""
404 1
        if event.destination.protocol.state == 'sending_features':
405 1
            event.destination.protocol.state = 'waiting_features_reply'
406
    # pylint: enable=no-self-use
407
408 1
    @staticmethod
409 1
    @listen_to('kytos/of_core.v0x[0-9a-f]{2}.messages.in.hello_failed',
410
               'kytos/of_core.v0x0[14].messages.out.hello_failed')
411
    def handle_openflow_in_hello_failed(event):
412
        """Close the connection upon hello failure."""
413 1
        event.destination.close()
414 1
        log.debug("Connection %s: Connection closed.", event.destination.id)
415
416 1
    def shutdown(self):
417
        """End of the application."""
418 1
        log.debug('Shutting down...')
419
420 1
    def update_links(self, message, source):
421
        """Dispatch 'reacheable.mac' event.
422
423
        Args:
424
            message: python openflow (pyof) PacketIn object.
425
            source: kytos.core.switch.Connection instance.
426
427
        Dispatch:
428
            `reachable.mac`:
429
                {
430
                  switch : <switch.id>,
431
                  port: <port.port_no>
432
                  reachable_mac: <mac_address>
433
                }
434
435
        """
436 1
        ethernet = Ethernet()
437 1
        ethernet.unpack(message.data.value)
438 1
        if ethernet.ether_type in (EtherType.LLDP, EtherType.IPV6):
439
            return
440
441 1
        try:
442 1
            port = source.switch.get_interface_by_port_no(
443
                message.in_port.value)
444 1
        except AttributeError:
445 1
            port = source.switch.get_interface_by_port_no(message.in_port)
446
447 1
        name = 'kytos/of_core.reachable.mac'
448 1
        content = {'switch': source.switch,
449
                   'port': port,
450
                   'reachable_mac': ethernet.source.value}
451 1
        event = KytosEvent(name, content)
452 1
        self.controller.buffers.app.put(event)
453
454 1
        msg = 'The MAC %s is reachable from switch/port %s/%s.'
455 1
        log.debug(msg, ethernet.source, source.switch.id,
456
                  message.in_port)
457
458 1
    def _send_specific_port_mod(self, port, interface, current_state):
459
        """Dispatch port link_up/link_down events."""
460 1
        event_name = 'kytos/of_core.switch.interface.'
461 1
        event_content = {'interface': interface}
462
463 1
        if port.state.value % 2:
464 1
            status = 'link_down'
465
        else:
466 1
            status = 'link_up'
467
468 1
        if current_state:
469 1
            if current_state % 2:
470 1
                current_status = 'link_down'
471
            else:
472 1
                current_status = 'link_up'
473
        else:
474 1
            current_status = None
475
476 1
        if status != current_status:
477 1
            event = KytosEvent(name=event_name+status, content=event_content)
478 1
            self.controller.buffers.app.put(event)
479
480 1
    def update_port_status(self, port_status, source):
481
        """Dispatch 'port.*' events.
482
483
        Current events:
484
485
        created|deleted|link_up|link_down|modified
486
487
        Args:
488
            port_status: python openflow (pyof) PortStatus object.
489
            source: kytos.core.switch.Connection instance.
490
491
        Dispatch:
492
            `kytos/of_core.switch.port.[created|modified|deleted]`:
493
                {
494
                  switch : <switch.id>,
495
                  port: <port.port_no>
496
                  port_description: {<description of the port>}
497
                }
498
499
        """
500 1
        reason = port_status.reason.enum_ref(port_status.reason.value).name
501 1
        port = port_status.desc
502 1
        port_no = port.port_no.value
503 1
        event_name = 'kytos/of_core.switch.interface.'
504
505 1
        if reason == 'OFPPR_ADD':
506 1
            status = 'created'
507 1
            interface = Interface(name=port.name.value,
508
                                  address=port.hw_addr.value,
509
                                  port_number=port_no,
510
                                  switch=source.switch,
511
                                  state=port.state.value,
512
                                  features=port.curr)
513 1
            source.switch.update_interface(interface)
514
515 1
        elif reason == 'OFPPR_MODIFY':
516 1
            status = 'modified'
517 1
            interface = source.switch.get_interface_by_port_no(port_no)
518 1
            current_status = None
519 1
            if interface:
520 1
                log.info('Modified %s %s:%s' %
521
                         (interface, interface.switch.dpid,
522
                          interface.port_number))
523 1
                current_status = interface.state
524 1
                interface.state = port.state.value
525 1
                interface.name = port.name.value
526 1
                interface.address = port.hw_addr.value
527 1
                interface.features = port.curr
528
            else:
529 1
                interface = Interface(name=port.name.value,
530
                                      address=port.hw_addr.value,
531
                                      port_number=port_no,
532
                                      switch=source.switch,
533
                                      state=port.state.value,
534
                                      features=port.curr)
535 1
            source.switch.update_interface(interface)
536 1
            self._send_specific_port_mod(port, interface, current_status)
537
538 1
        elif reason == 'OFPPR_DELETE':
539 1
            status = 'deleted'
540 1
            interface = source.switch.get_interface_by_port_no(port_no)
541 1
            source.switch.remove_interface(interface)
542
543 1
        event_name += status
0 ignored issues
show
introduced by
The variable status does not seem to be defined for all execution paths.
Loading history...
544 1
        content = {'interface': interface}
0 ignored issues
show
introduced by
The variable interface does not seem to be defined for all execution paths.
Loading history...
545
546 1
        event = KytosEvent(name=event_name, content=content)
547 1
        self.controller.buffers.app.put(event)
548
549 1
        msg = 'The port %s from switch %s was %s.'
550 1
        log.debug(msg, port_status.desc.port_no, source.switch.id, status)
551
552
553 1
def _get_version_from_bitmask(message_versions):
554
    """Get common version from hello message version bitmap."""
555
    try:
556
        return max([version for version in message_versions
557
                    if version in settings.OPENFLOW_VERSIONS])
558
    except ValueError:
559
        return None
560
561
562 1
def _get_version_from_header(message_version):
563
    """Get common version from hello message header version."""
564
    version = min(message_version, max(settings.OPENFLOW_VERSIONS))
565
    return version if version in settings.OPENFLOW_VERSIONS else None
566