Passed
Push — master ( d036b9...099b1a )
by Vinicius
12:28 queued 10:49
created

build.tests.unit.test_main   F

Complexity

Total Complexity 77

Size/Duplication

Total Lines 1673
Duplicated Lines 14.29 %

Importance

Changes 0
Metric Value
eloc 1343
dl 239
loc 1673
rs 1.04
c 0
b 0
f 0
wmc 77

1 Function

Rating   Name   Duplication   Size   Complexity  
A test_handle_link_liveness_status() 0 23 1

68 Methods

Rating   Name   Duplication   Size   Complexity  
A TestMain.setUp() 0 13 2
A TestMain.test_get_link_from_interface() 0 14 1
B TestMain.test_get_topology() 0 53 1
B TestMain.test_load_topology() 0 63 3
A TestMain.test_load_topology_fail_link() 0 19 1
B TestMain.test_verify_api_urls() 47 47 1
A TestMain.test_load_topology_fail_switch() 0 19 1
A TestMain.test_get_event_listeners() 0 26 1
A TestMain.test_get_link_or_create() 0 20 1
A TestMain.test_load_topology_does_nothing() 0 11 1
A TestMain.test_handle_new_switch() 0 11 1
A TestMain.test_disable_switch() 24 24 1
A TestMain.test_disable_link() 0 20 1
A TestMain.test_add_switch_metadata() 0 24 1
A TestMain.test_get_switch_metadata() 0 17 1
A TestMain.test_enable_link() 0 20 1
B TestMain.test_load_switch() 0 57 1
A TestMain.test_add_link_metadata_wrong_format() 0 15 1
A TestMain.test_enable_switch() 24 24 1
A TestMain.test_delete_interface_metadata() 0 41 1
A TestMain.test_add_interface_metadata() 0 32 1
A TestMain.test_handle_on_link_available_tags() 0 33 1
B TestMain.test_enable_interfaces() 50 50 1
A TestMain.test_add_switch_metadata_wrong_format() 0 14 1
A TestMain.test_get_interface_metadata() 0 26 1
A TestMain.test_interfaces_available_tags() 0 13 1
A TestMain.test_delete_switch_metadata() 0 26 1
A TestMain.test_handle_topo_controller_upsert_switch() 0 6 1
B TestMain.test_load_switch_attrs() 0 68 1
B TestMain.test_disable_interfaces() 50 50 1
A TestMain.test_add_link_metadata() 0 22 1
A TestMain.test_get_link_metadata() 0 19 1
A TestMain.test_delete_link_metadata() 0 31 1
A TestMain.test_add_interface_metadata_wrong_format() 0 14 1
A TestMain.test_handle_connection_lost() 0 9 1
A TestMain.test_handle_interface_created() 0 9 1
A TestMain.test_handle_lldp_status_updated() 0 20 1
B TestMain.test_load_link() 0 59 2
A TestMain.test_fail_load_link() 0 41 3
A TestMain.test_handle_link_down() 0 19 1
A TestMain.test_handle_interfaces_created() 0 17 1
A TestMain.test_handle_interface_down() 0 8 1
A TestMain.test_interface_link_up() 0 27 1
A TestMain.test_handle_interface_created_inactive() 0 10 1
A TestMain.test_interface_deleted() 0 6 1
A TestMain.test_handle_link_down_not_active() 0 19 1
A TestMain.test_interface_link_down() 0 17 1
A TestMain.test_handle_switch_maintenance_start() 22 22 1
A TestMain.test_handle_link_up_intf_down() 0 19 1
A TestMain.test_notify_port_created() 0 9 1
A TestMain.test_notify_topology_update() 0 8 1
A TestMain.test_add_links() 0 22 1
A TestMain.test_notify_link_status_change() 0 9 1
A TestMain.test_get_links_from_interfaces() 0 16 1
A TestMain.test_handle_link_down_not_active_last_status() 0 22 1
A TestMain.test_notify_switch_links_status() 0 22 1
A TestMain.test_link_status_hook_link_up_timer() 0 13 1
A TestMain.test_handle_switch_maintenance_end() 22 22 1
A TestMain.test_notify_interface_link_status() 0 24 1
A TestMain.test_notify_switch_enabled() 0 9 1
A TestMain.test_handle_link_up() 0 18 1
A TestMain.test_handle_link_maintenance_start() 0 15 1
A TestMain.test_notify_link_up_if_status() 0 27 1
A TestMain.test_handle_link_maintenance_end() 0 15 1
A TestMain.test_notify_switch_disabled() 0 9 1
A TestMain.test_notify_current_topology() 0 9 1
A TestMain.test_notify_metadata_changes() 0 15 3
A TestMain.test_handle_link_liveness_disabled() 0 21 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like build.tests.unit.test_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
"""Module to test the main napp file."""
2
# pylint: disable=import-error,no-name-in-module,wrong-import-order
3
# pylint: disable=import-outside-toplevel
4
import pytest
5
import json
6
import time
7
from datetime import timedelta
8
from unittest import TestCase
9
from unittest.mock import MagicMock, create_autospec, patch
10
11
from kytos.core.common import EntityStatus
12
from kytos.core.helpers import now
13
from kytos.core.interface import Interface
14
from kytos.core.link import Link
15
from kytos.core.switch import Switch
16
from kytos.lib.helpers import (get_interface_mock, get_link_mock,
17
                               get_controller_mock, get_switch_mock,
18
                               get_test_client)
19
from napps.kytos.topology.exceptions import RestoreError
20
from tests.unit.helpers import get_napp_urls
21
22
23
@pytest.mark.parametrize("liveness_status, status",
24
                         [("up", EntityStatus.UP),
25
                          ("down", EntityStatus.DOWN)])
26
def test_handle_link_liveness_status(liveness_status, status) -> None:
27
    """Test handle link liveness."""
28
    from napps.kytos.topology.main import Main
29
    Main.get_topo_controller = MagicMock()
30
    napp = Main(get_controller_mock())
31
    napp.notify_topology_update = MagicMock()
32
    napp.notify_link_status_change = MagicMock()
33
34
    link = MagicMock(id="some_id", status=status)
35
    napp.handle_link_liveness_status(link, liveness_status)
36
37
    add_link_meta = napp.topo_controller.add_link_metadata
38
    add_link_meta.assert_called_with(link.id, {"liveness_status":
39
                                               liveness_status})
40
    link.extend_metadata.assert_called_with({"liveness_status":
41
                                             liveness_status})
42
    assert napp.notify_topology_update.call_count == 1
43
    assert napp.notify_link_status_change.call_count == 1
44
    reason = f"liveness_{liveness_status}"
45
    napp.notify_link_status_change.assert_called_with(link, reason=reason)
46
47
48
# pylint: disable=too-many-public-methods
49
class TestMain(TestCase):
50
    """Test the Main class."""
51
52
    # pylint: disable=too-many-public-methods, protected-access,C0302
53
54
    def setUp(self):
55
        """Execute steps before each tests.
56
57
        Set the server_name_url_url from kytos/topology
58
        """
59
        self.server_name_url = 'http://localhost:8181/api/kytos/topology'
60
61
        patch('kytos.core.helpers.run_on_thread', lambda x: x).start()
62
        # pylint: disable=import-outside-toplevel
63
        from napps.kytos.topology.main import Main
64
        Main.get_topo_controller = MagicMock()
65
        self.addCleanup(patch.stopall)
66
        self.napp = Main(get_controller_mock())
67
68
    def test_get_event_listeners(self):
69
        """Verify all event listeners registered."""
70
        expected_events = ['kytos/core.shutdown',
71
                           'kytos/core.shutdown.kytos/topology',
72
                           'kytos/maintenance.start_link',
73
                           'kytos/maintenance.end_link',
74
                           'kytos/maintenance.start_switch',
75
                           'kytos/maintenance.end_switch',
76
                           'kytos/.*.link_available_tags',
77
                           '.*.topo_controller.upsert_switch',
78
                           '.*.of_lldp.network_status.updated',
79
                           '.*.interface.is.nni',
80
                           '.*.connection.lost',
81
                           '.*.switch.interfaces.created',
82
                           '.*.topology.switch.interface.created',
83
                           '.*.switch.interface.deleted',
84
                           '.*.switch.interface.link_down',
85
                           '.*.switch.interface.link_up',
86
                           '.*.switch.(new|reconnected)',
87
                           'kytos/.*.liveness.(up|down)',
88
                           'kytos/.*.liveness.disabled',
89
                           'kytos/topology.get',
90
                           '.*.switch.port.created',
91
                           'kytos/topology.notify_link_up_if_status']
92
        actual_events = self.napp.listeners()
93
        self.assertCountEqual(expected_events, actual_events)
94
95 View Code Duplication
    def test_verify_api_urls(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
96
        """Verify all APIs registered."""
97
        expected_urls = [
98
         ({}, {'GET', 'OPTIONS', 'HEAD'}, '/api/kytos/topology/v3/interfaces'),
99
         ({}, {'GET', 'OPTIONS', 'HEAD'}, '/api/kytos/topology/v3/switches'),
100
         ({}, {'GET', 'OPTIONS', 'HEAD'}, '/api/kytos/topology/v3/links'),
101
         ({}, {'GET', 'OPTIONS', 'HEAD'}, '/api/kytos/topology/v3/'),
102
         ({'dpid': '[dpid]'}, {'POST', 'OPTIONS'},
103
          '/api/kytos/topology/v3/interfaces/switch/<dpid>/disable'),
104
         ({'dpid': '[dpid]'}, {'POST', 'OPTIONS'},
105
          '/api/kytos/topology/v3/interfaces/switch/<dpid>/enable'),
106
         ({'key': '[key]', 'interface_id': '[interface_id]'},
107
          {'OPTIONS', 'DELETE'},
108
          '/api/kytos/topology/v3/interfaces/<interface_id>/metadata/<key>'),
109
         ({'interface_id': '[interface_id]'}, {'POST', 'OPTIONS'},
110
          '/api/kytos/topology/v3/interfaces/<interface_id>/metadata'),
111
         ({'interface_id': '[interface_id]'}, {'GET', 'OPTIONS', 'HEAD'},
112
          '/api/kytos/topology/v3/interfaces/<interface_id>/metadata'),
113
         ({'interface_disable_id': '[interface_disable_id]'},
114
          {'POST', 'OPTIONS'},
115
          '/api/kytos/topology/v3/interfaces/<interface_disable_id>/disable'),
116
         ({'interface_enable_id': '[interface_enable_id]'},
117
          {'POST', 'OPTIONS'},
118
          '/api/kytos/topology/v3/interfaces/<interface_enable_id>/enable'),
119
         ({'dpid': '[dpid]', 'key': '[key]'}, {'OPTIONS', 'DELETE'},
120
          '/api/kytos/topology/v3/switches/<dpid>/metadata/<key>'),
121
         ({'dpid': '[dpid]'}, {'POST', 'OPTIONS'},
122
          '/api/kytos/topology/v3/switches/<dpid>/metadata'),
123
         ({'dpid': '[dpid]'}, {'GET', 'OPTIONS', 'HEAD'},
124
          '/api/kytos/topology/v3/switches/<dpid>/metadata'),
125
         ({'dpid': '[dpid]'}, {'POST', 'OPTIONS'},
126
          '/api/kytos/topology/v3/switches/<dpid>/disable'),
127
         ({'dpid': '[dpid]'}, {'POST', 'OPTIONS'},
128
          '/api/kytos/topology/v3/switches/<dpid>/enable'),
129
         ({'link_id': '[link_id]', 'key': '[key]'}, {'OPTIONS', 'DELETE'},
130
          '/api/kytos/topology/v3/links/<link_id>/metadata/<key>'),
131
         ({'link_id': '[link_id]'}, {'POST', 'OPTIONS'},
132
          '/api/kytos/topology/v3/links/<link_id>/metadata'),
133
         ({'link_id': '[link_id]'}, {'GET', 'OPTIONS', 'HEAD'},
134
          '/api/kytos/topology/v3/links/<link_id>/metadata'),
135
         ({'link_id': '[link_id]'}, {'POST', 'OPTIONS'},
136
          '/api/kytos/topology/v3/links/<link_id>/disable'),
137
         ({'link_id': '[link_id]'}, {'POST', 'OPTIONS'},
138
          '/api/kytos/topology/v3/links/<link_id>/enable')]
139
140
        urls = get_napp_urls(self.napp)
141
        self.assertEqual(expected_urls, urls)
142
143
    def test_get_link_or_create(self):
144
        """Test _get_link_or_create."""
145
        dpid_a = "00:00:00:00:00:00:00:01"
146
        dpid_b = "00:00:00:00:00:00:00:02"
147
        mock_switch_a = get_switch_mock(dpid_a, 0x04)
148
        mock_switch_b = get_switch_mock(dpid_b, 0x04)
149
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
150
        mock_interface_b = get_interface_mock('s2-eth1', 1, mock_switch_b)
151
        mock_interface_a.id = dpid_a
152
        mock_interface_b.id = dpid_b
153
154
        link, created = self.napp._get_link_or_create(mock_interface_a,
155
                                                      mock_interface_b)
156
        self.assertTrue(created)
157
        self.assertEqual(link.endpoint_a.id, dpid_a)
158
        self.assertEqual(link.endpoint_b.id, dpid_b)
159
160
        link, created = self.napp._get_link_or_create(mock_interface_a,
161
                                                      mock_interface_b)
162
        self.assertFalse(created)
163
164
    def test_get_link_from_interface(self):
165
        """Test _get_link_from_interface."""
166
        mock_switch_a = get_switch_mock("00:00:00:00:00:00:00:01", 0x04)
167
        mock_switch_b = get_switch_mock("00:00:00:00:00:00:00:02", 0x04)
168
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
169
        mock_interface_b = get_interface_mock('s2-eth1', 1, mock_switch_b)
170
        mock_interface_c = get_interface_mock('s2-eth1', 2, mock_switch_b)
171
        mock_link = get_link_mock(mock_interface_a, mock_interface_b)
172
        self.napp.links = {'0e2b5d7bc858b9f38db11b69': mock_link}
173
        response = self.napp._get_link_from_interface(mock_interface_a)
174
        self.assertEqual(response, mock_link)
175
176
        response = self.napp._get_link_from_interface(mock_interface_c)
177
        self.assertEqual(response, None)
178
179
    def test_get_topology(self):
180
        """Test get_topology."""
181
        dpid_a = "00:00:00:00:00:00:00:01"
182
        dpid_b = "00:00:00:00:00:00:00:02"
183
        expected = {
184
                      "topology": {
185
                        "switches": {
186
                          "00:00:00:00:00:00:00:01": {
187
                            "metadata": {
188
                              "lat": "0.0",
189
                              "lng": "-30.0"
190
                            }
191
                          },
192
                          "00:00:00:00:00:00:00:02": {
193
                            "metadata": {
194
                              "lat": "0.0",
195
                              "lng": "-30.0"
196
                            }
197
                          }
198
                        },
199
                        "links": {
200
                          "cf0f4071be4": {
201
                            "id": "cf0f4071be4"
202
                          }
203
                        }
204
                      }
205
                    }
206
207
        mock_switch_a = get_switch_mock(dpid_a, 0x04)
208
        mock_switch_b = get_switch_mock(dpid_b, 0x04)
209
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
210
        mock_interface_b = get_interface_mock('s2-eth1', 1, mock_switch_b)
211
212
        mock_link = get_link_mock(mock_interface_a, mock_interface_b)
213
        mock_link.id = 'cf0f4071be4'
214
        mock_switch_a.id = dpid_a
215
        mock_switch_a.as_dict.return_value = {'metadata': {'lat': '0.0',
216
                                              'lng': '-30.0'}}
217
        mock_switch_b.id = dpid_b
218
        mock_switch_b.as_dict.return_value = {'metadata': {'lat': '0.0',
219
                                              'lng': '-30.0'}}
220
221
        self.napp.controller.switches = {dpid_a: mock_switch_a,
222
                                         dpid_b: mock_switch_b}
223
224
        self.napp.links = {"cf0f4071be4": mock_link}
225
        mock_link.as_dict.return_value = {"id": "cf0f4071be4"}
226
        api = get_test_client(self.napp.controller, self.napp)
227
228
        url = f'{self.server_name_url}/v3/'
229
        response = api.get(url)
230
        self.assertEqual(response.status_code, 200)
231
        self.assertEqual(json.loads(response.data), expected)
232
233
    def test_load_topology(self):
234
        """Test load_topology."""
235
        link_id = \
236
            'cf0f4071be426b3f745027f5d22bc61f8312ae86293c9b28e7e66015607a9260'
237
        dpid_a = '00:00:00:00:00:00:00:01'
238
        dpid_b = '00:00:00:00:00:00:00:02'
239
        topology = {
240
            "topology": {
241
                "links": {
242
                    link_id: {
243
                        "enabled": True,
244
                        "id": link_id,
245
                        "endpoint_a": {"id": f"{dpid_a}:2"},
246
                        "endpoint_b": {"id": f"{dpid_b}:2"},
247
                    }
248
                },
249
                "switches": {
250
                    dpid_a: {
251
                        "dpid": dpid_a,
252
                        "enabled": True,
253
                        "metadata": {},
254
                        "id": dpid_a,
255
                        "interfaces": {
256
                            f"{dpid_a}:2": {
257
                                "enabled": True,
258
                                "metadata": {},
259
                                "lldp": True,
260
                                "port_number": 2,
261
                                "name": "s1-eth2",
262
                            }
263
                        },
264
                    },
265
                    dpid_b: {
266
                        "dpid": dpid_b,
267
                        "enabled": True,
268
                        "metadata": {},
269
                        "id": dpid_b,
270
                        "interfaces": {
271
                            f"{dpid_b}:2": {
272
                                "enabled": True,
273
                                "metadata": {},
274
                                "lldp": True,
275
                                "port_number": 2,
276
                                "name": "s2-eth2",
277
                            }
278
                        },
279
                    },
280
                },
281
            }
282
        }
283
        switches_expected = [dpid_a, dpid_b]
284
        interfaces_expected = [f'{dpid_a}:2', f'{dpid_b}:2']
285
        links_expected = [link_id]
286
        self.napp.topo_controller.get_topology.return_value = topology
287
        self.napp.load_topology()
288
        self.assertListEqual(switches_expected,
289
                             list(self.napp.controller.switches.keys()))
290
        interfaces = []
291
        for switch in self.napp.controller.switches.values():
292
            for iface in switch.interfaces.values():
293
                interfaces.append(iface.id)
294
        self.assertListEqual(interfaces_expected, interfaces)
295
        self.assertListEqual(links_expected, list(self.napp.links.keys()))
296
297
    @patch('napps.kytos.topology.main.Main._load_switch')
298
    @patch('napps.kytos.topology.main.Main._load_link')
299
    def test_load_topology_does_nothing(self, *args):
300
        """Test _load_network_status doing nothing."""
301
        (mock_load_link, mock_load_switch) = args
302
        self.napp.topo_controller.get_topology.return_value = {
303
            "topology": {"switches": {}, "links": {}}
304
        }
305
        self.napp.topo_controller.load_topology()
306
        assert mock_load_link.call_count == 0
307
        assert mock_load_switch.call_count == 0
308
309
    @patch('napps.kytos.topology.main.Main._load_switch')
310
    @patch('napps.kytos.topology.main.log')
311
    def test_load_topology_fail_switch(self, *args):
312
        """Test load_topology failure in switch."""
313
        (mock_log, mock_load_switch) = args
314
        topology = {
315
            'topology': {
316
                'links': {},
317
                'switches': {
318
                    '1': {}
319
                }
320
            }
321
        }
322
        mock_log.error.return_value = True
323
        self.napp.topo_controller.get_topology.return_value = topology
324
        mock_load_switch.side_effect = Exception('xpto')
325
        self.napp.load_topology()
326
        error = 'Error loading switch: xpto'
327
        mock_log.error.assert_called_with(error)
328
329
    @patch('napps.kytos.topology.main.Main._load_link')
330
    @patch('napps.kytos.topology.main.log')
331
    def test_load_topology_fail_link(self, *args):
332
        """Test load_topology failure in link."""
333
        (mock_log, mock_load_link) = args
334
        topology = {
335
            'topology': {
336
                'switches': {},
337
                'links': {
338
                    '1': {}
339
                }
340
            }
341
        }
342
        mock_log.error.return_value = True
343
        self.napp.topo_controller.get_topology.return_value = topology
344
        mock_load_link.side_effect = Exception('xpto')
345
        self.napp.load_topology()
346
        error = 'Error loading link 1: xpto'
347
        mock_log.error.assert_called_with(error)
348
349
    @patch('napps.kytos.topology.main.Main.load_interfaces_available_tags')
350
    @patch('napps.kytos.topology.main.KytosEvent')
351
    @patch('kytos.core.buffers.KytosEventBuffer.put')
352
    def test_load_switch(self, *args):
353
        """Test _load_switch."""
354
        (mock_buffers_put, mock_event, mock_load_tags) = args
355
        dpid_a = "00:00:00:00:00:00:00:01"
356
        dpid_x = "00:00:00:00:00:00:00:XX"
357
        iface_a = f'{dpid_a}:1'
358
        switch_attrs = {
359
            'dpid': dpid_a,
360
            'enabled': True,
361
            'id': dpid_a,
362
            'metadata': {},
363
            'interfaces': {
364
                iface_a: {
365
                    'enabled': True,
366
                    'active': True,
367
                    'lldp': True,
368
                    'id': iface_a,
369
                    'switch': dpid_a,
370
                    'metadata': {},
371
                    'name': 's2-eth1',
372
                    'port_number': 1
373
                }
374
            }
375
        }
376
        self.napp._load_switch(dpid_a, switch_attrs)
377
378
        self.assertEqual(len(self.napp.controller.switches), 1)
379
        self.assertIn(dpid_a, self.napp.controller.switches)
380
        self.assertNotIn(dpid_x, self.napp.controller.switches)
381
        switch = self.napp.controller.switches[dpid_a]
382
        interface_details = self.napp.topo_controller.get_interfaces_details
383
        interface_details.assert_called_once_with([iface_a])
384
        mock_load_tags.assert_called()
385
386
        self.assertEqual(switch.id, dpid_a)
387
        self.assertEqual(switch.dpid, dpid_a)
388
        self.assertTrue(switch.is_enabled())
389
        self.assertFalse(switch.is_active())
390
391
        self.assertEqual(len(switch.interfaces), 1)
392
        self.assertIn(1, switch.interfaces)
393
        self.assertNotIn(2, switch.interfaces)
394
        mock_event.assert_called()
395
        mock_buffers_put.assert_called()
396
397
        interface = switch.interfaces[1]
398
        self.assertEqual(interface.id, iface_a)
399
        self.assertEqual(interface.switch.id, dpid_a)
400
        self.assertEqual(interface.port_number, 1)
401
        self.assertTrue(interface.is_enabled())
402
        self.assertFalse(interface.is_active())
403
        self.assertTrue(interface.lldp)
404
        self.assertTrue(interface.uni)
405
        self.assertFalse(interface.nni)
406
407
    def test_load_switch_attrs(self):
408
        """Test _load_switch."""
409
        dpid_b = "00:00:00:00:00:00:00:02"
410
        iface_b = f'{dpid_b}:1'
411
        switch_attrs = {
412
            "active": True,
413
            "connection": "127.0.0.1:43230",
414
            "data_path": "XX Human readable desc of dp",
415
            "dpid": "00:00:00:00:00:00:00:02",
416
            "enabled": False,
417
            "hardware": "Open vSwitch",
418
            "id": "00:00:00:00:00:00:00:02",
419
            "interfaces": {
420
                "00:00:00:00:00:00:00:02:1": {
421
                    "active": True,
422
                    "enabled": False,
423
                    "id": "00:00:00:00:00:00:00:02:1",
424
                    "link": "",
425
                    "lldp": False,
426
                    "mac": "de:58:c3:30:b7:b7",
427
                    "metadata": {},
428
                    "name": "s2-eth1",
429
                    "nni": False,
430
                    "port_number": 1,
431
                    "speed": 1250000000,
432
                    "switch": "00:00:00:00:00:00:00:02",
433
                    "type": "interface",
434
                    "uni": True
435
                },
436
            },
437
            "manufacturer": "Nicira, Inc.",
438
            "metadata": {},
439
            "name": "00:00:00:00:00:00:00:04",
440
            "ofp_version": "0x04",
441
            "serial": "XX serial number",
442
            "software": "2.10.7",
443
            "type": "switch"
444
        }
445
446
        self.assertEqual(len(self.napp.controller.switches), 0)
447
        self.napp._load_switch(dpid_b, switch_attrs)
448
        self.assertEqual(len(self.napp.controller.switches), 1)
449
        self.assertIn(dpid_b, self.napp.controller.switches)
450
451
        switch = self.napp.controller.switches[dpid_b]
452
        self.assertEqual(switch.id, dpid_b)
453
        self.assertEqual(switch.dpid, dpid_b)
454
        self.assertFalse(switch.is_enabled())
455
        self.assertFalse(switch.is_active())
456
        self.assertEqual(switch.description['manufacturer'], 'Nicira, Inc.')
457
        self.assertEqual(switch.description['hardware'], 'Open vSwitch')
458
        self.assertEqual(switch.description['software'], '2.10.7')
459
        self.assertEqual(switch.description['serial'], 'XX serial number')
460
        self.assertEqual(switch.description['data_path'],
461
                         'XX Human readable desc of dp')
462
463
        self.assertEqual(len(switch.interfaces), 1)
464
        self.assertIn(1, switch.interfaces)
465
        self.assertNotIn(2, switch.interfaces)
466
467
        interface = switch.interfaces[1]
468
        self.assertEqual(interface.id, iface_b)
469
        self.assertEqual(interface.switch.id, dpid_b)
470
        self.assertEqual(interface.port_number, 1)
471
        self.assertFalse(interface.is_enabled())
472
        self.assertFalse(interface.lldp)
473
        self.assertTrue(interface.uni)
474
        self.assertFalse(interface.nni)
475
476
    def test_interfaces_available_tags(self):
477
        """Test load_interfaces_available_tags."""
478
        dpid_a = "00:00:00:00:00:00:00:01"
479
        mock_switch_a = get_switch_mock(dpid_a, 0x04)
480
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
481
        mock_interface_a.id = dpid_a + ':1'
482
        mock_switch_a.interfaces = {1: mock_interface_a}
483
        tags = [1, 2, 3]
484
        interface_details = [{"id": mock_interface_a.id,
485
                             "available_vlans": tags}]
486
        self.napp.load_interfaces_available_tags(mock_switch_a,
487
                                                 interface_details)
488
        mock_interface_a.set_available_tags.assert_called_once_with(tags)
489
490
    def test_handle_on_link_available_tags(self):
491
        """test_handle_on_link_available_tags."""
492
        dpid_a = "00:00:00:00:00:00:00:01"
493
        dpid_b = "00:00:00:00:00:00:00:02"
494
        tag = MagicMock()
495
        tag.value = 1
496
        tags = [tag]
497
        link_mock = MagicMock()
498
        mock_switch_a = get_switch_mock(dpid_a, 0x04)
499
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
500
        mock_interface_a.available_tags = tags
501
        mock_switch_b = get_switch_mock(dpid_b, 0x04)
502
        mock_interface_b = get_interface_mock('s2-eth1', 1, mock_switch_b)
503
        mock_interface_b.available_tags = tags
504
        link_id = '4d42dc08522'
505
        link_mock.id = link_id
506
        link_mock.endpoint_a = mock_interface_a
507
        link_mock.endpoint_b = mock_interface_b
508
509
        self.napp.links[link_id] = link_mock
510
        self.napp.handle_on_link_available_tags(link_mock)
511
        bulk_upsert = self.napp.topo_controller.bulk_upsert_interface_details
512
        bulk_upsert.assert_called_once_with(
513
            [
514
                (
515
                    "00:00:00:00:00:00:00:01:1",
516
                    {"_id": "00:00:00:00:00:00:00:01:1",
517
                     "available_vlans": [1]},
518
                ),
519
                (
520
                    "00:00:00:00:00:00:00:02:1",
521
                    {"_id": "00:00:00:00:00:00:00:02:1",
522
                     "available_vlans": [1]},
523
                ),
524
            ]
525
        )
526
527
    def test_load_link(self):
528
        """Test _load_link."""
529
        dpid_a = "00:00:00:00:00:00:00:01"
530
        dpid_b = "00:00:00:00:00:00:00:02"
531
        link_id = '4d42dc08522'
532
        mock_switch_a = get_switch_mock(dpid_a, 0x04)
533
        mock_switch_b = get_switch_mock(dpid_b, 0x04)
534
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
535
        mock_interface_a.id = dpid_a + ':1'
536
        mock_interface_a.available_tags = [1, 2, 3]
537
        mock_interface_b = get_interface_mock('s2-eth1', 1, mock_switch_b)
538
        mock_interface_b.id = dpid_b + ':1'
539
        mock_interface_b.available_tags = [1, 2, 3]
540
        mock_switch_a.interfaces = {1: mock_interface_a}
541
        mock_switch_b.interfaces = {1: mock_interface_b}
542
        self.napp.controller.switches[dpid_a] = mock_switch_a
543
        self.napp.controller.switches[dpid_b] = mock_switch_b
544
        link_attrs = {
545
            'enabled': True,
546
            'id': link_id,
547
            'metadata': {},
548
            'endpoint_a': {
549
                'id': mock_interface_a.id
550
            },
551
            'endpoint_b': {
552
                'id': mock_interface_b.id
553
            }
554
        }
555
556
        self.napp._load_link(link_attrs)
557
558
        self.assertEqual(len(self.napp.links), 1)
559
        link = list(self.napp.links.values())[0]
560
561
        self.assertEqual(link.endpoint_a.id, mock_interface_a.id)
562
        self.assertEqual(link.endpoint_b.id, mock_interface_b.id)
563
        self.assertTrue(mock_interface_a.nni)
564
        self.assertTrue(mock_interface_b.nni)
565
        self.assertEqual(mock_interface_a.update_link.call_count, 1)
566
        self.assertEqual(mock_interface_b.update_link.call_count, 1)
567
568
        # test enable/disable
569
        link_id = '4d42dc08522'
570
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
571
        mock_interface_b = get_interface_mock('s2-eth1', 1, mock_switch_b)
572
        mock_link = get_link_mock(mock_interface_a, mock_interface_b)
573
        mock_link.id = link_id
574
        with patch('napps.kytos.topology.main.Main._get_link_or_create',
575
                   return_value=(mock_link, True)):
576
            # enable link
577
            link_attrs['enabled'] = True
578
            self.napp.links = {link_id: mock_link}
579
            self.napp._load_link(link_attrs)
580
            self.assertEqual(mock_link.enable.call_count, 1)
581
            # disable link
582
            link_attrs['enabled'] = False
583
            self.napp.links = {link_id: mock_link}
584
            self.napp._load_link(link_attrs)
585
            self.assertEqual(mock_link.disable.call_count, 1)
586
587
    @patch('napps.kytos.topology.main.Main._get_link_or_create')
588
    def test_fail_load_link(self, get_link_or_create_mock):
589
        """Test fail load_link."""
590
        dpid_a = '00:00:00:00:00:00:00:01'
591
        dpid_b = '00:00:00:00:00:00:00:02'
592
        link_id = '4d42dc08522'
593
        mock_switch_a = get_switch_mock(dpid_a)
594
        mock_switch_b = get_switch_mock(dpid_b)
595
        mock_interface_a_1 = get_interface_mock('s1-eth1', 1, mock_switch_a)
596
        mock_interface_b_1 = get_interface_mock('s2-eth1', 1, mock_switch_b)
597
        mock_link = get_link_mock(mock_interface_a_1, mock_interface_b_1)
598
        mock_link.id = link_id
599
        self.napp.links = {link_id: mock_link}
600
        get_link_or_create_mock.return_value = mock_link
601
602
        link_attrs_fail = {
603
            'enabled': True,
604
            'id': link_id,
605
            'metadata': {},
606
            'endpoint_a': {
607
                'id': f"{dpid_a}:999",
608
            },
609
            'endpoint_b': {
610
                'id': f"{dpid_b}:999",
611
            }
612
        }
613
        with self.assertRaises(RestoreError):
614
            self.napp._load_link(link_attrs_fail)
615
616
        link_attrs_fail = {
617
            'enabled': True,
618
            'id': link_id,
619
            'endpoint_a': {
620
                'id': f"{dpid_a}:1",
621
            },
622
            'endpoint_b': {
623
                'id': f"{dpid_b}:1",
624
            }
625
        }
626
        with self.assertRaises(RestoreError):
627
            self.napp._load_link(link_attrs_fail)
628
629 View Code Duplication
    @patch('napps.kytos.topology.main.Main.notify_switch_links_status')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
630
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
631
    def test_enable_switch(self, mock_notify_topo, mock_sw_l_status):
632
        """Test enable_switch."""
633
        dpid = "00:00:00:00:00:00:00:01"
634
        mock_switch = get_switch_mock(dpid)
635
        self.napp.controller.switches = {dpid: mock_switch}
636
        api = get_test_client(self.napp.controller, self.napp)
637
638
        url = f'{self.server_name_url}/v3/switches/{dpid}/enable'
639
        response = api.post(url)
640
        self.assertEqual(response.status_code, 201, response.data)
641
        self.assertEqual(mock_switch.enable.call_count, 1)
642
        self.napp.topo_controller.enable_switch.assert_called_once_with(dpid)
643
        mock_notify_topo.assert_called()
644
        mock_sw_l_status.assert_called()
645
646
        # fail case
647
        mock_switch.enable.call_count = 0
648
        dpid = "00:00:00:00:00:00:00:02"
649
        url = f'{self.server_name_url}/v3/switches/{dpid}/enable'
650
        response = api.post(url)
651
        self.assertEqual(response.status_code, 404, response.data)
652
        self.assertEqual(mock_switch.enable.call_count, 0)
653
654 View Code Duplication
    @patch('napps.kytos.topology.main.Main.notify_switch_links_status')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
655
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
656
    def test_disable_switch(self, mock_notify_topo, mock_sw_l_status):
657
        """Test disable_switch."""
658
        dpid = "00:00:00:00:00:00:00:01"
659
        mock_switch = get_switch_mock(dpid)
660
        self.napp.controller.switches = {dpid: mock_switch}
661
        api = get_test_client(self.napp.controller, self.napp)
662
663
        url = f'{self.server_name_url}/v3/switches/{dpid}/disable'
664
        response = api.post(url)
665
        self.assertEqual(response.status_code, 201, response.data)
666
        self.assertEqual(mock_switch.disable.call_count, 1)
667
        self.napp.topo_controller.disable_switch.assert_called_once_with(dpid)
668
        mock_notify_topo.assert_called()
669
        mock_sw_l_status.assert_called()
670
671
        # fail case
672
        mock_switch.disable.call_count = 0
673
        dpid = "00:00:00:00:00:00:00:02"
674
        url = f'{self.server_name_url}/v3/switches/{dpid}/disable'
675
        response = api.post(url)
676
        self.assertEqual(response.status_code, 404, response.data)
677
        self.assertEqual(mock_switch.disable.call_count, 0)
678
679
    def test_get_switch_metadata(self):
680
        """Test get_switch_metadata."""
681
        dpid = "00:00:00:00:00:00:00:01"
682
        mock_switch = get_switch_mock(dpid)
683
        mock_switch.metadata = "A"
684
        self.napp.controller.switches = {dpid: mock_switch}
685
        api = get_test_client(self.napp.controller, self.napp)
686
687
        url = f'{self.server_name_url}/v3/switches/{dpid}/metadata'
688
        response = api.get(url)
689
        self.assertEqual(response.status_code, 200, response.data)
690
691
        # fail case
692
        dpid = "00:00:00:00:00:00:00:02"
693
        url = f'{self.server_name_url}/v3/switches/{dpid}/metadata'
694
        response = api.get(url)
695
        self.assertEqual(response.status_code, 404, response.data)
696
697
    @patch('napps.kytos.topology.main.Main.notify_metadata_changes')
698
    def test_add_switch_metadata(self, mock_metadata_changes):
699
        """Test add_switch_metadata."""
700
        dpid = "00:00:00:00:00:00:00:01"
701
        mock_switch = get_switch_mock(dpid)
702
        self.napp.controller.switches = {dpid: mock_switch}
703
        api = get_test_client(self.napp.controller, self.napp)
704
        payload = {"data": "A"}
705
706
        url = f'{self.server_name_url}/v3/switches/{dpid}/metadata'
707
        response = api.post(url, data=json.dumps(payload),
708
                            content_type='application/json')
709
        self.assertEqual(response.status_code, 201, response.data)
710
        mock_metadata_changes.assert_called()
711
        self.napp.topo_controller.add_switch_metadata.assert_called_once_with(
712
            dpid, payload
713
        )
714
715
        # fail case
716
        dpid = "00:00:00:00:00:00:00:02"
717
        url = f'{self.server_name_url}/v3/switches/{dpid}/metadata'
718
        response = api.post(url, data=json.dumps(payload),
719
                            content_type='application/json')
720
        self.assertEqual(response.status_code, 404, response.data)
721
722
    def test_add_switch_metadata_wrong_format(self):
723
        """Test add_switch_metadata_wrong_format."""
724
        dpid = "00:00:00:00:00:00:00:01"
725
        api = get_test_client(self.napp.controller, self.napp)
726
        payload = 'A'
727
728
        url = f'{self.server_name_url}/v3/switches/{dpid}/metadata'
729
        response = api.post(url, data=payload, content_type='application/json')
730
        self.assertEqual(response.status_code, 400, response.data)
731
732
        payload = None
733
        response = api.post(url, data=json.dumps(payload),
734
                            content_type='application/json')
735
        self.assertEqual(response.status_code, 415, response.data)
736
737
    @patch('napps.kytos.topology.main.Main.notify_metadata_changes')
738
    def test_delete_switch_metadata(self, mock_metadata_changes):
739
        """Test delete_switch_metadata."""
740
        dpid = "00:00:00:00:00:00:00:01"
741
        mock_switch = get_switch_mock(dpid)
742
        mock_switch.metadata = {"A": "A"}
743
        self.napp.controller.switches = {dpid: mock_switch}
744
        api = get_test_client(self.napp.controller, self.napp)
745
746
        key = "A"
747
        url = f'{self.server_name_url}/v3/switches/{dpid}/metadata/{key}'
748
        response = api.delete(url)
749
        self.assertEqual(mock_metadata_changes.call_count, 1)
750
        self.assertEqual(response.status_code, 200, response.data)
751
        del_key_mock = self.napp.topo_controller.delete_switch_metadata_key
752
        del_key_mock.assert_called_with(
753
            dpid, key
754
        )
755
756
        # fail case
757
        key = "A"
758
        dpid = "00:00:00:00:00:00:00:02"
759
        url = f'{self.server_name_url}/v3/switches/{dpid}/metadata/{key}'
760
        response = api.delete(url)
761
        self.assertEqual(mock_metadata_changes.call_count, 1)  # remains 1 call
762
        self.assertEqual(response.status_code, 404, response.data)
763
764 View Code Duplication
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
765
    def test_enable_interfaces(self, mock_notify_topo):
766
        """Test enable_interfaces."""
767
        dpid = '00:00:00:00:00:00:00:01'
768
        mock_switch = get_switch_mock(dpid)
769
        mock_interface_1 = get_interface_mock('s1-eth1', 1, mock_switch)
770
        mock_interface_2 = get_interface_mock('s1-eth2', 2, mock_switch)
771
        mock_switch.interfaces = {1: mock_interface_1, 2: mock_interface_2}
772
        self.napp.controller.switches = {dpid: mock_switch}
773
        api = get_test_client(self.napp.controller, self.napp)
774
775
        interface_id = '00:00:00:00:00:00:00:01:1'
776
        url = f'{self.server_name_url}/v3/interfaces/{interface_id}/enable'
777
        response = api.post(url)
778
        self.assertEqual(response.status_code, 200, response.data)
779
        self.assertEqual(mock_interface_1.enable.call_count, 1)
780
        self.assertEqual(mock_interface_2.enable.call_count, 0)
781
        self.napp.topo_controller.enable_interface.assert_called_with(
782
            interface_id
783
        )
784
        mock_notify_topo.assert_called()
785
786
        mock_interface_1.enable.call_count = 0
787
        mock_interface_2.enable.call_count = 0
788
        url = f'{self.server_name_url}/v3/interfaces/switch/{dpid}/enable'
789
        response = api.post(url)
790
        self.assertEqual(response.status_code, 200, response.data)
791
        self.napp.topo_controller.upsert_switch.assert_called_with(
792
            mock_switch.id, mock_switch.as_dict()
793
        )
794
        self.assertEqual(mock_interface_1.enable.call_count, 1)
795
        self.assertEqual(mock_interface_2.enable.call_count, 1)
796
797
        # test interface not found
798
        interface_id = '00:00:00:00:00:00:00:01:3'
799
        mock_interface_1.enable.call_count = 0
800
        mock_interface_2.enable.call_count = 0
801
        url = f'{self.server_name_url}/v3/interfaces/{interface_id}/enable'
802
        response = api.post(url)
803
        self.assertEqual(response.status_code, 404, response.data)
804
        self.assertEqual(mock_interface_1.enable.call_count, 0)
805
        self.assertEqual(mock_interface_2.enable.call_count, 0)
806
807
        # test switch not found
808
        dpid = '00:00:00:00:00:00:00:02'
809
        url = f'{self.server_name_url}/v3/interfaces/switch/{dpid}/enable'
810
        response = api.post(url)
811
        self.assertEqual(response.status_code, 404, response.data)
812
        self.assertEqual(mock_interface_1.enable.call_count, 0)
813
        self.assertEqual(mock_interface_2.enable.call_count, 0)
814
815 View Code Duplication
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
816
    def test_disable_interfaces(self, mock_notify_topo):
817
        """Test disable_interfaces."""
818
        interface_id = '00:00:00:00:00:00:00:01:1'
819
        dpid = '00:00:00:00:00:00:00:01'
820
        mock_switch = get_switch_mock(dpid)
821
        mock_interface_1 = get_interface_mock('s1-eth1', 1, mock_switch)
822
        mock_interface_2 = get_interface_mock('s1-eth2', 2, mock_switch)
823
        mock_switch.interfaces = {1: mock_interface_1, 2: mock_interface_2}
824
        self.napp.controller.switches = {dpid: mock_switch}
825
        api = get_test_client(self.napp.controller, self.napp)
826
827
        url = f'{self.server_name_url}/v3/interfaces/{interface_id}/disable'
828
        response = api.post(url)
829
        self.assertEqual(response.status_code, 200, response.data)
830
        self.napp.topo_controller.disable_interface.assert_called_with(
831
            interface_id
832
        )
833
        self.assertEqual(mock_interface_1.disable.call_count, 1)
834
        self.assertEqual(mock_interface_2.disable.call_count, 0)
835
        mock_notify_topo.assert_called()
836
837
        mock_interface_1.disable.call_count = 0
838
        mock_interface_2.disable.call_count = 0
839
        url = f'{self.server_name_url}/v3/interfaces/switch/{dpid}/disable'
840
        response = api.post(url)
841
        self.assertEqual(response.status_code, 200, response.data)
842
        self.napp.topo_controller.upsert_switch.assert_called_with(
843
            mock_switch.id, mock_switch.as_dict()
844
        )
845
        self.assertEqual(mock_interface_1.disable.call_count, 1)
846
        self.assertEqual(mock_interface_2.disable.call_count, 1)
847
848
        # test interface not found
849
        interface_id = '00:00:00:00:00:00:00:01:3'
850
        mock_interface_1.disable.call_count = 0
851
        mock_interface_2.disable.call_count = 0
852
        url = f'{self.server_name_url}/v3/interfaces/{interface_id}/disable'
853
        response = api.post(url)
854
        self.assertEqual(response.status_code, 404, response.data)
855
        self.assertEqual(mock_interface_1.disable.call_count, 0)
856
        self.assertEqual(mock_interface_2.disable.call_count, 0)
857
858
        # test switch not found
859
        dpid = '00:00:00:00:00:00:00:02'
860
        url = f'{self.server_name_url}/v3/interfaces/switch/{dpid}/disable'
861
        response = api.post(url)
862
        self.assertEqual(response.status_code, 404, response.data)
863
        self.assertEqual(mock_interface_1.disable.call_count, 0)
864
        self.assertEqual(mock_interface_2.disable.call_count, 0)
865
866
    def test_get_interface_metadata(self):
867
        """Test get_interface_metada."""
868
        interface_id = '00:00:00:00:00:00:00:01:1'
869
        dpid = '00:00:00:00:00:00:00:01'
870
        mock_switch = get_switch_mock(dpid)
871
        mock_interface = get_interface_mock('s1-eth1', 1, mock_switch)
872
        mock_interface.metadata = {"metada": "A"}
873
        mock_switch.interfaces = {1: mock_interface}
874
        self.napp.controller.switches = {dpid: mock_switch}
875
        api = get_test_client(self.napp.controller, self.napp)
876
877
        url = f'{self.server_name_url}/v3/interfaces/{interface_id}/metadata'
878
        response = api.get(url)
879
        self.assertEqual(response.status_code, 200, response.data)
880
881
        # fail case switch not found
882
        interface_id = '00:00:00:00:00:00:00:02:1'
883
        url = f'{self.server_name_url}/v3/interfaces/{interface_id}/metadata'
884
        response = api.get(url)
885
        self.assertEqual(response.status_code, 404, response.data)
886
887
        # fail case interface not found
888
        interface_id = '00:00:00:00:00:00:00:01:2'
889
        url = f'{self.server_name_url}/v3/interfaces/{interface_id}/metadata'
890
        response = api.get(url)
891
        self.assertEqual(response.status_code, 404, response.data)
892
893
    @patch('napps.kytos.topology.main.Main.notify_metadata_changes')
894
    def test_add_interface_metadata(self, mock_metadata_changes):
895
        """Test add_interface_metadata."""
896
        interface_id = '00:00:00:00:00:00:00:01:1'
897
        dpid = '00:00:00:00:00:00:00:01'
898
        mock_switch = get_switch_mock(dpid)
899
        mock_interface = get_interface_mock('s1-eth1', 1, mock_switch)
900
        mock_interface.metadata = {"metada": "A"}
901
        mock_switch.interfaces = {1: mock_interface}
902
        self.napp.controller.switches = {dpid: mock_switch}
903
        api = get_test_client(self.napp.controller, self.napp)
904
905
        url = f'{self.server_name_url}/v3/interfaces/{interface_id}/metadata'
906
        payload = {"metada": "A"}
907
        response = api.post(url, data=json.dumps(payload),
908
                            content_type='application/json')
909
        self.assertEqual(response.status_code, 201, response.data)
910
        mock_metadata_changes.assert_called()
911
912
        # fail case switch not found
913
        interface_id = '00:00:00:00:00:00:00:02:1'
914
        url = f'{self.server_name_url}/v3/interfaces/{interface_id}/metadata'
915
        response = api.post(url, data=json.dumps(payload),
916
                            content_type='application/json')
917
        self.assertEqual(response.status_code, 404, response.data)
918
919
        # fail case interface not found
920
        interface_id = '00:00:00:00:00:00:00:01:2'
921
        url = f'{self.server_name_url}/v3/interfaces/{interface_id}/metadata'
922
        response = api.post(url, data=json.dumps(payload),
923
                            content_type='application/json')
924
        self.assertEqual(response.status_code, 404, response.data)
925
926
    def test_add_interface_metadata_wrong_format(self):
927
        """Test add_interface_metadata_wrong_format."""
928
        dpid = "00:00:00:00:00:00:00:01:1"
929
        api = get_test_client(self.napp.controller, self.napp)
930
        payload = 'A'
931
932
        url = f'{self.server_name_url}/v3/interfaces/{dpid}/metadata'
933
        response = api.post(url, data=payload, content_type='application/json')
934
        self.assertEqual(response.status_code, 400, response.data)
935
936
        payload = None
937
        response = api.post(url, data=json.dumps(payload),
938
                            content_type='application/json')
939
        self.assertEqual(response.status_code, 415, response.data)
940
941
    def test_delete_interface_metadata(self):
942
        """Test delete_interface_metadata."""
943
        interface_id = '00:00:00:00:00:00:00:01:1'
944
        dpid = '00:00:00:00:00:00:00:01'
945
        iface_url = '/v3/interfaces/'
946
        mock_switch = get_switch_mock(dpid)
947
        mock_interface = get_interface_mock('s1-eth1', 1, mock_switch)
948
        mock_interface.remove_metadata.side_effect = [True, False]
949
        mock_interface.metadata = {"A": "A"}
950
        mock_switch.interfaces = {1: mock_interface}
951
        self.napp.controller.switches = {'00:00:00:00:00:00:00:01':
952
                                         mock_switch}
953
        api = get_test_client(self.napp.controller, self.napp)
954
955
        key = 'A'
956
        url = f'{self.server_name_url}{iface_url}{interface_id}/metadata/{key}'
957
        response = api.delete(url)
958
        self.assertEqual(response.status_code, 200, response.data)
959
        del_key_mock = self.napp.topo_controller.delete_interface_metadata_key
960
        del_key_mock.assert_called_once_with(interface_id, key)
961
962
        # fail case switch not found
963
        key = 'A'
964
        interface_id = '00:00:00:00:00:00:00:02:1'
965
        url = f'{self.server_name_url}{iface_url}{interface_id}/metadata/{key}'
966
        response = api.delete(url)
967
        self.assertEqual(response.status_code, 404, response.data)
968
969
        # fail case interface not found
970
        key = 'A'
971
        interface_id = '00:00:00:00:00:00:00:01:2'
972
        url = f'{self.server_name_url}{iface_url}{interface_id}/metadata/{key}'
973
        response = api.delete(url)
974
        self.assertEqual(response.status_code, 404, response.data)
975
976
        # fail case metadata not found
977
        key = 'B'
978
        interface_id = '00:00:00:00:00:00:00:01:1'
979
        url = f'{self.server_name_url}{iface_url}{interface_id}/metadata/{key}'
980
        response = api.delete(url)
981
        self.assertEqual(response.status_code, 404, response.data)
982
983
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
984
    def test_enable_link(self, mock_notify_topo):
985
        """Test enable_link."""
986
        mock_link = MagicMock(Link)
987
        self.napp.links = {'1': mock_link}
988
        api = get_test_client(self.napp.controller, self.napp)
989
990
        link_id = "1"
991
        url = f'{self.server_name_url}/v3/links/{link_id}/enable'
992
        response = api.post(url)
993
        self.assertEqual(response.status_code, 201, response.data)
994
        self.assertEqual(mock_link.enable.call_count, 1)
995
        self.napp.topo_controller.enable_link.assert_called_with(link_id)
996
        mock_notify_topo.assert_called()
997
998
        # fail case
999
        link_id = 2
1000
        url = f'{self.server_name_url}/v3/links/{link_id}/enable'
1001
        response = api.post(url)
1002
        self.assertEqual(response.status_code, 404, response.data)
1003
1004
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1005
    def test_disable_link(self, mock_notify_topo):
1006
        """Test disable_link."""
1007
        mock_link = MagicMock(Link)
1008
        self.napp.links = {'1': mock_link}
1009
        api = get_test_client(self.napp.controller, self.napp)
1010
1011
        link_id = "1"
1012
        url = f'{self.server_name_url}/v3/links/{link_id}/disable'
1013
        response = api.post(url)
1014
        self.assertEqual(response.status_code, 201, response.data)
1015
        self.assertEqual(mock_link.disable.call_count, 1)
1016
        self.assertEqual(mock_notify_topo.call_count, 1)
1017
        self.napp.topo_controller.disable_link.assert_called_with(link_id)
1018
1019
        # fail case
1020
        link_id = 2
1021
        url = f'{self.server_name_url}/v3/links/{link_id}/disable'
1022
        response = api.post(url)
1023
        self.assertEqual(response.status_code, 404, response.data)
1024
1025
    def test_handle_lldp_status_updated(self):
1026
        """Test handle_lldp_status_updated."""
1027
        event = MagicMock()
1028
        self.napp.controller.buffers.app.put = MagicMock()
1029
1030
        dpid_a = "00:00:00:00:00:00:00:01"
1031
        dpid_b = "00:00:00:00:00:00:00:02"
1032
        dpids = [dpid_a, dpid_b]
1033
        interface_ids = [f"{dpid}:1" for dpid in dpids]
1034
1035
        mock_switch_a = get_switch_mock(dpid_a, 0x04)
1036
        mock_switch_b = get_switch_mock(dpid_b, 0x04)
1037
        self.napp.controller.switches = {dpid_a: mock_switch_a,
1038
                                         dpid_b: mock_switch_b}
1039
1040
        event.content = {"interface_ids": interface_ids, "state": "disabled"}
1041
        self.napp.handle_lldp_status_updated(event)
1042
1043
        mock_put = self.napp.controller.buffers.app.put
1044
        assert mock_put.call_count == len(interface_ids)
1045
1046
    def test_handle_topo_controller_upsert_switch(self):
1047
        """Test handle_topo_controller_upsert_switch."""
1048
        event = MagicMock()
1049
        self.napp.handle_topo_controller_upsert_switch(event)
1050
        mock = self.napp.topo_controller.upsert_switch
1051
        mock.assert_called_with(event.id, event.as_dict())
1052
1053
    def test_get_link_metadata(self):
1054
        """Test get_link_metadata."""
1055
        mock_link = MagicMock(Link)
1056
        mock_link.metadata = "A"
1057
        self.napp.links = {'1': mock_link}
1058
        msg_success = {"metadata": "A"}
1059
        api = get_test_client(self.napp.controller, self.napp)
1060
1061
        link_id = 1
1062
        url = f'{self.server_name_url}/v3/links/{link_id}/metadata'
1063
        response = api.get(url)
1064
        self.assertEqual(response.status_code, 200, response.data)
1065
        self.assertEqual(msg_success, json.loads(response.data))
1066
1067
        # fail case
1068
        link_id = 2
1069
        url = f'{self.server_name_url}/v3/links/{link_id}/metadata'
1070
        response = api.get(url)
1071
        self.assertEqual(response.status_code, 404, response.data)
1072
1073
    @patch('napps.kytos.topology.main.Main.notify_metadata_changes')
1074
    def test_add_link_metadata(self, mock_metadata_changes):
1075
        """Test add_link_metadata."""
1076
        mock_link = MagicMock(Link)
1077
        mock_link.metadata = "A"
1078
        self.napp.links = {'1': mock_link}
1079
        payload = {"metadata": "A"}
1080
        api = get_test_client(self.napp.controller, self.napp)
1081
1082
        link_id = 1
1083
        url = f'{self.server_name_url}/v3/links/{link_id}/metadata'
1084
        response = api.post(url, data=json.dumps(payload),
1085
                            content_type='application/json')
1086
        self.assertEqual(response.status_code, 201, response.data)
1087
        mock_metadata_changes.assert_called()
1088
1089
        # fail case
1090
        link_id = 2
1091
        url = f'{self.server_name_url}/v3/links/{link_id}/metadata'
1092
        response = api.post(url, data=json.dumps(payload),
1093
                            content_type='application/json')
1094
        self.assertEqual(response.status_code, 404, response.data)
1095
1096
    def test_add_link_metadata_wrong_format(self):
1097
        """Test add_link_metadata_wrong_format."""
1098
        link_id = 'cf0f4071be426b3f745027f5d22'
1099
        api = get_test_client(self.napp.controller, self.napp)
1100
        payload = "A"
1101
1102
        url = f'{self.server_name_url}/v3/links/{link_id}/metadata'
1103
        response = api.post(url, data=payload,
1104
                            content_type='application/json')
1105
        self.assertEqual(response.status_code, 400, response.data)
1106
1107
        payload = None
1108
        response = api.post(url, data=json.dumps(payload),
1109
                            content_type='application/json')
1110
        self.assertEqual(response.status_code, 415, response.data)
1111
1112
    @patch('napps.kytos.topology.main.Main.notify_metadata_changes')
1113
    def test_delete_link_metadata(self, mock_metadata_changes):
1114
        """Test delete_link_metadata."""
1115
        mock_link = MagicMock(Link)
1116
        mock_link.metadata = {"A": "A"}
1117
        mock_link.remove_metadata.side_effect = [True, False]
1118
        self.napp.links = {'1': mock_link}
1119
        api = get_test_client(self.napp.controller, self.napp)
1120
1121
        link_id = 1
1122
        key = 'A'
1123
        url = f'{self.server_name_url}/v3/links/{link_id}/metadata/{key}'
1124
        response = api.delete(url)
1125
        self.assertEqual(response.status_code, 200, response.data)
1126
        del_mock = self.napp.topo_controller.delete_link_metadata_key
1127
        del_mock.assert_called_once_with(mock_link.id, key)
1128
        mock_metadata_changes.assert_called()
1129
1130
        # fail case link not found
1131
        link_id = 2
1132
        key = 'A'
1133
        url = f'{self.server_name_url}/v3/links/{link_id}/metadata/{key}'
1134
        response = api.delete(url)
1135
        self.assertEqual(response.status_code, 404, response.data)
1136
1137
        # fail case metadata not found
1138
        link_id = 1
1139
        key = 'B'
1140
        url = f'{self.server_name_url}/v3/links/{link_id}/metadata/{key}'
1141
        response = api.delete(url)
1142
        self.assertEqual(response.status_code, 404, response.data)
1143
1144
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1145
    def test_handle_new_switch(self, mock_notify_topology_update):
1146
        """Test handle_new_switch."""
1147
        mock_event = MagicMock()
1148
        mock_switch = create_autospec(Switch)
1149
        mock_event.content['switch'] = mock_switch
1150
        self.napp.handle_new_switch(mock_event)
1151
        mock = self.napp.topo_controller.upsert_switch
1152
        mock.assert_called_once_with(mock_event.content['switch'].id,
1153
                                     mock_event.content['switch'].as_dict())
1154
        mock_notify_topology_update.assert_called()
1155
1156
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1157
    def test_handle_connection_lost(self, mock_notify_topology_update):
1158
        """Test handle connection_lost."""
1159
        mock_event = MagicMock()
1160
        mock_switch = create_autospec(Switch)
1161
        mock_switch.return_value = True
1162
        mock_event.content['source'] = mock_switch
1163
        self.napp.handle_connection_lost(mock_event)
1164
        mock_notify_topology_update.assert_called()
1165
1166
    @patch('napps.kytos.topology.main.Main.handle_interface_link_up')
1167
    def test_handle_interface_created(self, mock_link_up):
1168
        """Test handle_interface_created."""
1169
        mock_event = MagicMock()
1170
        mock_interface = create_autospec(Interface)
1171
        mock_interface.id = "1"
1172
        mock_event.content = {'interface': mock_interface}
1173
        self.napp.handle_interface_created(mock_event)
1174
        mock_link_up.assert_called()
1175
1176
    @patch('napps.kytos.topology.main.Main.handle_interface_link_up')
1177
    def test_handle_interface_created_inactive(self, mock_link_up):
1178
        """Test handle_interface_created inactive."""
1179
        mock_event = MagicMock()
1180
        mock_interface = create_autospec(Interface)
1181
        mock_interface.id = "1"
1182
        mock_event.content = {'interface': mock_interface}
1183
        mock_interface.is_active.return_value = False
1184
        self.napp.handle_interface_created(mock_event)
1185
        mock_link_up.assert_not_called()
1186
1187
    def test_handle_interfaces_created(self):
1188
        """Test handle_interfaces_created."""
1189
        buffers_app_mock = MagicMock()
1190
        self.napp.controller.buffers.app = buffers_app_mock
1191
        mock_switch = create_autospec(Switch)
1192
        mock_event = MagicMock()
1193
        mock_interface = create_autospec(Interface)
1194
        mock_interface.id = "1"
1195
        mock_interface.switch = mock_switch
1196
        mock_interface_two = create_autospec(Interface)
1197
        mock_interface_two.id = "2"
1198
        mock_event.content = {'interfaces': [mock_interface,
1199
                              mock_interface_two]}
1200
        self.napp.handle_interfaces_created(mock_event)
1201
        upsert_mock = self.napp.topo_controller.upsert_switch
1202
        upsert_mock.assert_called_with(mock_switch.id, mock_switch.as_dict())
1203
        assert self.napp.controller.buffers.app.put.call_count == 2
1204
1205
    @patch('napps.kytos.topology.main.Main.handle_interface_link_down')
1206
    def test_handle_interface_down(self, mock_handle_interface_link_down):
1207
        """Test handle interface down."""
1208
        mock_event = MagicMock()
1209
        mock_interface = create_autospec(Interface)
1210
        mock_event.content['interface'] = mock_interface
1211
        self.napp.handle_interface_down(mock_event)
1212
        mock_handle_interface_link_down.assert_called()
1213
1214
    @patch('napps.kytos.topology.main.Main.handle_interface_down')
1215
    def test_interface_deleted(self, mock_handle_interface_link_down):
1216
        """Test interface deleted."""
1217
        mock_event = MagicMock()
1218
        self.napp.handle_interface_deleted(mock_event)
1219
        mock_handle_interface_link_down.assert_called()
1220
1221
    @patch('napps.kytos.topology.main.Main._get_link_from_interface')
1222
    @patch('napps.kytos.topology.main.Main.notify_link_up_if_status')
1223
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1224
    def test_interface_link_up(self, *args):
1225
        """Test interface link_up."""
1226
        (mock_notify_topology_update,
1227
         mock_notify_link_up_if_status,
1228
         mock_link_from_interface) = args
1229
1230
        tnow = time.time()
1231
        mock_interface_a = create_autospec(Interface)
1232
        mock_interface_a.is_active.return_value = False
1233
        mock_interface_b = create_autospec(Interface)
1234
        mock_interface_b.is_active.return_value = True
1235
        mock_link = create_autospec(Link)
1236
        mock_link.get_metadata.return_value = tnow
1237
        mock_link.is_active.side_effect = [False, True]
1238
        mock_link.endpoint_a = mock_interface_a
1239
        mock_link.endpoint_b = mock_interface_b
1240
        mock_link_from_interface.return_value = mock_link
1241
        mock_link.status = EntityStatus.UP
1242
        self.napp.handle_interface_link_up(mock_interface_a)
1243
        mock_notify_topology_update.assert_called()
1244
        mock_link.extend_metadata.assert_called()
1245
        mock_link.activate.assert_called()
1246
        self.napp.topo_controller.activate_link.assert_called()
1247
        mock_notify_link_up_if_status.assert_called()
1248
1249
    @patch('napps.kytos.topology.main.Main._get_link_from_interface')
1250
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1251
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1252
    def test_interface_link_down(self, *args):
1253
        """Test interface link down."""
1254
        (mock_status_change, mock_topology_update,
1255
         mock_link_from_interface) = args
1256
1257
        mock_event = MagicMock()
1258
        mock_interface = create_autospec(Interface)
1259
        mock_link = create_autospec(Link)
1260
        mock_link.is_active.return_value = True
1261
        mock_link_from_interface.return_value = mock_link
1262
        mock_event.content['interface'] = mock_interface
1263
        self.napp.handle_interface_link_down(mock_event)
1264
        mock_topology_update.assert_called()
1265
        mock_status_change.assert_called()
1266
1267
    @patch('napps.kytos.topology.main.Main._get_link_from_interface')
1268
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1269
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1270
    def test_handle_link_down(self, *args):
1271
        """Test interface link down."""
1272
        (mock_status_change, mock_topology_update,
1273
         mock_link_from_interface) = args
1274
1275
        mock_interface = create_autospec(Interface)
1276
        mock_link = create_autospec(Link)
1277
        mock_link.is_active.return_value = True
1278
        mock_link_from_interface.return_value = mock_link
1279
        self.napp.handle_link_down(mock_interface)
1280
        assert self.napp.topo_controller.deactivate_link.call_count == 1
1281
        mock_interface.deactivate.assert_called()
1282
        mock_link.deactivate.assert_called()
1283
        assert mock_topology_update.call_count == 1
1284
        mock_status_change.assert_called()
1285
        assert self.napp.topo_controller.deactivate_interface.call_count == 1
1286
1287
    @patch('napps.kytos.topology.main.Main._get_link_from_interface')
1288
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1289
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1290
    def test_handle_link_down_not_active(self, *args):
1291
        """Test interface link down with link not active."""
1292
        (mock_status_change, mock_topology_update,
1293
         mock_link_from_interface) = args
1294
1295
        mock_interface = create_autospec(Interface)
1296
        mock_link = create_autospec(Link)
1297
        mock_link.is_active.return_value = False
1298
        mock_link_from_interface.return_value = mock_link
1299
        mock_link.get_metadata.return_value = False
1300
        self.napp.handle_link_down(mock_interface)
1301
        assert self.napp.topo_controller.deactivate_link.call_count == 0
1302
        mock_interface.deactivate.assert_called()
1303
        mock_topology_update.assert_called()
1304
        mock_status_change.assert_not_called()
1305
        assert self.napp.topo_controller.deactivate_interface.call_count == 1
1306
1307
    @patch('napps.kytos.topology.main.Main._get_link_from_interface')
1308
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1309
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1310
    def test_handle_link_down_not_active_last_status(self, *args):
1311
        """Test interface link down with link not active."""
1312
        (mock_status_change, mock_topology_update,
1313
         mock_link_from_interface) = args
1314
1315
        mock_interface = create_autospec(Interface)
1316
        mock_link = create_autospec(Link)
1317
        mock_link.is_active.return_value = False
1318
        mock_link_from_interface.return_value = mock_link
1319
        mock_link.get_metadata.return_value = True
1320
        self.napp.handle_link_down(mock_interface)
1321
        deactivate_link = self.napp.topo_controller.deactivate_link
1322
        assert deactivate_link.call_count == 1
1323
        assert deactivate_link.call_args[0][0] == mock_link.id
1324
        assert not deactivate_link.call_args[0][2]
1325
        mock_interface.deactivate.assert_called()
1326
        mock_topology_update.assert_called()
1327
        mock_status_change.assert_called()
1328
        assert self.napp.topo_controller.deactivate_interface.call_count == 1
1329
1330
    @patch('napps.kytos.topology.main.Main._get_link_from_interface')
1331
    @patch('napps.kytos.topology.main.Main.notify_link_up_if_status')
1332
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1333
    def test_handle_link_up(self, *args):
1334
        """Test handle link up."""
1335
        (mock_notify_topology_update,
1336
         mock_notify_link_up_if_status,
1337
         mock_link_from_interface) = args
1338
1339
        mock_interface = create_autospec(Interface)
1340
        mock_link = MagicMock(status=EntityStatus.UP)
1341
        mock_link.is_active.return_value = True
1342
        mock_link_from_interface.return_value = mock_link
1343
        self.napp.handle_link_up(mock_interface)
1344
        assert self.napp.topo_controller.activate_link.call_count == 1
1345
        mock_interface.activate.assert_called()
1346
        assert mock_notify_link_up_if_status.call_count == 1
1347
        mock_notify_topology_update.assert_called()
1348
1349
    @patch('time.sleep')
1350
    @patch('napps.kytos.topology.main.Main._get_link_from_interface')
1351
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1352
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1353
    def test_handle_link_up_intf_down(self, *args):
1354
        """Test handle link up but one intf down."""
1355
        (mock_status_change, mock_topology_update,
1356
         mock_link_from_interface, _) = args
1357
1358
        mock_interface = create_autospec(Interface)
1359
        mock_link = MagicMock()
1360
        mock_link.endpoint_a.is_active.return_value = False
1361
        mock_link.is_active.return_value = False
1362
        mock_link_from_interface.return_value = mock_link
1363
        self.napp.handle_link_up(mock_interface)
1364
        mock_interface.activate.assert_called()
1365
        assert self.napp.topo_controller.activate_link.call_count == 0
1366
        assert mock_topology_update.call_count == 1
1367
        mock_status_change.assert_not_called()
1368
1369
    @patch('napps.kytos.topology.main.Main._get_link_or_create')
1370
    @patch('napps.kytos.topology.main.Main.notify_link_up_if_status')
1371
    def test_add_links(self, *args):
1372
        """Test add_links."""
1373
        (mock_notify_link_up_if_status,
1374
         mock_get_link_or_create) = args
1375
1376
        mock_link = MagicMock()
1377
        mock_get_link_or_create.return_value = (mock_link, True)
1378
        mock_event = MagicMock()
1379
        mock_intf_a = MagicMock()
1380
        mock_intf_b = MagicMock()
1381
        mock_event.content = {"interface_a": mock_intf_a,
1382
                              "interface_b": mock_intf_b}
1383
        self.napp.add_links(mock_event)
1384
        mock_link.extend_metadata.assert_called()
1385
        mock_get_link_or_create.assert_called()
1386
        mock_notify_link_up_if_status.assert_called()
1387
        mock_intf_a.update_link.assert_called()
1388
        mock_intf_b.update_link.assert_called()
1389
        mock_link.endpoint_a = mock_intf_a
1390
        mock_link.endpoint_b = mock_intf_b
1391
1392
    @patch('napps.kytos.topology.main.KytosEvent')
1393
    @patch('kytos.core.buffers.KytosEventBuffer.put')
1394
    def test_notify_switch_enabled(self, *args):
1395
        """Test notify switch enabled."""
1396
        dpid = "00:00:00:00:00:00:00:01"
1397
        (mock_buffers_put, mock_event) = args
1398
        self.napp.notify_switch_enabled(dpid)
1399
        mock_event.assert_called()
1400
        mock_buffers_put.assert_called()
1401
1402
    @patch('napps.kytos.topology.main.KytosEvent')
1403
    @patch('kytos.core.buffers.KytosEventBuffer.put')
1404
    def test_notify_switch_disabled(self, *args):
1405
        """Test notify switch disabled."""
1406
        dpid = "00:00:00:00:00:00:00:01"
1407
        (mock_buffers_put, mock_event) = args
1408
        self.napp.notify_switch_disabled(dpid)
1409
        mock_event.assert_called()
1410
        mock_buffers_put.assert_called()
1411
1412
    @patch('napps.kytos.topology.main.KytosEvent')
1413
    @patch('kytos.core.buffers.KytosEventBuffer.put')
1414
    def test_notify_topology_update(self, *args):
1415
        """Test notify_topology_update."""
1416
        (mock_buffers_put, mock_event) = args
1417
        self.napp.notify_topology_update()
1418
        mock_event.assert_called()
1419
        mock_buffers_put.assert_called()
1420
1421
    @patch('kytos.core.buffers.KytosEventBuffer.put')
1422
    def test_notify_current_topology(self, mock_buffers_put):
1423
        """Test notify_current_topology."""
1424
        self.napp.notify_current_topology()
1425
        mock_buffers_put.assert_called()
1426
        args = mock_buffers_put.call_args
1427
        expected_event = args[0][0]
1428
        assert expected_event.name == "kytos/topology.current"
1429
        assert "topology" in expected_event.content
1430
1431
    @patch('napps.kytos.topology.main.KytosEvent')
1432
    @patch('kytos.core.buffers.KytosEventBuffer.put')
1433
    def test_notify_link_status_change(self, *args):
1434
        """Test notify link status change."""
1435
        (mock_buffers_put, mock_event) = args
1436
        mock_link = create_autospec(Link)
1437
        self.napp.notify_link_status_change(mock_link)
1438
        mock_event.assert_called()
1439
        mock_buffers_put.assert_called()
1440
1441
    @patch('napps.kytos.topology.main.KytosEvent')
1442
    @patch('kytos.core.buffers.KytosEventBuffer.put')
1443
    def test_notify_metadata_changes(self, *args):
1444
        """Test notify metadata changes."""
1445
        (mock_buffers_put, mock_event) = args
1446
        count = 0
1447
        for spec in [Switch, Interface, Link]:
1448
            mock_obj = create_autospec(spec)
1449
            mock_obj.metadata = {"some_key": "some_value"}
1450
            self.napp.notify_metadata_changes(mock_obj, 'added')
1451
            self.assertEqual(mock_event.call_count, count+1)
1452
            self.assertEqual(mock_buffers_put.call_count, count+1)
1453
            count += 1
1454
        with self.assertRaises(ValueError):
1455
            self.napp.notify_metadata_changes(MagicMock(), 'added')
1456
1457
    @patch('napps.kytos.topology.main.KytosEvent')
1458
    @patch('kytos.core.buffers.KytosEventBuffer.put')
1459
    def test_notify_port_created(self, *args):
1460
        """Test notify port created."""
1461
        (mock_buffers_put, mock_kytos_event) = args
1462
        mock_event = MagicMock()
1463
        self.napp.notify_port_created(mock_event)
1464
        mock_kytos_event.assert_called()
1465
        mock_buffers_put.assert_called()
1466
1467
    def test_get_links_from_interfaces(self) -> None:
1468
        """Test get_links_from_interfaces."""
1469
        interfaces = [MagicMock(id=f"intf{n}") for n in range(4)]
1470
        links = {
1471
            "link1": MagicMock(id="link1",
1472
                               endpoint_a=interfaces[0],
1473
                               endpoint_b=interfaces[1]),
1474
            "link2": MagicMock(id="link2",
1475
                               endpoint_a=interfaces[2],
1476
                               endpoint_b=interfaces[3]),
1477
        }
1478
        self.napp.links = links
1479
        response = self.napp.get_links_from_interfaces(interfaces)
1480
        assert links == response
1481
        response = self.napp.get_links_from_interfaces(interfaces[:2])
1482
        assert response == {"link1": links["link1"]}
1483
1484
    def test_handle_link_liveness_disabled(self) -> None:
1485
        """Test handle_link_liveness_disabled."""
1486
        interfaces = [MagicMock(id=f"intf{n}") for n in range(4)]
1487
        links = {
1488
            "link1": MagicMock(id="link1",
1489
                               endpoint_a=interfaces[0],
1490
                               endpoint_b=interfaces[1]),
1491
            "link2": MagicMock(id="link2",
1492
                               endpoint_a=interfaces[2],
1493
                               endpoint_b=interfaces[3]),
1494
        }
1495
        self.napp.links = links
1496
        self.napp.notify_topology_update = MagicMock()
1497
        self.napp.notify_link_status_change = MagicMock()
1498
1499
        self.napp.handle_link_liveness_disabled(interfaces)
1500
1501
        bulk_delete = self.napp.topo_controller.bulk_delete_link_metadata_key
1502
        assert bulk_delete.call_count == 1
1503
        assert self.napp.notify_topology_update.call_count == 1
1504
        assert self.napp.notify_link_status_change.call_count == len(links)
1505
1506
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1507
    def test_handle_link_maintenance_start(self, status_change_mock):
1508
        """Test handle_link_maintenance_start."""
1509
        link1 = MagicMock()
1510
        link1.id = 2
1511
        link2 = MagicMock()
1512
        link2.id = 3
1513
        link3 = MagicMock()
1514
        link3.id = 4
1515
        content = {'links': [link1, link2]}
1516
        event = MagicMock()
1517
        event.content = content
1518
        self.napp.links = {2: link1, 4: link3}
1519
        self.napp.handle_link_maintenance_start(event)
1520
        status_change_mock.assert_called_once_with(link1, reason='maintenance')
1521
1522
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1523
    def test_handle_link_maintenance_end(self, status_change_mock):
1524
        """Test handle_link_maintenance_end."""
1525
        link1 = MagicMock()
1526
        link1.id = 2
1527
        link2 = MagicMock()
1528
        link2.id = 3
1529
        link3 = MagicMock()
1530
        link3.id = 4
1531
        content = {'links': [link1, link2]}
1532
        event = MagicMock()
1533
        event.content = content
1534
        self.napp.links = {2: link1, 4: link3}
1535
        self.napp.handle_link_maintenance_end(event)
1536
        status_change_mock.assert_called_once_with(link1, reason='maintenance')
1537
1538 View Code Duplication
    @patch('napps.kytos.topology.main.Main.handle_link_down')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1539
    def test_handle_switch_maintenance_start(self, handle_link_down_mock):
1540
        """Test handle_switch_maintenance_start."""
1541
        switch1 = MagicMock()
1542
        interface1 = MagicMock()
1543
        interface1.is_active.return_value = True
1544
        interface2 = MagicMock()
1545
        interface2.is_active.return_value = False
1546
        interface3 = MagicMock()
1547
        interface3.is_active.return_value = True
1548
        switch1.interfaces = {1: interface1, 2: interface2, 3: interface3}
1549
        switch2 = MagicMock()
1550
        interface4 = MagicMock()
1551
        interface4.is_active.return_value = False
1552
        interface5 = MagicMock()
1553
        interface5.is_active.return_value = True
1554
        switch2.interfaces = {1: interface4, 2: interface5}
1555
        content = {'switches': [switch1, switch2]}
1556
        event = MagicMock()
1557
        event.content = content
1558
        self.napp.handle_switch_maintenance_start(event)
1559
        self.assertEqual(handle_link_down_mock.call_count, 3)
1560
1561 View Code Duplication
    @patch('napps.kytos.topology.main.Main.handle_link_up')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1562
    def test_handle_switch_maintenance_end(self, handle_link_up_mock):
1563
        """Test handle_switch_maintenance_end."""
1564
        switch1 = MagicMock()
1565
        interface1 = MagicMock()
1566
        interface1.is_active.return_value = True
1567
        interface2 = MagicMock()
1568
        interface2.is_active.return_value = False
1569
        interface3 = MagicMock()
1570
        interface3.is_active.return_value = True
1571
        switch1.interfaces = {1: interface1, 2: interface2, 3: interface3}
1572
        switch2 = MagicMock()
1573
        interface4 = MagicMock()
1574
        interface4.is_active.return_value = False
1575
        interface5 = MagicMock()
1576
        interface5.is_active.return_value = True
1577
        switch2.interfaces = {1: interface4, 2: interface5}
1578
        content = {'switches': [switch1, switch2]}
1579
        event = MagicMock()
1580
        event.content = content
1581
        self.napp.handle_switch_maintenance_end(event)
1582
        self.assertEqual(handle_link_up_mock.call_count, 5)
1583
1584
    def test_link_status_hook_link_up_timer(self) -> None:
1585
        """Test status hook link up timer."""
1586
        last_change = time.time() - self.napp.link_up_timer + 5
1587
        link = MagicMock(metadata={"last_status_change": last_change})
1588
        link.is_active.return_value = True
1589
        link.is_enabled.return_value = True
1590
        res = self.napp.link_status_hook_link_up_timer(link)
1591
        assert res == EntityStatus.DOWN
1592
1593
        last_change = time.time() - self.napp.link_up_timer
1594
        link.metadata["last_status_change"] = last_change
1595
        res = self.napp.link_status_hook_link_up_timer(link)
1596
        assert res is None
1597
1598
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1599
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1600
    @patch('time.sleep')
1601
    def test_notify_link_up_if_status(
1602
        self,
1603
        mock_sleep,
1604
        mock_notify_topo,
1605
        mock_notify_link,
1606
    ) -> None:
1607
        """Test notify link up if status."""
1608
1609
        link = MagicMock(status=EntityStatus.UP)
1610
        link.get_metadata.return_value = now()
1611
        assert not self.napp.notify_link_up_if_status(link, "link up")
1612
        link.update_metadata.assert_not_called()
1613
        mock_notify_topo.assert_not_called()
1614
        mock_notify_link.assert_not_called()
1615
1616
        link = MagicMock(status=EntityStatus.UP)
1617
        link.get_metadata.return_value = now() - timedelta(seconds=60)
1618
        assert not self.napp.notify_link_up_if_status(link, "link up")
1619
        link.update_metadata.assert_called()
1620
        self.napp.topo_controller.add_link_metadata.assert_called()
1621
        mock_notify_topo.assert_called()
1622
        mock_notify_link.assert_called()
1623
1624
        assert mock_sleep.call_count == 2
1625
1626
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1627
    def test_notify_switch_links_status(self, mock_notify_link_status_change):
1628
        """Test switch links notification when switch status change"""
1629
        buffers_app_mock = MagicMock()
1630
        self.napp.controller.buffers.app = buffers_app_mock
1631
        dpid = "00:00:00:00:00:00:00:01"
1632
        mock_switch = get_switch_mock(dpid)
1633
        link1 = MagicMock()
1634
        link1.endpoint_a.switch = mock_switch
1635
        self.napp.links = {1: link1}
1636
1637
        self.napp.notify_switch_links_status(mock_switch, "link enabled")
1638
        assert self.napp.controller.buffers.app.put.call_count == 1
1639
1640
        self.napp.notify_switch_links_status(mock_switch, "link disabled")
1641
        assert self.napp.controller.buffers.app.put.call_count == 1
1642
        assert mock_notify_link_status_change.call_count == 1
1643
1644
        # Without notification
1645
        link1.endpoint_a.switch = None
1646
        self.napp.notify_switch_links_status(mock_switch, "link enabled")
1647
        assert self.napp.controller.buffers.app.put.call_count == 1
1648
1649
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1650
    @patch('napps.kytos.topology.main.Main._get_link_from_interface')
1651
    def test_notify_interface_link_status(self, *args):
1652
        """Test interface links notification when enable"""
1653
        (mock_get_link_from_interface,
1654
         mock_notify_link_status_change) = args
1655
        buffers_app_mock = MagicMock()
1656
        self.napp.controller.buffers.app = buffers_app_mock
1657
        mock_link = MagicMock()
1658
        mock_get_link_from_interface.return_value = mock_link
1659
        self.napp.notify_interface_link_status(MagicMock(), "link enabled")
1660
        assert mock_get_link_from_interface.call_count == 1
1661
        assert self.napp.controller.buffers.app.put.call_count == 1
1662
1663
        self.napp.notify_interface_link_status(MagicMock(), "link disabled")
1664
        assert mock_get_link_from_interface.call_count == 2
1665
        assert mock_notify_link_status_change.call_count == 1
1666
        assert self.napp.controller.buffers.app.put.call_count == 1
1667
1668
        # Without notification
1669
        mock_get_link_from_interface.return_value = None
1670
        self.napp.notify_interface_link_status(MagicMock(), "link enabled")
1671
        assert mock_get_link_from_interface.call_count == 3
1672
        assert self.napp.controller.buffers.app.put.call_count == 1
1673