Passed
Pull Request — master (#226)
by Italo Valcy
03:28
created

TestEVC.test_install_nni_flows()   A

Complexity

Conditions 1

Size

Total Lines 41
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

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