Passed
Pull Request — master (#258)
by
unknown
04:01
created

build.tests.unit.models.test_evc_deploy   F

Complexity

Total Complexity 72

Size/Duplication

Total Lines 1507
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 1096
dl 0
loc 1507
rs 1.856
c 0
b 0
f 0
wmc 72

44 Methods

Rating   Name   Duplication   Size   Complexity  
A TestEVC.test_prepare_pop_flow() 0 28 1
A TestEVC.test_should_deploy_case1() 0 15 1
A TestEVC.test_send_flow_mods_case1() 0 18 1
A TestEVC.test_send_flow_mods_case2() 0 17 1
A TestEVC.test_prepare_flow_mod() 0 26 1
A TestEVC.test_should_deploy_case4() 0 15 1
A TestEVC.setUp() 0 8 1
A TestEVC.test_primary_links_zipped() 0 2 1
A TestEVC.test_should_deploy_case2() 0 15 1
A TestEVC.test_send_flow_mods_error() 0 16 2
A TestEVC.test_should_deploy_case3() 0 15 1
A TestEVC.create_evc_inter_switch() 0 42 1
A TestEVC.test_remove() 0 11 1
A TestEVC.create_evc_intra_switch() 0 27 1
B TestEVC.test_deploy_successfully() 0 47 1
B TestEVC.test_remove_failover_flows_exclude_uni_switches() 0 67 1
B TestEVC.test_deploy_fail() 0 53 1
B TestEVC.test_remove_current_flows() 0 71 1
A TestEVC.test_deploy_to_backup_path1() 0 30 1
A TestEVC.test_install_nni_flows() 0 41 1
B TestEVC.test_remove_failover_flows_include_all() 0 66 1
B TestEVC.test_deploy_error() 0 72 1
A TestEVC.test_deploy() 0 28 1
B TestEVC.test_deploy_without_path_case1() 0 79 1
B TestEVC.test_setup_failover_path() 0 65 1
B TestEVC.test_install_uni_flows() 0 112 1
D TestEVC.test_prepare_push_flow() 0 57 12
A TestEVC.test_is_using_backup_path() 0 5 1
A TestEVC.test_get_failover_flows() 0 11 1
A TestEVC.test_is_using_primary_path() 0 5 1
B TestEVC.test_check_list_traces() 0 94 2
A TestEVC.test_remove_path_flows() 0 53 1
A TestEVC.test_is_eligible_for_failover_path() 0 7 1
A TestEVC.test_is_failover_path_affected_by_link() 0 11 1
A TestEVC.test_is_primary_path_affected_by_link() 0 4 1
A TestEVC.test_get_value_from_uni_tag() 0 17 1
A TestEVC.test_is_using_dynamic_path() 0 7 1
A TestEVC.test_is_backup_path_affected_by_link() 0 4 1
A TestEVC.test_get_path_status() 0 22 1
F TestEVC.test_deploy_direct_uni_flows() 0 74 16
A TestEVC.test_run_sdntrace() 0 24 1
A TestEVC.test_get_failover_path_vandidates() 0 8 1
A TestEVC.test_run_bulk_sdntraces() 0 29 1
A TestEVC.test_is_affected_by_link() 0 4 1

How to fix   Complexity   

Complexity

Complex classes like build.tests.unit.models.test_evc_deploy 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
"""Method to thest EVCDeploy class."""
2
import sys
3
from unittest import TestCase
4
from unittest.mock import MagicMock, Mock, patch, call
5
6
from kytos.core.common import EntityStatus
7
from kytos.core.exceptions import KytosNoTagAvailableError
8
from kytos.core.interface import Interface
9
from kytos.core.switch import Switch
10
from kytos.lib.helpers import get_controller_mock
11
12
# pylint: disable=wrong-import-position
13
sys.path.insert(0, "/var/lib/kytos/napps/..")
14
# pylint: enable=wrong-import-position
15
16
from napps.kytos.mef_eline.models import EVC, EVCDeploy, Path  # NOQA
17
from napps.kytos.mef_eline.settings import (
18
    MANAGER_URL,
19
    SDN_TRACE_CP_URL,
20
    EVPL_SB_PRIORITY,
21
    EPL_SB_PRIORITY
22
)  # NOQA
23
from napps.kytos.mef_eline.exceptions import FlowModException  # NOQA
24
from napps.kytos.mef_eline.tests.helpers import (
25
    get_link_mocked,
26
    get_uni_mocked,
27
)  # NOQA
28
29
30
# pylint: disable=too-many-public-methods, too-many-lines
31
class TestEVC(TestCase):
32
    """Tests to verify EVC class."""
33
34
    def setUp(self):
35
        attributes = {
36
            "controller": get_controller_mock(),
37
            "name": "circuit_for_tests",
38
            "uni_a": get_uni_mocked(is_valid=True),
39
            "uni_z": get_uni_mocked(is_valid=True),
40
        }
41
        self.evc_deploy = EVCDeploy(**attributes)
42
43
    def test_primary_links_zipped(self):
44
        """Test primary links zipped method."""
45
46
    @staticmethod
47
    @patch("napps.kytos.mef_eline.models.evc.log")
48
    def test_should_deploy_case1(log_mock):
49
        """Test should deploy method without primary links."""
50
        log_mock.debug.return_value = True
51
        attributes = {
52
            "controller": get_controller_mock(),
53
            "name": "custom_name",
54
            "uni_a": get_uni_mocked(is_valid=True),
55
            "uni_z": get_uni_mocked(is_valid=True),
56
        }
57
58
        evc = EVC(**attributes)
59
        evc.should_deploy()
60
        log_mock.debug.assert_called_with("Path is empty.")
61
62
    @patch("napps.kytos.mef_eline.models.evc.log")
63
    def test_should_deploy_case2(self, log_mock):
64
        """Test should deploy method with disable circuit."""
65
        log_mock.debug.return_value = True
66
        attributes = {
67
            "controller": get_controller_mock(),
68
            "name": "custom_name",
69
            "uni_a": get_uni_mocked(is_valid=True),
70
            "uni_z": get_uni_mocked(is_valid=True),
71
            "primary_links": [get_link_mocked(), get_link_mocked()],
72
        }
73
        evc = EVC(**attributes)
74
75
        self.assertFalse(evc.should_deploy(attributes["primary_links"]))
76
        log_mock.debug.assert_called_with(f"{evc} is disabled.")
77
78
    @patch("napps.kytos.mef_eline.models.evc.log")
79
    def test_should_deploy_case3(self, log_mock):
80
        """Test should deploy method with enabled and not active circuit."""
81
        log_mock.debug.return_value = True
82
        attributes = {
83
            "controller": get_controller_mock(),
84
            "name": "custom_name",
85
            "uni_a": get_uni_mocked(is_valid=True),
86
            "uni_z": get_uni_mocked(is_valid=True),
87
            "primary_links": [get_link_mocked(), get_link_mocked()],
88
            "enabled": True,
89
        }
90
        evc = EVC(**attributes)
91
        self.assertTrue(evc.should_deploy(attributes["primary_links"]))
92
        log_mock.debug.assert_called_with(f"{evc} will be deployed.")
93
94
    @patch("napps.kytos.mef_eline.models.evc.log")
95
    def test_should_deploy_case4(self, log_mock):
96
        """Test should deploy method with enabled and active circuit."""
97
        log_mock.debug.return_value = True
98
        attributes = {
99
            "controller": get_controller_mock(),
100
            "name": "custom_name",
101
            "uni_a": get_uni_mocked(is_valid=True),
102
            "uni_z": get_uni_mocked(is_valid=True),
103
            "primary_links": [get_link_mocked(), get_link_mocked()],
104
            "enabled": True,
105
            "active": True,
106
        }
107
        evc = EVC(**attributes)
108
        self.assertFalse(evc.should_deploy(attributes["primary_links"]))
109
110
    @patch("napps.kytos.mef_eline.models.evc.requests")
111
    def test_send_flow_mods_case1(self, requests_mock):
112
        """Test if you are sending flow_mods."""
113
        flow_mods = {"id": 20}
114
        switch = Mock(spec=Switch, id=1)
115
116
        response = MagicMock()
117
        response.status_code = 201
118
        requests_mock.post.return_value = response
119
120
        # pylint: disable=protected-access
121
        EVC._send_flow_mods(switch.id, flow_mods)
122
123
        expected_endpoint = f"{MANAGER_URL}/flows/{switch.id}"
124
        expected_data = {"flows": flow_mods, "force": False}
125
        self.assertEqual(requests_mock.post.call_count, 1)
126
        requests_mock.post.assert_called_once_with(
127
            expected_endpoint, json=expected_data
128
        )
129
130
    @patch("napps.kytos.mef_eline.models.evc.requests")
131
    def test_send_flow_mods_case2(self, requests_mock):
132
        """Test if you are sending flow_mods."""
133
        flow_mods = {"id": 20}
134
        switch = Mock(spec=Switch, id=1)
135
        response = MagicMock()
136
        response.status_code = 201
137
        requests_mock.post.return_value = response
138
139
        # pylint: disable=protected-access
140
        EVC._send_flow_mods(switch.id, flow_mods, command='delete', force=True)
141
142
        expected_endpoint = f"{MANAGER_URL}/delete/{switch.id}"
143
        expected_data = {"flows": flow_mods, "force": True}
144
        self.assertEqual(requests_mock.post.call_count, 1)
145
        requests_mock.post.assert_called_once_with(
146
            expected_endpoint, json=expected_data
147
        )
148
149
    @patch("napps.kytos.mef_eline.models.evc.requests")
150
    def test_send_flow_mods_error(self, requests_mock):
151
        """Test flow_manager call fails."""
152
        flow_mods = {"id": 20}
153
        switch = Mock(spec=Switch, id=1)
154
        response = MagicMock()
155
        response.status_code = 415
156
        requests_mock.post.return_value = response
157
158
        # pylint: disable=protected-access
159
        with self.assertRaises(FlowModException):
160
            EVC._send_flow_mods(
161
                switch.id,
162
                flow_mods,
163
                command='delete',
164
                force=True
165
            )
166
167
    def test_prepare_flow_mod(self):
168
        """Test prepare flow_mod method."""
169
        interface_a = Interface("eth0", 1, Mock(spec=Switch))
170
        interface_z = Interface("eth1", 3, Mock(spec=Switch))
171
        attributes = {
172
            "controller": get_controller_mock(),
173
            "name": "custom_name",
174
            "uni_a": get_uni_mocked(is_valid=True),
175
            "uni_z": get_uni_mocked(is_valid=True),
176
            "primary_links": [get_link_mocked(), get_link_mocked()],
177
            "enabled": True,
178
            "active": True,
179
        }
180
        evc = EVC(**attributes)
181
182
        # pylint: disable=protected-access
183
        flow_mod = evc._prepare_flow_mod(interface_a, interface_z)
184
        expected_flow_mod = {
185
            "match": {"in_port": interface_a.port_number},
186
            "cookie": evc.get_cookie(),
187
            "actions": [
188
                {"action_type": "output", "port": interface_z.port_number}
189
            ],
190
            "priority": EVPL_SB_PRIORITY,
191
        }
192
        self.assertEqual(expected_flow_mod, flow_mod)
193
194
    def test_prepare_pop_flow(self):
195
        """Test prepare pop flow  method."""
196
        attributes = {
197
            "controller": get_controller_mock(),
198
            "name": "custom_name",
199
            "uni_a": get_uni_mocked(interface_port=1, is_valid=True),
200
            "uni_z": get_uni_mocked(interface_port=2, is_valid=True),
201
        }
202
        evc = EVC(**attributes)
203
        interface_a = evc.uni_a.interface
204
        interface_z = evc.uni_z.interface
205
        in_vlan = 10
206
207
        # pylint: disable=protected-access
208
        flow_mod = evc._prepare_pop_flow(
209
            interface_a, interface_z, in_vlan
210
        )
211
212
        expected_flow_mod = {
213
            "match": {"in_port": interface_a.port_number, "dl_vlan": in_vlan},
214
            "cookie": evc.get_cookie(),
215
            "actions": [
216
                {"action_type": "pop_vlan"},
217
                {"action_type": "output", "port": interface_z.port_number},
218
            ],
219
            "priority": EVPL_SB_PRIORITY,
220
        }
221
        self.assertEqual(expected_flow_mod, flow_mod)
222
223
    def test_prepare_push_flow(self):
224
        """Test prepare push flow method."""
225
        attributes = {
226
            "controller": get_controller_mock(),
227
            "name": "custom_name",
228
            "uni_a": get_uni_mocked(interface_port=1, is_valid=True),
229
            "uni_z": get_uni_mocked(interface_port=2, is_valid=True),
230
        }
231
        evc = EVC(**attributes)
232
        interface_a = evc.uni_a.interface
233
        interface_z = evc.uni_z.interface
234
        out_vlan_a = 20
235
236
        for in_vlan_a in [100, "4096/4096", 0, None]:
237
            for in_vlan_z in [50, "4096/4096", 0, None]:
238
                with self.subTest(in_vlan_a=in_vlan_a, in_vlan_z=in_vlan_z):
239
                    # pylint: disable=protected-access
240
                    flow_mod = evc._prepare_push_flow(interface_a, interface_z,
241
                                                      in_vlan_a, out_vlan_a,
242
                                                      in_vlan_z)
243
                    expected_flow_mod = {
244
                        'match': {'in_port': interface_a.port_number},
245
                        'cookie': evc.get_cookie(),
246
                        'actions': [
247
                            {'action_type': 'push_vlan', 'tag_type': 's'},
248
                            {'action_type': 'set_vlan', 'vlan_id': out_vlan_a},
249
                            {
250
                                'action_type': 'output',
251
                                'port': interface_z.port_number
252
                            }
253
                        ],
254
                        "priority": EVPL_SB_PRIORITY,
255
                    }
256
                    if in_vlan_a is not None:
257
                        expected_flow_mod['match']['dl_vlan'] = in_vlan_a
258
                    else:
259
                        expected_flow_mod['priority'] = EPL_SB_PRIORITY
260
261
                    if in_vlan_z not in {"4096/4096", 0, None}:
262
                        new_action = {"action_type": "set_vlan",
263
                                      "vlan_id": in_vlan_z}
264
                        expected_flow_mod["actions"].insert(0, new_action)
265
266
                    if in_vlan_a not in {"4096/4096", 0, None}:
267
                        if in_vlan_z == 0:
268
                            new_action = {"action_type": "pop_vlan"}
269
                            expected_flow_mod["actions"].insert(0, new_action)
270
                    elif in_vlan_a == "4096/4096":
271
                        if in_vlan_z == 0:
272
                            new_action = {"action_type": "pop_vlan"}
273
                            expected_flow_mod["actions"].insert(0, new_action)
274
                    elif not in_vlan_a:
275
                        if in_vlan_z not in {"4096/4096", 0, None}:
276
                            new_action = {"action_type": "push_vlan",
277
                                          "tag_type": "c"}
278
                            expected_flow_mod["actions"].insert(0, new_action)
279
                    self.assertEqual(expected_flow_mod, flow_mod)
280
281
    @staticmethod
282
    @patch("napps.kytos.mef_eline.models.evc.EVC._send_flow_mods")
283
    def test_install_uni_flows(send_flow_mods_mock):
284
        """Test install uni flows method.
285
286
        This test will verify the flows send to the send_flow_mods method.
287
        """
288
        evc = TestEVC.create_evc_inter_switch()
289
290
        # pylint: disable=protected-access
291
        evc._install_uni_flows()
292
        send_flow_mods_mock.assert_not_called()
293
294
        # pylint: disable=protected-access
295
        evc._install_uni_flows(evc.primary_links)
296
297
        expected_flow_mod_a = [
298
            {
299
                "match": {
300
                    "in_port": evc.uni_a.interface.port_number,
301
                    "dl_vlan": evc.uni_a.user_tag.value,
302
                },
303
                "cookie": evc.get_cookie(),
304
                "actions": [
305
                    {
306
                        "action_type": "set_vlan",
307
                        "vlan_id": evc.uni_z.user_tag.value
308
                    },
309
                    {"action_type": "push_vlan", "tag_type": "s"},
310
                    {
311
                        "action_type": "set_vlan",
312
                        "vlan_id": evc.primary_links[0]
313
                        .get_metadata("s_vlan")
314
                        .value,
315
                    },
316
                    {
317
                        "action_type": "output",
318
                        "port": evc.primary_links[0].endpoint_a.port_number,
319
                    },
320
                ],
321
                "priority": EVPL_SB_PRIORITY,
322
            },
323
            {
324
                "match": {
325
                    "in_port": evc.primary_links[0].endpoint_a.port_number,
326
                    "dl_vlan": evc.primary_links[0]
327
                    .get_metadata("s_vlan")
328
                    .value,
329
                },
330
                "cookie": evc.get_cookie(),
331
                "actions": [
332
                    {"action_type": "pop_vlan"},
333
                    {
334
                        "action_type": "output",
335
                        "port": evc.uni_a.interface.port_number,
336
                    },
337
                ],
338
                "priority": EVPL_SB_PRIORITY,
339
            },
340
        ]
341
342
        send_flow_mods_mock.assert_any_call(
343
            evc.uni_a.interface.switch.id, expected_flow_mod_a
344
        )
345
346
        expected_flow_mod_z = [
347
            {
348
                "match": {
349
                    "in_port": evc.uni_z.interface.port_number,
350
                    "dl_vlan": evc.uni_z.user_tag.value,
351
                },
352
                "cookie": evc.get_cookie(),
353
                "actions": [
354
                    {
355
                        "action_type": "set_vlan",
356
                        "vlan_id": evc.uni_a.user_tag.value
357
                    },
358
                    {"action_type": "push_vlan", "tag_type": "s"},
359
                    {
360
                        "action_type": "set_vlan",
361
                        "vlan_id": evc.primary_links[-1]
362
                        .get_metadata("s_vlan")
363
                        .value,
364
                    },
365
                    {
366
                        "action_type": "output",
367
                        "port": evc.primary_links[-1].endpoint_b.port_number,
368
                    },
369
                ],
370
                "priority": EVPL_SB_PRIORITY,
371
            },
372
            {
373
                "match": {
374
                    "in_port": evc.primary_links[-1].endpoint_b.port_number,
375
                    "dl_vlan": evc.primary_links[-1]
376
                    .get_metadata("s_vlan")
377
                    .value,
378
                },
379
                "cookie": evc.get_cookie(),
380
                "actions": [
381
                    {"action_type": "pop_vlan"},
382
                    {
383
                        "action_type": "output",
384
                        "port": evc.uni_z.interface.port_number,
385
                    },
386
                ],
387
                "priority": EVPL_SB_PRIORITY,
388
            },
389
        ]
390
391
        send_flow_mods_mock.assert_any_call(
392
            evc.uni_z.interface.switch.id, expected_flow_mod_z
393
        )
394
395
    @staticmethod
396
    def create_evc_inter_switch():
397
        """Create inter-switch EVC with two links in the path"""
398
        uni_a = get_uni_mocked(
399
            interface_port=2,
400
            tag_value=82,
401
            switch_id=1,
402
            switch_dpid=1,
403
            is_valid=True,
404
        )
405
        uni_z = get_uni_mocked(
406
            interface_port=3,
407
            tag_value=83,
408
            switch_id=3,
409
            switch_dpid=3,
410
            is_valid=True,
411
        )
412
413
        attributes = {
414
            "controller": get_controller_mock(),
415
            "name": "custom_name",
416
            "id": "1",
417
            "uni_a": uni_a,
418
            "uni_z": uni_z,
419
            "primary_links": [
420
                get_link_mocked(
421
                    switch_a=Switch(1),
422
                    switch_b=Switch(2),
423
                    endpoint_a_port=9,
424
                    endpoint_b_port=10,
425
                    metadata={"s_vlan": 5},
426
                ),
427
                get_link_mocked(
428
                    switch_a=Switch(2),
429
                    switch_b=Switch(3),
430
                    endpoint_a_port=11,
431
                    endpoint_b_port=12,
432
                    metadata={"s_vlan": 6},
433
                ),
434
            ],
435
        }
436
        return EVC(**attributes)
437
438
    @staticmethod
439
    @patch("napps.kytos.mef_eline.models.evc.EVC._send_flow_mods")
440
    def test_install_nni_flows(send_flow_mods_mock):
441
        """Test install nni flows method.
442
443
        This test will verify the flows send to the send_flow_mods method.
444
        """
445
        evc = TestEVC.create_evc_inter_switch()
446
447
        # pylint: disable=protected-access
448
        evc._install_nni_flows(evc.primary_links)
449
450
        in_vlan = evc.primary_links[0].get_metadata("s_vlan").value
451
        out_vlan = evc.primary_links[-1].get_metadata("s_vlan").value
452
453
        in_port = evc.primary_links[0].endpoint_b.port_number
454
        out_port = evc.primary_links[-1].endpoint_a.port_number
455
456
        expected_flow_mods = [
457
            {
458
                "match": {"in_port": in_port, "dl_vlan": in_vlan},
459
                "cookie": evc.get_cookie(),
460
                "actions": [
461
                    {"action_type": "set_vlan", "vlan_id": out_vlan},
462
                    {"action_type": "output", "port": out_port},
463
                ],
464
                "priority": EVPL_SB_PRIORITY
465
            },
466
            {
467
                "match": {"in_port": out_port, "dl_vlan": out_vlan},
468
                "cookie": evc.get_cookie(),
469
                "actions": [
470
                    {"action_type": "set_vlan", "vlan_id": in_vlan},
471
                    {"action_type": "output", "port": in_port},
472
                ],
473
                "priority": EVPL_SB_PRIORITY,
474
            },
475
        ]
476
477
        dpid = evc.primary_links[0].endpoint_b.switch.id
478
        send_flow_mods_mock.assert_called_once_with(dpid, expected_flow_mods)
479
480
    @patch("requests.post")
481
    @patch("napps.kytos.mef_eline.controllers.ELineController.upsert_evc")
482
    @patch("napps.kytos.mef_eline.models.evc.log")
483
    @patch("napps.kytos.mef_eline.models.path.Path.choose_vlans")
484
    @patch("napps.kytos.mef_eline.models.evc.EVC._install_nni_flows")
485
    @patch("napps.kytos.mef_eline.models.evc.EVC._install_uni_flows")
486
    @patch("napps.kytos.mef_eline.models.evc.EVC._install_direct_uni_flows")
487
    @patch("napps.kytos.mef_eline.models.evc.EVC.activate")
488
    @patch("napps.kytos.mef_eline.models.evc.EVC.should_deploy")
489
    def test_deploy_successfully(self, *args):
490
        """Test if all methods to deploy are called."""
491
        # pylint: disable=too-many-locals
492
        (
493
            should_deploy_mock,
494
            activate_mock,
495
            install_direct_uni_flows_mock,
496
            install_uni_flows_mock,
497
            install_nni_flows,
498
            chose_vlans_mock,
499
            log_mock,
500
            _,
501
            requests_mock,
502
        ) = args
503
504
        response = MagicMock()
505
        response.status_code = 201
506
        requests_mock.return_value = response
507
508
        should_deploy_mock.return_value = True
509
        evc = self.create_evc_inter_switch()
510
        deployed = evc.deploy_to_path(evc.primary_links)
511
512
        self.assertEqual(should_deploy_mock.call_count, 1)
513
        self.assertEqual(activate_mock.call_count, 1)
514
        self.assertEqual(install_uni_flows_mock.call_count, 1)
515
        self.assertEqual(install_nni_flows.call_count, 1)
516
        self.assertEqual(chose_vlans_mock.call_count, 1)
517
        log_mock.info.assert_called_with(f"{evc} was deployed.")
518
        self.assertTrue(deployed)
519
520
        # intra switch EVC
521
        evc = self.create_evc_intra_switch()
522
        self.assertTrue(evc.deploy_to_path(evc.primary_links))
523
        self.assertEqual(install_direct_uni_flows_mock.call_count, 1)
524
        self.assertEqual(activate_mock.call_count, 2)
525
        self.assertEqual(log_mock.info.call_count, 2)
526
        log_mock.info.assert_called_with(f"{evc} was deployed.")
527
528
    @patch("requests.post")
529
    @patch("napps.kytos.mef_eline.models.evc.log")
530
    @patch("napps.kytos.mef_eline.models.evc.EVC.discover_new_paths")
531
    @patch("napps.kytos.mef_eline.models.path.Path.choose_vlans")
532
    @patch("napps.kytos.mef_eline.models.evc.EVC._install_nni_flows")
533
    @patch("napps.kytos.mef_eline.models.evc.EVC._install_uni_flows")
534
    @patch("napps.kytos.mef_eline.models.evc.EVC.activate")
535
    @patch("napps.kytos.mef_eline.models.evc.EVC.should_deploy")
536
    @patch("napps.kytos.mef_eline.models.EVC.sync")
537
    def test_deploy_fail(self, *args):
538
        """Test if all methods is ignored when the should_deploy is false."""
539
        # pylint: disable=too-many-locals
540
        (
541
            sync_mock,
542
            should_deploy_mock,
543
            activate_mock,
544
            install_uni_flows_mock,
545
            install_nni_flows,
546
            choose_vlans_mock,
547
            discover_new_paths_mock,
548
            log_mock,
549
            requests_mock,
550
        ) = args
551
552
        response = MagicMock()
553
        response.status_code = 201
554
        requests_mock.return_value = response
555
556
        evc = self.create_evc_inter_switch()
557
        should_deploy_mock.return_value = False
558
        discover_new_paths_mock.return_value = []
559
        deployed = evc.deploy_to_path()
560
561
        self.assertEqual(discover_new_paths_mock.call_count, 1)
562
        self.assertEqual(should_deploy_mock.call_count, 1)
563
        self.assertEqual(activate_mock.call_count, 0)
564
        self.assertEqual(install_uni_flows_mock.call_count, 0)
565
        self.assertEqual(install_nni_flows.call_count, 0)
566
        self.assertEqual(choose_vlans_mock.call_count, 0)
567
        self.assertEqual(log_mock.info.call_count, 0)
568
        self.assertEqual(sync_mock.call_count, 1)
569
        self.assertFalse(deployed)
570
571
        # NoTagAvailable on static path
572
        should_deploy_mock.return_value = True
573
        choose_vlans_mock.side_effect = KytosNoTagAvailableError("error")
574
        self.assertFalse(evc.deploy_to_path(evc.primary_links))
575
576
        # NoTagAvailable on dynamic path
577
        should_deploy_mock.return_value = False
578
        discover_new_paths_mock.return_value = [Path(['a', 'b'])]
579
        choose_vlans_mock.side_effect = KytosNoTagAvailableError("error")
580
        self.assertFalse(evc.deploy_to_path(evc.primary_links))
581
582
    @patch("napps.kytos.mef_eline.models.evc.log")
583
    @patch(
584
        "napps.kytos.mef_eline.models.evc.EVC.discover_new_paths",
585
        return_value=[],
586
    )
587
    @patch("napps.kytos.mef_eline.models.path.Path.choose_vlans")
588
    @patch("napps.kytos.mef_eline.models.evc.EVC._install_nni_flows")
589
    @patch("napps.kytos.mef_eline.models.evc.EVC.should_deploy")
590
    @patch("napps.kytos.mef_eline.models.evc.EVC.remove_current_flows")
591
    @patch("napps.kytos.mef_eline.models.evc.EVC.sync")
592
    def test_deploy_error(self, *args):
593
        """Test if all methods is ignored when the should_deploy is false."""
594
        # pylint: disable=too-many-locals
595
        (
596
            sync_mock,
597
            remove_current_flows,
598
            should_deploy_mock,
599
            install_nni_flows,
600
            choose_vlans_mock,
601
            discover_new_paths,
602
            log_mock,
603
        ) = args
604
605
        install_nni_flows.side_effect = FlowModException
606
        should_deploy_mock.return_value = True
607
        uni_a = get_uni_mocked(
608
            interface_port=2,
609
            tag_value=82,
610
            switch_id="switch_uni_a",
611
            is_valid=True,
612
        )
613
        uni_z = get_uni_mocked(
614
            interface_port=3,
615
            tag_value=83,
616
            switch_id="switch_uni_z",
617
            is_valid=True,
618
        )
619
620
        primary_links = [
621
            get_link_mocked(
622
                endpoint_a_port=9, endpoint_b_port=10, metadata={"s_vlan": 5}
623
            ),
624
            get_link_mocked(
625
                endpoint_a_port=11, endpoint_b_port=12, metadata={"s_vlan": 6}
626
            ),
627
        ]
628
629
        attributes = {
630
            "controller": get_controller_mock(),
631
            "name": "custom_name",
632
            "uni_a": uni_a,
633
            "uni_z": uni_z,
634
            "primary_links": primary_links,
635
            "queue_id": 5,
636
        }
637
        # Setup path to deploy
638
        path = Path()
639
        path.append(primary_links[0])
640
        path.append(primary_links[1])
641
642
        evc = EVC(**attributes)
643
644
        deployed = evc.deploy_to_path(path)
645
646
        self.assertEqual(discover_new_paths.call_count, 0)
647
        self.assertEqual(should_deploy_mock.call_count, 1)
648
        self.assertEqual(install_nni_flows.call_count, 1)
649
        self.assertEqual(choose_vlans_mock.call_count, 1)
650
        self.assertEqual(log_mock.error.call_count, 1)
651
        self.assertEqual(sync_mock.call_count, 0)
652
        self.assertEqual(remove_current_flows.call_count, 2)
653
        self.assertFalse(deployed)
654
655
    @patch("napps.kytos.mef_eline.models.evc.notify_link_available_tags")
656
    @patch("napps.kytos.mef_eline.models.evc.EVC.get_failover_path_candidates")
657
    @patch("napps.kytos.mef_eline.models.evc.EVC._install_nni_flows")
658
    @patch("napps.kytos.mef_eline.models.evc.EVC._install_uni_flows")
659
    @patch("napps.kytos.mef_eline.models.evc.EVC.remove_path_flows")
660
    @patch("napps.kytos.mef_eline.models.EVC.sync")
661
    def test_setup_failover_path(self, *args):
662
        """Test setup_failover_path method."""
663
        (
664
            sync_mock,
665
            remove_path_flows_mock,
666
            install_uni_flows_mock,
667
            install_nni_flows_mock,
668
            get_failover_path_candidates_mock,
669
            notify_mock,
670
        ) = args
671
672
        # case1: early return intra switch
673
        evc1 = self.create_evc_intra_switch()
674
675
        self.assertFalse(evc1.setup_failover_path())
676
        self.assertEqual(sync_mock.call_count, 0)
677
678
        # case2: early return not eligible for path failover
679
        evc2 = self.create_evc_inter_switch()
680
        evc2.is_eligible_for_failover_path = MagicMock(return_value=False)
681
682
        self.assertFalse(evc2.setup_failover_path())
683
        self.assertEqual(sync_mock.call_count, 0)
684
685
        # case3: success failover_path setup
686
        evc2.is_eligible_for_failover_path = MagicMock(return_value=True)
687
        evc2.failover_path = ["link1", "link2"]
688
        path_mock = MagicMock()
689
        path_mock.__iter__.return_value = ["link3"]
690
        get_failover_path_candidates_mock.return_value = [None, path_mock]
691
692
        self.assertTrue(evc2.setup_failover_path())
693
        remove_path_flows_mock.assert_called_with(["link1", "link2"])
694
        path_mock.choose_vlans.assert_called()
695
        notify_mock.assert_called()
696
        install_nni_flows_mock.assert_called_with(path_mock)
697
        install_uni_flows_mock.assert_called_with(path_mock, skip_in=True)
698
        self.assertEqual(evc2.failover_path, path_mock)
699
        self.assertEqual(sync_mock.call_count, 1)
700
701
        # case 4: failed to setup failover_path - No Tag available
702
        evc2.failover_path = []
703
        path_mock.choose_vlans.side_effect = KytosNoTagAvailableError("error")
704
        sync_mock.call_count = 0
705
706
        self.assertFalse(evc2.setup_failover_path())
707
        self.assertEqual(list(evc2.failover_path), [])
708
        self.assertEqual(sync_mock.call_count, 1)
709
710
        # case 5: failed to setup failover_path - FlowMod exception
711
        evc2.failover_path = []
712
        path_mock.choose_vlans.side_effect = None
713
        install_nni_flows_mock.side_effect = FlowModException("error")
714
        sync_mock.call_count = 0
715
716
        self.assertFalse(evc2.setup_failover_path())
717
        self.assertEqual(list(evc2.failover_path), [])
718
        self.assertEqual(sync_mock.call_count, 1)
719
        remove_path_flows_mock.assert_called_with(path_mock)
720
721
    @patch("napps.kytos.mef_eline.models.evc.EVC.deploy_to_path")
722
    @patch("napps.kytos.mef_eline.models.evc.EVC.discover_new_paths")
723
    def test_deploy_to_backup_path1(
724
        self, discover_new_paths_mocked, deploy_to_path_mocked
725
    ):
726
        """Test deployment when dynamic_backup_path is False in same switch"""
727
        uni_a = get_uni_mocked(interface_port=2, tag_value=82, is_valid=True)
728
        uni_z = get_uni_mocked(interface_port=3, tag_value=83, is_valid=True)
729
730
        switch = Mock(spec=Switch)
731
        uni_a.interface.switch = switch
732
        uni_z.interface.switch = switch
733
734
        attributes = {
735
            "controller": get_controller_mock(),
736
            "name": "custom_name",
737
            "uni_a": uni_a,
738
            "uni_z": uni_z,
739
            "enabled": True,
740
            "dynamic_backup_path": False,
741
        }
742
743
        evc = EVC(**attributes)
744
        discover_new_paths_mocked.return_value = []
745
        deploy_to_path_mocked.return_value = True
746
747
        deployed = evc.deploy_to_backup_path()
748
749
        deploy_to_path_mocked.assert_called_once_with()
750
        self.assertEqual(deployed, True)
751
752
    @patch("requests.post")
753
    @patch("napps.kytos.mef_eline.controllers.ELineController.upsert_evc")
754
    @patch("napps.kytos.mef_eline.models.evc.log")
755
    @patch("napps.kytos.mef_eline.models.path.Path.choose_vlans")
756
    @patch("napps.kytos.mef_eline.models.evc.EVC._install_nni_flows")
757
    @patch("napps.kytos.mef_eline.models.evc.EVC._install_uni_flows")
758
    @patch("napps.kytos.mef_eline.models.evc.EVC.activate")
759
    @patch("napps.kytos.mef_eline.models.evc.EVC.should_deploy")
760
    @patch("napps.kytos.mef_eline.models.evc.EVC.discover_new_paths")
761
    def test_deploy_without_path_case1(self, *args):
762
        """Test if not path is found a dynamic path is used."""
763
        # pylint: disable=too-many-locals
764
        (
765
            discover_new_paths_mocked,
766
            should_deploy_mock,
767
            activate_mock,
768
            install_uni_flows_mock,
769
            install_nni_flows,
770
            chose_vlans_mock,
771
            log_mock,
772
            _,
773
            requests_mock,
774
        ) = args
775
776
        response = MagicMock()
777
        response.status_code = 201
778
        requests_mock.return_value = response
779
780
        should_deploy_mock.return_value = False
781
        uni_a = get_uni_mocked(
782
            interface_port=2,
783
            tag_value=82,
784
            switch_id="switch_uni_a",
785
            is_valid=True,
786
        )
787
        uni_z = get_uni_mocked(
788
            interface_port=3,
789
            tag_value=83,
790
            switch_id="switch_uni_z",
791
            is_valid=True,
792
        )
793
794
        attributes = {
795
            "controller": get_controller_mock(),
796
            "name": "custom_name",
797
            "uni_a": uni_a,
798
            "uni_z": uni_z,
799
            "enabled": True,
800
            "dynamic_backup_path": False,
801
        }
802
803
        dynamic_backup_path = Path(
804
            [
805
                get_link_mocked(
806
                    endpoint_a_port=9,
807
                    endpoint_b_port=10,
808
                    metadata={"s_vlan": 5},
809
                ),
810
                get_link_mocked(
811
                    endpoint_a_port=11,
812
                    endpoint_b_port=12,
813
                    metadata={"s_vlan": 6},
814
                ),
815
            ]
816
        )
817
818
        evc = EVC(**attributes)
819
        discover_new_paths_mocked.return_value = [dynamic_backup_path]
820
821
        deployed = evc.deploy_to_path()
822
823
        self.assertEqual(should_deploy_mock.call_count, 1)
824
        self.assertEqual(discover_new_paths_mocked.call_count, 1)
825
        self.assertEqual(activate_mock.call_count, 1)
826
        self.assertEqual(install_uni_flows_mock.call_count, 1)
827
        self.assertEqual(install_nni_flows.call_count, 1)
828
        self.assertEqual(chose_vlans_mock.call_count, 1)
829
        log_mock.info.assert_called_with(f"{evc} was deployed.")
830
        self.assertTrue(deployed)
831
832
    @patch("napps.kytos.mef_eline.models.evc.EVCDeploy.deploy_to_primary_path")
833
    @patch("napps.kytos.mef_eline.models.evc.EVCDeploy.deploy_to_backup_path")
834
    @patch("napps.kytos.mef_eline.models.evc.emit_event")
835
    def test_deploy(self, *args):
836
        """Test method deploy"""
837
        (emit_event_mock, deploy_primary_mock, deploy_backup_mock) = args
838
839
        # case 1: deploy to primary
840
        self.evc_deploy.archived = False
841
        deploy_primary_mock.return_value = True
842
        self.assertTrue(self.evc_deploy.deploy())
843
        self.assertEqual(emit_event_mock.call_count, 1)
844
845
        # case 2: deploy to backup
846
        deploy_primary_mock.return_value = False
847
        deploy_backup_mock.return_value = True
848
        self.assertTrue(self.evc_deploy.deploy())
849
        self.assertEqual(emit_event_mock.call_count, 2)
850
851
        # case 3: fail to deploy to primary and backup
852
        deploy_backup_mock.return_value = False
853
        self.assertFalse(self.evc_deploy.deploy())
854
        self.assertEqual(emit_event_mock.call_count, 2)
855
856
        # case 4: archived
857
        self.evc_deploy.archived = True
858
        self.assertFalse(self.evc_deploy.deploy())
859
        self.assertEqual(emit_event_mock.call_count, 2)
860
861
    @patch("napps.kytos.mef_eline.models.evc.EVCDeploy.remove_current_flows")
862
    @patch("napps.kytos.mef_eline.models.evc.EVCDeploy.sync")
863
    @patch("napps.kytos.mef_eline.models.evc.emit_event")
864
    def test_remove(self, *args):
865
        """Test method remove"""
866
        (emit_event_mock, sync_mock, remove_flows_mock) = args
867
        self.evc_deploy.remove()
868
        remove_flows_mock.assert_called()
869
        sync_mock.assert_called()
870
        emit_event_mock.assert_called()
871
        self.assertFalse(self.evc_deploy.is_enabled())
872
873
    @patch("napps.kytos.mef_eline.controllers.ELineController.upsert_evc")
874
    @patch("napps.kytos.mef_eline.models.evc.notify_link_available_tags")
875
    @patch("napps.kytos.mef_eline.models.evc.EVC._send_flow_mods")
876
    @patch("napps.kytos.mef_eline.models.evc.log.error")
877
    def test_remove_current_flows(self, *args):
878
        """Test remove current flows."""
879
        # pylint: disable=too-many-locals
880
        (log_error_mock, send_flow_mods_mocked, notify_mock, _) = args
881
        uni_a = get_uni_mocked(
882
            interface_port=2,
883
            tag_value=82,
884
            switch_id="switch_uni_a",
885
            is_valid=True,
886
        )
887
        uni_z = get_uni_mocked(
888
            interface_port=3,
889
            tag_value=83,
890
            switch_id="switch_uni_z",
891
            is_valid=True,
892
        )
893
894
        switch_a = Switch("00:00:00:00:00:01")
895
        switch_b = Switch("00:00:00:00:00:02")
896
        switch_c = Switch("00:00:00:00:00:03")
897
898
        attributes = {
899
            "controller": get_controller_mock(),
900
            "name": "custom_name",
901
            "uni_a": uni_a,
902
            "uni_z": uni_z,
903
            "active": True,
904
            "enabled": True,
905
            "primary_links": [
906
                get_link_mocked(
907
                    switch_a=switch_a,
908
                    switch_b=switch_b,
909
                    endpoint_a_port=9,
910
                    endpoint_b_port=10,
911
                    metadata={"s_vlan": 5},
912
                ),
913
                get_link_mocked(
914
                    switch_a=switch_b,
915
                    switch_b=switch_c,
916
                    endpoint_a_port=11,
917
                    endpoint_b_port=12,
918
                    metadata={"s_vlan": 6},
919
                ),
920
            ],
921
        }
922
923
        evc = EVC(**attributes)
924
925
        evc.current_path = evc.primary_links
926
        evc.remove_current_flows()
927
        notify_mock.assert_called()
928
929
        self.assertEqual(send_flow_mods_mocked.call_count, 5)
930
        self.assertFalse(evc.is_active())
931
        flows = [
932
            {"cookie": evc.get_cookie(), "cookie_mask": 18446744073709551615}
933
        ]
934
        switch_1 = evc.primary_links[0].endpoint_a.switch
935
        switch_2 = evc.primary_links[0].endpoint_b.switch
936
        send_flow_mods_mocked.assert_any_call(switch_1.id, flows, 'delete',
937
                                              force=True)
938
        send_flow_mods_mocked.assert_any_call(switch_2.id, flows, 'delete',
939
                                              force=True)
940
941
        send_flow_mods_mocked.side_effect = FlowModException("error")
942
        evc.remove_current_flows()
943
        log_error_mock.assert_called()
944
945
    @patch("napps.kytos.mef_eline.controllers.ELineController.upsert_evc")
946
    @patch("napps.kytos.mef_eline.models.evc.notify_link_available_tags")
947
    @patch("napps.kytos.mef_eline.models.evc.EVC._send_flow_mods")
948
    @patch("napps.kytos.mef_eline.models.evc.log.error")
949
    def test_remove_failover_flows_exclude_uni_switches(self, *args):
950
        """Test remove failover flows excluding UNI switches."""
951
        # pylint: disable=too-many-locals
952
        (log_error_mock, send_flow_mods_mocked,
953
         notify_mock, mock_upsert) = args
954
        uni_a = get_uni_mocked(
955
            interface_port=2,
956
            tag_value=82,
957
            switch_id="00:00:00:00:00:00:00:01",
958
            is_valid=True,
959
        )
960
        uni_z = get_uni_mocked(
961
            interface_port=3,
962
            tag_value=83,
963
            switch_id="00:00:00:00:00:00:00:03",
964
            is_valid=True,
965
        )
966
967
        switch_a = Switch("00:00:00:00:00:00:00:01")
968
        switch_b = Switch("00:00:00:00:00:00:00:02")
969
        switch_c = Switch("00:00:00:00:00:00:00:03")
970
971
        attributes = {
972
            "controller": get_controller_mock(),
973
            "name": "custom_name",
974
            "uni_a": uni_a,
975
            "uni_z": uni_z,
976
            "active": True,
977
            "enabled": True,
978
            "failover_path": [
979
                get_link_mocked(
980
                    switch_a=switch_a,
981
                    switch_b=switch_b,
982
                    endpoint_a_port=9,
983
                    endpoint_b_port=10,
984
                    metadata={"s_vlan": 5},
985
                ),
986
                get_link_mocked(
987
                    switch_a=switch_b,
988
                    switch_b=switch_c,
989
                    endpoint_a_port=11,
990
                    endpoint_b_port=12,
991
                    metadata={"s_vlan": 6},
992
                ),
993
            ],
994
        }
995
996
        evc = EVC(**attributes)
997
        evc.remove_failover_flows(exclude_uni_switches=True, sync=True)
998
        notify_mock.assert_called()
999
1000
        assert send_flow_mods_mocked.call_count == 1
1001
        flows = [
1002
            {"cookie": evc.get_cookie(),
1003
             "cookie_mask": int(0xffffffffffffffff)}
1004
        ]
1005
        send_flow_mods_mocked.assert_any_call(switch_b.id, flows, 'delete',
1006
                                              force=True)
1007
        assert mock_upsert.call_count == 1
1008
1009
        send_flow_mods_mocked.side_effect = FlowModException("error")
1010
        evc.remove_current_flows()
1011
        log_error_mock.assert_called()
1012
1013
    @patch("napps.kytos.mef_eline.controllers.ELineController.upsert_evc")
1014
    @patch("napps.kytos.mef_eline.models.evc.notify_link_available_tags")
1015
    @patch("napps.kytos.mef_eline.models.evc.EVC._send_flow_mods")
1016
    def test_remove_failover_flows_include_all(self, *args):
1017
        """Test remove failover flows including UNI switches."""
1018
        # pylint: disable=too-many-locals
1019
        (send_flow_mods_mocked,
1020
         notify_mock, mock_upsert) = args
1021
        uni_a = get_uni_mocked(
1022
            interface_port=2,
1023
            tag_value=82,
1024
            switch_id="00:00:00:00:00:00:00:01",
1025
            is_valid=True,
1026
        )
1027
        uni_z = get_uni_mocked(
1028
            interface_port=3,
1029
            tag_value=83,
1030
            switch_id="00:00:00:00:00:00:00:03",
1031
            is_valid=True,
1032
        )
1033
1034
        switch_a = Switch("00:00:00:00:00:00:00:01")
1035
        switch_b = Switch("00:00:00:00:00:00:00:02")
1036
        switch_c = Switch("00:00:00:00:00:00:00:03")
1037
1038
        attributes = {
1039
            "controller": get_controller_mock(),
1040
            "name": "custom_name",
1041
            "uni_a": uni_a,
1042
            "uni_z": uni_z,
1043
            "active": True,
1044
            "enabled": True,
1045
            "failover_path": [
1046
                get_link_mocked(
1047
                    switch_a=switch_a,
1048
                    switch_b=switch_b,
1049
                    endpoint_a_port=9,
1050
                    endpoint_b_port=10,
1051
                    metadata={"s_vlan": 5},
1052
                ),
1053
                get_link_mocked(
1054
                    switch_a=switch_b,
1055
                    switch_b=switch_c,
1056
                    endpoint_a_port=11,
1057
                    endpoint_b_port=12,
1058
                    metadata={"s_vlan": 6},
1059
                ),
1060
            ],
1061
        }
1062
1063
        evc = EVC(**attributes)
1064
        evc.remove_failover_flows(exclude_uni_switches=False, sync=True)
1065
        notify_mock.assert_called()
1066
1067
        assert send_flow_mods_mocked.call_count == 3
1068
        flows = [
1069
            {"cookie": evc.get_cookie(),
1070
             "cookie_mask": int(0xffffffffffffffff)}
1071
        ]
1072
        send_flow_mods_mocked.assert_any_call(switch_a.id, flows, 'delete',
1073
                                              force=True)
1074
        send_flow_mods_mocked.assert_any_call(switch_b.id, flows, 'delete',
1075
                                              force=True)
1076
        send_flow_mods_mocked.assert_any_call(switch_c.id, flows, 'delete',
1077
                                              force=True)
1078
        assert mock_upsert.call_count == 1
1079
1080
    @staticmethod
1081
    def create_evc_intra_switch():
1082
        """Create intra-switch EVC."""
1083
        switch = Mock(spec=Switch)
1084
        switch.dpid = 2
1085
        switch.id = switch.dpid
1086
        interface_a = Interface("eth0", 1, switch)
1087
        interface_z = Interface("eth1", 3, switch)
1088
        uni_a = get_uni_mocked(
1089
            tag_value=82,
1090
            is_valid=True,
1091
        )
1092
        uni_z = get_uni_mocked(
1093
            tag_value=84,
1094
            is_valid=True,
1095
        )
1096
        uni_a.interface = interface_a
1097
        uni_z.interface = interface_z
1098
        attributes = {
1099
            "controller": get_controller_mock(),
1100
            "name": "custom_name",
1101
            "id": "1",
1102
            "uni_a": uni_a,
1103
            "uni_z": uni_z,
1104
            "enabled": True,
1105
        }
1106
        return EVC(**attributes)
1107
1108
    # pylint: disable=too-many-branches
1109
    @staticmethod
1110
    @patch("napps.kytos.mef_eline.models.evc.EVC._send_flow_mods")
1111
    def test_deploy_direct_uni_flows(send_flow_mods_mock):
1112
        """Test _install_direct_uni_flows"""
1113
        evc = TestEVC.create_evc_intra_switch()
1114
        expected_dpid = evc.uni_a.interface.switch.id
1115
1116
        for uni_a in [100, "4096/4096", 0, None]:
1117
            for uni_z in [100, "4096/4096", 0, None]:
1118
                expected_flows = [
1119
                    {
1120
                        "match": {"in_port": 1},
1121
                        "cookie": evc.get_cookie(),
1122
                        "actions": [
1123
                            {"action_type": "output", "port": 3},
1124
                        ],
1125
                        "priority": EPL_SB_PRIORITY
1126
                    },
1127
                    {
1128
                        "match": {"in_port": 3},
1129
                        "cookie": evc.get_cookie(),
1130
                        "actions": [
1131
                            {"action_type": "output", "port": 1},
1132
                        ],
1133
                        "priority": EPL_SB_PRIORITY
1134
                    }
1135
                ]
1136
                evc.uni_a = get_uni_mocked(tag_value=uni_a, is_valid=True)
1137
                evc.uni_a.interface.port_number = 1
1138
                evc.uni_z = get_uni_mocked(tag_value=uni_z, is_valid=True)
1139
                evc.uni_z.interface.port_number = 3
1140
                expected_dpid = evc.uni_a.interface.switch.id
1141
                evc._install_direct_uni_flows()
1142
                if uni_a is not None:
1143
                    expected_flows[0]["match"]["dl_vlan"] = uni_a
1144
                    expected_flows[0]["priority"] = EVPL_SB_PRIORITY
1145
                if uni_z is not None:
1146
                    expected_flows[1]["match"]["dl_vlan"] = uni_z
1147
                    expected_flows[1]["priority"] = EVPL_SB_PRIORITY
1148
1149
                if uni_z not in {None, "4096/4096", 0}:
1150
                    expected_flows[0]["actions"].insert(
1151
                        0, {"action_type": "set_vlan", "vlan_id": uni_z}
1152
                    )
1153
                if uni_a not in {None, "4096/4096", 0}:
1154
                    expected_flows[1]["actions"].insert(
1155
                            0, {"action_type": "set_vlan", "vlan_id": uni_a}
1156
                        )
1157
                    if not uni_z:
1158
                        expected_flows[1]["actions"].insert(
1159
                            0, {"action_type": "push_vlan", "tag_type": "c"}
1160
                        )
1161
                    if uni_z == 0:
1162
                        new_action = {"action_type": "pop_vlan"}
1163
                        expected_flows[0]["actions"].insert(0, new_action)
1164
                elif uni_a == "4096/4096":
1165
                    if uni_z == 0:
1166
                        new_action = {"action_type": "pop_vlan"}
1167
                        expected_flows[0]["actions"].insert(0, new_action)
1168
                elif uni_a == 0:
1169
                    if uni_z not in {None, "4096/4096", 0}:
1170
                        expected_flows[0]["actions"].insert(
1171
                            0, {"action_type": "push_vlan", "tag_type": "c"}
1172
                        )
1173
                    if uni_z:
1174
                        new_action = {"action_type": "pop_vlan"}
1175
                        expected_flows[1]["actions"].insert(0, new_action)
1176
                elif uni_a is None:
1177
                    if uni_z not in {None, "4096/4096", 0}:
1178
                        expected_flows[0]["actions"].insert(
1179
                            0, {"action_type": "push_vlan", "tag_type": "c"}
1180
                        )
1181
                send_flow_mods_mock.assert_called_with(
1182
                    expected_dpid, expected_flows
1183
                )
1184
1185
    def test_is_affected_by_link(self):
1186
        """Test is_affected_by_link method"""
1187
        self.evc_deploy.current_path = Path(['a', 'b', 'c'])
1188
        self.assertTrue(self.evc_deploy.is_affected_by_link('b'))
1189
1190
    def test_is_backup_path_affected_by_link(self):
1191
        """Test is_backup_path_affected_by_link method"""
1192
        self.evc_deploy.backup_path = Path(['a', 'b', 'c'])
1193
        self.assertFalse(self.evc_deploy.is_backup_path_affected_by_link('d'))
1194
1195
    def test_is_primary_path_affected_by_link(self):
1196
        """Test is_primary_path_affected_by_link method"""
1197
        self.evc_deploy.primary_path = Path(['a', 'b', 'c'])
1198
        self.assertTrue(self.evc_deploy.is_primary_path_affected_by_link('c'))
1199
1200
    def test_is_using_primary_path(self):
1201
        """Test is_using_primary_path method"""
1202
        self.evc_deploy.primary_path = Path(['a', 'b', 'c'])
1203
        self.evc_deploy.current_path = Path(['e', 'f', 'g'])
1204
        self.assertFalse(self.evc_deploy.is_using_primary_path())
1205
1206
    def test_is_using_backup_path(self):
1207
        """Test is_using_backup_path method"""
1208
        self.evc_deploy.backup_path = Path(['a', 'b', 'c'])
1209
        self.evc_deploy.current_path = Path(['e', 'f', 'g'])
1210
        self.assertFalse(self.evc_deploy.is_using_backup_path())
1211
1212
    @patch('napps.kytos.mef_eline.models.path.Path.status')
1213
    def test_is_using_dynamic_path(self, mock_status):
1214
        """Test is_using_dynamic_path method"""
1215
        mock_status.return_value = False
1216
        self.evc_deploy.backup_path = Path([])
1217
        self.evc_deploy.primary_path = Path([])
1218
        self.assertFalse(self.evc_deploy.is_using_dynamic_path())
1219
1220
    def test_get_path_status(self):
1221
        """Test get_path_status method"""
1222
        path = Path([])
1223
        self.assertEqual(
1224
            self.evc_deploy.get_path_status(path),
1225
            EntityStatus.DISABLED
1226
        )
1227
        path = Path([
1228
            get_link_mocked(status=EntityStatus.UP),
1229
            get_link_mocked(status=EntityStatus.DOWN)
1230
        ])
1231
        self.assertEqual(
1232
            self.evc_deploy.get_path_status(path),
1233
            EntityStatus.DOWN
1234
        )
1235
        path = Path([
1236
            get_link_mocked(status=EntityStatus.UP),
1237
            get_link_mocked(status=EntityStatus.UP)
1238
        ])
1239
        self.assertEqual(
1240
            self.evc_deploy.get_path_status(path),
1241
            EntityStatus.UP
1242
        )
1243
1244
    @patch("napps.kytos.mef_eline.models.evc.EVC._prepare_uni_flows")
1245
    def test_get_failover_flows(self, prepare_uni_flows_mock):
1246
        """Test get_failover_flows method."""
1247
        evc = self.create_evc_inter_switch()
1248
        evc.failover_path = Path([])
1249
        self.assertEqual(evc.get_failover_flows(), {})
1250
1251
        path = MagicMock()
1252
        evc.failover_path = path
1253
        evc.get_failover_flows()
1254
        prepare_uni_flows_mock.assert_called_with(path, skip_out=True)
1255
1256
    @patch("napps.kytos.mef_eline.models.evc.log")
1257
    @patch("napps.kytos.mef_eline.models.evc.EVC._send_flow_mods")
1258
    @patch("napps.kytos.mef_eline.models.path.Path.make_vlans_available")
1259
    def test_remove_path_flows(self, *args):
1260
        """Test remove path flows."""
1261
        (
1262
            make_vlans_available_mock,
1263
            send_flow_mods_mock,
1264
            log_mock,
1265
        ) = args
1266
1267
        evc = self.create_evc_inter_switch()
1268
1269
        evc.remove_path_flows()
1270
        make_vlans_available_mock.assert_not_called()
1271
1272
        expected_flows_1 = [
1273
            {
1274
                'cookie': 12249790986447749121,
1275
                'cookie_mask': 18446744073709551615,
1276
                'match': {'in_port': 9, 'dl_vlan':  5}
1277
            },
1278
        ]
1279
        expected_flows_2 = [
1280
            {
1281
                'cookie': 12249790986447749121,
1282
                'cookie_mask': 18446744073709551615,
1283
                'match': {'in_port': 10, 'dl_vlan': 5}
1284
            },
1285
            {
1286
                'cookie': 12249790986447749121,
1287
                'cookie_mask': 18446744073709551615,
1288
                'match': {'in_port': 11, 'dl_vlan': 6}
1289
            },
1290
        ]
1291
        expected_flows_3 = [
1292
            {
1293
                'cookie': 12249790986447749121,
1294
                'cookie_mask': 18446744073709551615,
1295
                'match': {'in_port': 12, 'dl_vlan': 6}
1296
            },
1297
        ]
1298
1299
        evc.remove_path_flows(evc.primary_links)
1300
        send_flow_mods_mock.assert_has_calls([
1301
            call(1, expected_flows_1, 'delete', force=True),
1302
            call(2, expected_flows_2, 'delete', force=True),
1303
            call(3, expected_flows_3, 'delete', force=True),
1304
        ], any_order=True)
1305
1306
        send_flow_mods_mock.side_effect = FlowModException("err")
1307
        evc.remove_path_flows(evc.primary_links)
1308
        log_mock.error.assert_called()
1309
1310
    @patch("requests.put")
1311
    def test_run_sdntrace(self, put_mock):
1312
        """Test run_sdntrace method."""
1313
        evc = self.create_evc_inter_switch()
1314
        response = MagicMock()
1315
        response.status_code = 200
1316
        response.json.return_value = {"result": "ok"}
1317
        put_mock.return_value = response
1318
1319
        expected_endpoint = f"{SDN_TRACE_CP_URL}/trace"
1320
        expected_payload = {
1321
            'trace': {
1322
                'switch': {'dpid': 1, 'in_port': 2},
1323
                'eth': {'dl_type': 0x8100, 'dl_vlan': 82}
1324
            }
1325
        }
1326
1327
        result = evc.run_sdntrace(evc.uni_a)
1328
        put_mock.assert_called_with(expected_endpoint, json=expected_payload)
1329
        self.assertEqual(result, "ok")
1330
1331
        response.status_code = 400
1332
        result = evc.run_sdntrace(evc.uni_a)
1333
        self.assertEqual(result, [])
1334
1335
    @patch("requests.put")
1336
    def test_run_bulk_sdntraces(self, put_mock):
1337
        """Test run_bulk_sdntraces method for bulh request."""
1338
        evc = self.create_evc_inter_switch()
1339
        response = MagicMock()
1340
        response.status_code = 200
1341
        response.json.return_value = {"result": "ok"}
1342
        put_mock.return_value = response
1343
1344
        expected_endpoint = f"{SDN_TRACE_CP_URL}/traces"
1345
        expected_payload = [
1346
                            {
1347
                                'trace': {
1348
                                    'switch': {'dpid': 1, 'in_port': 2},
1349
                                    'eth': {'dl_type': 0x8100, 'dl_vlan': 82}
1350
                                }
1351
                            }
1352
                        ]
1353
        result = EVCDeploy.run_bulk_sdntraces([evc.uni_a])
1354
        put_mock.assert_called_with(
1355
                                    expected_endpoint,
1356
                                    json=expected_payload,
1357
                                    timeout=30
1358
                                )
1359
        self.assertEqual(result['result'], "ok")
1360
1361
        response.status_code = 400
1362
        result = EVCDeploy.run_bulk_sdntraces([evc.uni_a])
1363
        self.assertEqual(result, {"result": []})
1364
1365
    @patch("napps.kytos.mef_eline.models.evc.log")
1366
    @patch("napps.kytos.mef_eline.models.evc.EVCDeploy.run_bulk_sdntraces")
1367
    def test_check_list_traces(self, run_bulk_sdntraces_mock, _):
1368
        """Test check_list_traces method."""
1369
        evc = self.create_evc_inter_switch()
1370
1371
        for link in evc.primary_links:
1372
            link.metadata['s_vlan'] = MagicMock(value=link.metadata['s_vlan'])
1373
        evc.current_path = evc.primary_links
1374
1375
        trace_a = [
1376
            {"dpid": 1, "port": 2, "time": "t1", "type": "start", "vlan": 82},
1377
            {"dpid": 2, "port": 10, "time": "t2", "type": "trace", "vlan": 5},
1378
            {"dpid": 3, "port": 12, "time": "t3", "type": "trace", "vlan": 6},
1379
        ]
1380
        trace_z = [
1381
            {"dpid": 3, "port": 3, "time": "t1", "type": "start", "vlan": 83},
1382
            {"dpid": 2, "port": 11, "time": "t2", "type": "trace", "vlan": 6},
1383
            {"dpid": 1, "port": 9, "time": "t3", "type": "trace", "vlan": 5},
1384
        ]
1385
1386
        run_bulk_sdntraces_mock.return_value = {
1387
                                                "result": [trace_a, trace_z]
1388
                                            }
1389
        result = EVCDeploy.check_list_traces([evc])
1390
        self.assertTrue(result[evc.id])
1391
1392
        # case2: fail incomplete trace from uni_a
1393
        run_bulk_sdntraces_mock.return_value = {
1394
                                                "result": [
1395
                                                            trace_a[:2],
1396
                                                            trace_z
1397
                                                        ]
1398
        }
1399
        result = EVCDeploy.check_list_traces([evc])
1400
        self.assertFalse(result[evc.id])
1401
1402
        # case3: fail incomplete trace from uni_z
1403
        run_bulk_sdntraces_mock.return_value = {
1404
                                                "result": [
1405
                                                            trace_a,
1406
                                                            trace_z[:2]
1407
                                                        ]
1408
        }
1409
        result = EVCDeploy.check_list_traces([evc])
1410
        self.assertFalse(result[evc.id])
1411
1412
        # case4: fail wrong vlan id in trace from uni_a
1413
        trace_a[1]["vlan"] = 5
1414
        trace_z[1]["vlan"] = 99
1415
        run_bulk_sdntraces_mock.return_value = {
1416
                                                "result": [trace_a, trace_z]
1417
        }
1418
        result = EVCDeploy.check_list_traces([evc])
1419
        self.assertFalse(result[evc.id])
1420
1421
        # case5: fail wrong vlan id in trace from uni_z
1422
        trace_a[1]["vlan"] = 99
1423
        run_bulk_sdntraces_mock.return_value = {
1424
                                                "result": [trace_a, trace_z]
1425
        }
1426
        result = EVCDeploy.check_list_traces([evc])
1427
        self.assertFalse(result[evc.id])
1428
1429
        # case6: success when no output in traces
1430
        trace_a[1]["vlan"] = 5
1431
        trace_z[1]["vlan"] = 6
1432
        result = EVCDeploy.check_list_traces([evc])
1433
        self.assertTrue(result[evc.id])
1434
1435
        # case7: fail when output is None in trace_a or trace_b
1436
        trace_a[-1]["out"] = None
1437
        result = EVCDeploy.check_list_traces([evc])
1438
        self.assertFalse(result[evc.id])
1439
        trace_a[-1].pop("out", None)
1440
        trace_z[-1]["out"] = None
1441
        result = EVCDeploy.check_list_traces([evc])
1442
        self.assertFalse(result[evc.id])
1443
1444
        # case8: success when the output is correct on both uni
1445
        trace_a[-1]["out"] = {"port": 3, "vlan": 83}
1446
        trace_z[-1]["out"] = {"port": 2, "vlan": 82}
1447
        result = EVCDeploy.check_list_traces([evc])
1448
        self.assertTrue(result[evc.id])
1449
1450
        # case9: fail if any output is incorrect
1451
        trace_a[-1]["out"] = {"port": 3, "vlan": 99}
1452
        trace_z[-1]["out"] = {"port": 2, "vlan": 82}
1453
        result = EVCDeploy.check_list_traces([evc])
1454
        self.assertFalse(result[evc.id])
1455
        trace_a[-1]["out"] = {"port": 3, "vlan": 83}
1456
        trace_z[-1]["out"] = {"port": 2, "vlan": 99}
1457
        result = EVCDeploy.check_list_traces([evc])
1458
        self.assertFalse(result[evc.id])
1459
1460
    @patch(
1461
        "napps.kytos.mef_eline.models.path.DynamicPathManager"
1462
        ".get_disjoint_paths"
1463
    )
1464
    def test_get_failover_path_vandidates(self, get_disjoint_paths_mock):
1465
        """Test get_failover_path_candidates method"""
1466
        self.evc_deploy.get_failover_path_candidates()
1467
        get_disjoint_paths_mock.assert_called_once()
1468
1469
    def test_is_failover_path_affected_by_link(self):
1470
        """Test is_failover_path_affected_by_link method"""
1471
        link1 = get_link_mocked(endpoint_a_port=1, endpoint_b_port=2)
1472
        link2 = get_link_mocked(endpoint_a_port=3, endpoint_b_port=4)
1473
        link3 = get_link_mocked(endpoint_a_port=5, endpoint_b_port=6)
1474
        self.evc_deploy.failover_path = Path([link1, link2])
1475
        self.assertTrue(
1476
            self.evc_deploy.is_failover_path_affected_by_link(link1)
1477
        )
1478
        self.assertFalse(
1479
            self.evc_deploy.is_failover_path_affected_by_link(link3)
1480
        )
1481
1482
    def test_is_eligible_for_failover_path(self):
1483
        """Test is_eligible_for_failover_path method"""
1484
        self.assertFalse(self.evc_deploy.is_eligible_for_failover_path())
1485
        self.evc_deploy.dynamic_backup_path = True
1486
        self.evc_deploy.primary_path = Path([])
1487
        self.evc_deploy.backup_path = Path([])
1488
        self.assertTrue(self.evc_deploy.is_eligible_for_failover_path())
1489
1490
    def test_get_value_from_uni_tag(self):
1491
        """Test _get_value_from_uni_tag"""
1492
        uni = get_uni_mocked(tag_value=None)
1493
        value = EVC._get_value_from_uni_tag(uni)
1494
        self.assertEqual(value, None)
1495
1496
        uni = get_uni_mocked(tag_value="any")
1497
        value = EVC._get_value_from_uni_tag(uni)
1498
        self.assertEqual(value, "4096/4096")
1499
1500
        uni = get_uni_mocked(tag_value="untagged")
1501
        value = EVC._get_value_from_uni_tag(uni)
1502
        self.assertEqual(value, 0)
1503
1504
        uni = get_uni_mocked(tag_value=100)
1505
        value = EVC._get_value_from_uni_tag(uni)
1506
        self.assertEqual(value, 100)
1507