Passed
Pull Request — master (#258)
by
unknown
03:28
created

TestEVC.test_get_priority()   A

Complexity

Conditions 1

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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