Test Failed
Pull Request — master (#175)
by Italo Valcy
03:23
created

TestEVC.test_should_deploy_case1()   A

Complexity

Conditions 1

Size

Total Lines 15
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 12
nop 1
dl 0
loc 15
rs 9.8
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
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
11
# pylint: disable=wrong-import-position
12
sys.path.insert(0, "/var/lib/kytos/napps/..")
13
# pylint: enable=wrong-import-position
14
15
from napps.kytos.mef_eline.models import EVC, EVCDeploy, Path  # NOQA
16
from napps.kytos.mef_eline.settings import MANAGER_URL  # NOQA
17
from napps.kytos.mef_eline.exceptions import FlowModException  # NOQA
18
from napps.kytos.mef_eline.tests.helpers import (
19
    get_link_mocked,
20
    get_uni_mocked,
21
    get_controller_mock,
22
)  # NOQA
23
24
25
# pylint: disable=too-many-public-methods, too-many-lines
26
class TestEVC(TestCase):
27
    """Tests to verify EVC class."""
28
29
    def setUp(self):
30
        attributes = {
31
            "controller": get_controller_mock(),
32
            "name": "circuit_for_tests",
33
            "uni_a": get_uni_mocked(is_valid=True),
34
            "uni_z": get_uni_mocked(is_valid=True),
35
        }
36
        self.evc_deploy = EVCDeploy(**attributes)
37
38
    def test_primary_links_zipped(self):
39
        """Test primary links zipped method."""
40
41
    @staticmethod
42
    @patch("napps.kytos.mef_eline.models.evc.log")
43
    def test_should_deploy_case1(log_mock):
44
        """Test should deploy method without primary links."""
45
        log_mock.debug.return_value = True
46
        attributes = {
47
            "controller": get_controller_mock(),
48
            "name": "custom_name",
49
            "uni_a": get_uni_mocked(is_valid=True),
50
            "uni_z": get_uni_mocked(is_valid=True),
51
        }
52
53
        evc = EVC(**attributes)
54
        evc.should_deploy()
55
        log_mock.debug.assert_called_with("Path is empty.")
56
57
    @patch("napps.kytos.mef_eline.models.evc.log")
58
    def test_should_deploy_case2(self, log_mock):
59
        """Test should deploy method with disable circuit."""
60
        log_mock.debug.return_value = True
61
        attributes = {
62
            "controller": get_controller_mock(),
63
            "name": "custom_name",
64
            "uni_a": get_uni_mocked(is_valid=True),
65
            "uni_z": get_uni_mocked(is_valid=True),
66
            "primary_links": [get_link_mocked(), get_link_mocked()],
67
        }
68
        evc = EVC(**attributes)
69
70
        self.assertFalse(evc.should_deploy(attributes["primary_links"]))
71
        log_mock.debug.assert_called_with(f"{evc} is disabled.")
72
73
    @patch("napps.kytos.mef_eline.models.evc.log")
74
    def test_should_deploy_case3(self, log_mock):
75
        """Test should deploy method with enabled and not active circuit."""
76
        log_mock.debug.return_value = True
77
        attributes = {
78
            "controller": get_controller_mock(),
79
            "name": "custom_name",
80
            "uni_a": get_uni_mocked(is_valid=True),
81
            "uni_z": get_uni_mocked(is_valid=True),
82
            "primary_links": [get_link_mocked(), get_link_mocked()],
83
            "enabled": True,
84
        }
85
        evc = EVC(**attributes)
86
        self.assertTrue(evc.should_deploy(attributes["primary_links"]))
87
        log_mock.debug.assert_called_with(f"{evc} will be deployed.")
88
89
    @patch("napps.kytos.mef_eline.models.evc.log")
90
    def test_should_deploy_case4(self, log_mock):
91
        """Test should deploy method with enabled and active circuit."""
92
        log_mock.debug.return_value = True
93
        attributes = {
94
            "controller": get_controller_mock(),
95
            "name": "custom_name",
96
            "uni_a": get_uni_mocked(is_valid=True),
97
            "uni_z": get_uni_mocked(is_valid=True),
98
            "primary_links": [get_link_mocked(), get_link_mocked()],
99
            "enabled": True,
100
            "active": True,
101
        }
102
        evc = EVC(**attributes)
103
        self.assertFalse(evc.should_deploy(attributes["primary_links"]))
104
105
    @patch("napps.kytos.mef_eline.models.evc.requests")
106
    def test_send_flow_mods_case1(self, requests_mock):
107
        """Test if you are sending flow_mods."""
108
        flow_mods = {"id": 20}
109
        switch = Mock(spec=Switch, id=1)
110
111
        response = MagicMock()
112
        response.status_code = 201
113
        requests_mock.post.return_value = response
114
115
        # pylint: disable=protected-access
116
        EVC._send_flow_mods(switch.id, flow_mods)
117
118
        expected_endpoint = f"{MANAGER_URL}/flows/{switch.id}"
119
        expected_data = {"flows": flow_mods, "force": False}
120
        self.assertEqual(requests_mock.post.call_count, 1)
121
        requests_mock.post.assert_called_once_with(
122
            expected_endpoint, json=expected_data
123
        )
124
125
    @patch("napps.kytos.mef_eline.models.evc.requests")
126
    def test_send_flow_mods_case2(self, requests_mock):
127
        """Test if you are sending flow_mods."""
128
        flow_mods = {"id": 20}
129
        switch = Mock(spec=Switch, id=1)
130
        response = MagicMock()
131
        response.status_code = 201
132
        requests_mock.post.return_value = response
133
134
        # pylint: disable=protected-access
135
        EVC._send_flow_mods(switch.id, flow_mods, command='delete', force=True)
136
137
        expected_endpoint = f"{MANAGER_URL}/delete/{switch.id}"
138
        expected_data = {"flows": flow_mods, "force": True}
139
        self.assertEqual(requests_mock.post.call_count, 1)
140
        requests_mock.post.assert_called_once_with(
141
            expected_endpoint, json=expected_data
142
        )
143
144
    @patch("napps.kytos.mef_eline.models.evc.requests")
145
    def test_send_flow_mods_error(self, requests_mock):
146
        """Test flow_manager call fails."""
147
        flow_mods = {"id": 20}
148
        switch = Mock(spec=Switch, id=1)
149
        response = MagicMock()
150
        response.status_code = 415
151
        requests_mock.post.return_value = response
152
153
        # pylint: disable=protected-access
154
        with self.assertRaises(FlowModException):
155
            EVC._send_flow_mods(
156
                switch.id,
157
                flow_mods,
158
                command='delete',
159
                force=True
160
            )
161
162
    def test_prepare_flow_mod(self):
163
        """Test prepare flow_mod method."""
164
        interface_a = Interface("eth0", 1, Mock(spec=Switch))
165
        interface_z = Interface("eth1", 3, Mock(spec=Switch))
166
        attributes = {
167
            "controller": get_controller_mock(),
168
            "name": "custom_name",
169
            "uni_a": get_uni_mocked(is_valid=True),
170
            "uni_z": get_uni_mocked(is_valid=True),
171
            "primary_links": [get_link_mocked(), get_link_mocked()],
172
            "enabled": True,
173
            "active": True,
174
        }
175
        evc = EVC(**attributes)
176
177
        # pylint: disable=protected-access
178
        flow_mod = evc._prepare_flow_mod(interface_a, interface_z)
179
        expected_flow_mod = {
180
            "match": {"in_port": interface_a.port_number},
181
            "cookie": evc.get_cookie(),
182
            "actions": [
183
                {"action_type": "output", "port": interface_z.port_number}
184
            ],
185
        }
186
        self.assertEqual(expected_flow_mod, flow_mod)
187
188
    def test_prepare_pop_flow(self):
189
        """Test prepare pop flow  method."""
190
        attributes = {
191
            "controller": get_controller_mock(),
192
            "name": "custom_name",
193
            "uni_a": get_uni_mocked(interface_port=1, is_valid=True),
194
            "uni_z": get_uni_mocked(interface_port=2, is_valid=True),
195
        }
196
        evc = EVC(**attributes)
197
        interface_a = evc.uni_a.interface
198
        interface_z = evc.uni_z.interface
199
        in_vlan = 10
200
201
        # pylint: disable=protected-access
202
        flow_mod = evc._prepare_pop_flow(
203
            interface_a, interface_z, in_vlan
204
        )
205
206
        expected_flow_mod = {
207
            "match": {"in_port": interface_a.port_number, "dl_vlan": in_vlan},
208
            "cookie": evc.get_cookie(),
209
            "actions": [
210
                {"action_type": "pop_vlan"},
211
                {"action_type": "output", "port": interface_z.port_number},
212
            ],
213
        }
214
        self.assertEqual(expected_flow_mod, flow_mod)
215
216
    def test_prepare_push_flow(self):
217
        """Test prepare push flow method."""
218
        attributes = {
219
            "controller": get_controller_mock(),
220
            "name": "custom_name",
221
            "uni_a": get_uni_mocked(interface_port=1, is_valid=True),
222
            "uni_z": get_uni_mocked(interface_port=2, is_valid=True),
223
        }
224
        evc = EVC(**attributes)
225
        interface_a = evc.uni_a.interface
226
        interface_z = evc.uni_z.interface
227
        out_vlan_a = 20
228
229
        for in_vlan_a in (10, None):
230
            for in_vlan_z in (3, None):
231
                with self.subTest(in_vlan_a=in_vlan_a, in_vlan_z=in_vlan_z):
232
                    # pylint: disable=protected-access
233
                    flow_mod = evc._prepare_push_flow(interface_a, interface_z,
234
                                                      in_vlan_a, out_vlan_a,
235
                                                      in_vlan_z)
236
237
                    expected_flow_mod = {
238
                        'match': {'in_port': interface_a.port_number},
239
                        'cookie': evc.get_cookie(),
240
                        'actions': [
241
                            {'action_type': 'push_vlan', 'tag_type': 's'},
242
                            {'action_type': 'set_vlan', 'vlan_id': out_vlan_a},
243
                            {
244
                                'action_type': 'output',
245
                                'port': interface_z.port_number
246
                            }
247
                        ]
248
                    }
249
                    if in_vlan_a and in_vlan_z:
250
                        expected_flow_mod['match']['dl_vlan'] = in_vlan_a
251
                        expected_flow_mod['actions'].insert(0, {
252
                            'action_type': 'set_vlan', 'vlan_id': in_vlan_z
253
                        })
254
                    elif in_vlan_a:
255
                        expected_flow_mod['match']['dl_vlan'] = in_vlan_a
256
                        expected_flow_mod['actions'].insert(0, {
257
                            'action_type': 'pop_vlan'
258
                        })
259
                    elif in_vlan_z:
260
                        expected_flow_mod['actions'].insert(0, {
261
                            'action_type': 'set_vlan', 'vlan_id': in_vlan_z
262
                        })
263
                        expected_flow_mod['actions'].insert(0, {
264
                            'action_type': 'push_vlan', 'tag_type': 'c'
265
                        })
266
                    self.assertEqual(expected_flow_mod, flow_mod)
267
268
    @staticmethod
269
    @patch("napps.kytos.mef_eline.models.evc.EVC._send_flow_mods")
270
    def test_install_uni_flows(send_flow_mods_mock):
271
        """Test install uni flows method.
272
273
        This test will verify the flows send to the send_flow_mods method.
274
        """
275
        uni_a = get_uni_mocked(
276
            interface_port=2,
277
            tag_value=82,
278
            switch_id="switch_uni_a",
279
            is_valid=True,
280
        )
281
        uni_z = get_uni_mocked(
282
            interface_port=3,
283
            tag_value=83,
284
            switch_id="switch_uni_z",
285
            is_valid=True,
286
        )
287
288
        attributes = {
289
            "controller": get_controller_mock(),
290
            "name": "custom_name",
291
            "uni_a": uni_a,
292
            "uni_z": uni_z,
293
            "primary_links": [
294
                get_link_mocked(
295
                    endpoint_a_port=9,
296
                    endpoint_b_port=10,
297
                    metadata={"s_vlan": 5},
298
                ),
299
                get_link_mocked(
300
                    endpoint_a_port=11,
301
                    endpoint_b_port=12,
302
                    metadata={"s_vlan": 6},
303
                ),
304
            ],
305
        }
306
        evc = EVC(**attributes)
307
308
        # pylint: disable=protected-access
309
        evc._install_uni_flows(attributes["primary_links"])
310
311
        expected_flow_mod_a = [
312
            {
313
                "match": {
314
                    "in_port": uni_a.interface.port_number,
315
                    "dl_vlan": uni_a.user_tag.value,
316
                },
317
                "cookie": evc.get_cookie(),
318
                "actions": [
319
                    {
320
                        "action_type": "set_vlan",
321
                        "vlan_id": uni_z.user_tag.value
322
                    },
323
                    {"action_type": "push_vlan", "tag_type": "s"},
324
                    {
325
                        "action_type": "set_vlan",
326
                        "vlan_id": evc.primary_links[0]
327
                        .get_metadata("s_vlan")
328
                        .value,
329
                    },
330
                    {
331
                        "action_type": "output",
332
                        "port": evc.primary_links[0].endpoint_a.port_number,
333
                    },
334
                ],
335
            },
336
            {
337
                "match": {
338
                    "in_port": evc.primary_links[0].endpoint_a.port_number,
339
                    "dl_vlan": evc.primary_links[0]
340
                    .get_metadata("s_vlan")
341
                    .value,
342
                },
343
                "cookie": evc.get_cookie(),
344
                "actions": [
345
                    {"action_type": "pop_vlan"},
346
                    {
347
                        "action_type": "output",
348
                        "port": uni_a.interface.port_number,
349
                    },
350
                ],
351
            },
352
        ]
353
354
        send_flow_mods_mock.assert_any_call(
355
            uni_a.interface.switch.id, expected_flow_mod_a
356
        )
357
358
        expected_flow_mod_z = [
359
            {
360
                "match": {
361
                    "in_port": uni_z.interface.port_number,
362
                    "dl_vlan": uni_z.user_tag.value,
363
                },
364
                "cookie": evc.get_cookie(),
365
                "actions": [
366
                    {
367
                        "action_type": "set_vlan",
368
                        "vlan_id": uni_a.user_tag.value
369
                    },
370
                    {"action_type": "push_vlan", "tag_type": "s"},
371
                    {
372
                        "action_type": "set_vlan",
373
                        "vlan_id": evc.primary_links[-1]
374
                        .get_metadata("s_vlan")
375
                        .value,
376
                    },
377
                    {
378
                        "action_type": "output",
379
                        "port": evc.primary_links[-1].endpoint_b.port_number,
380
                    },
381
                ],
382
            },
383
            {
384
                "match": {
385
                    "in_port": evc.primary_links[-1].endpoint_b.port_number,
386
                    "dl_vlan": evc.primary_links[-1]
387
                    .get_metadata("s_vlan")
388
                    .value,
389
                },
390
                "cookie": evc.get_cookie(),
391
                "actions": [
392
                    {"action_type": "pop_vlan"},
393
                    {
394
                        "action_type": "output",
395
                        "port": uni_z.interface.port_number,
396
                    },
397
                ],
398
            },
399
        ]
400
401
        send_flow_mods_mock.assert_any_call(
402
            uni_z.interface.switch.id, expected_flow_mod_z
403
        )
404
405
    @staticmethod
406
    def create_evc_inter_switch():
407
        """Create inter-switch EVC with two links in the path"""
408
        uni_a = get_uni_mocked(
409
            interface_port=2,
410
            tag_value=82,
411
            switch_id=1,
412
            switch_dpid=1,
413
            is_valid=True,
414
        )
415
        uni_z = get_uni_mocked(
416
            interface_port=3,
417
            tag_value=83,
418
            switch_id=3,
419
            switch_dpid=3,
420
            is_valid=True,
421
        )
422
423
        attributes = {
424
            "controller": get_controller_mock(),
425
            "name": "custom_name",
426
            "id": "1",
427
            "uni_a": uni_a,
428
            "uni_z": uni_z,
429
            "primary_links": [
430
                get_link_mocked(
431
                    switch_a=Switch(1),
432
                    switch_b=Switch(2),
433
                    endpoint_a_port=9,
434
                    endpoint_b_port=10,
435
                    metadata={"s_vlan": 5},
436
                ),
437
                get_link_mocked(
438
                    switch_a=Switch(2),
439
                    switch_b=Switch(3),
440
                    endpoint_a_port=11,
441
                    endpoint_b_port=12,
442
                    metadata={"s_vlan": 6},
443
                ),
444
            ],
445
        }
446
        return EVC(**attributes)
447
448
    @staticmethod
449
    @patch("napps.kytos.mef_eline.models.evc.EVC._send_flow_mods")
450
    def test_install_nni_flows(send_flow_mods_mock):
451
        """Test install nni flows method.
452
453
        This test will verify the flows send to the send_flow_mods method.
454
        """
455
        evc = TestEVC.create_evc_inter_switch()
456
457
        # pylint: disable=protected-access
458
        evc._install_nni_flows(evc.primary_links)
459
460
        in_vlan = evc.primary_links[0].get_metadata("s_vlan").value
461
        out_vlan = evc.primary_links[-1].get_metadata("s_vlan").value
462
463
        in_port = evc.primary_links[0].endpoint_b.port_number
464
        out_port = evc.primary_links[-1].endpoint_a.port_number
465
466
        expected_flow_mods = [
467
            {
468
                "match": {"in_port": in_port, "dl_vlan": in_vlan},
469
                "cookie": evc.get_cookie(),
470
                "actions": [
471
                    {"action_type": "set_vlan", "vlan_id": out_vlan},
472
                    {"action_type": "output", "port": out_port},
473
                ],
474
            },
475
            {
476
                "match": {"in_port": out_port, "dl_vlan": out_vlan},
477
                "cookie": evc.get_cookie(),
478
                "actions": [
479
                    {"action_type": "set_vlan", "vlan_id": in_vlan},
480
                    {"action_type": "output", "port": in_port},
481
                ],
482
            },
483
        ]
484
485
        dpid = evc.primary_links[0].endpoint_b.switch.id
486
        send_flow_mods_mock.assert_called_once_with(dpid, expected_flow_mods)
487
488
    @patch("requests.post")
489
    @patch("napps.kytos.mef_eline.controllers.ELineController.upsert_evc")
490
    @patch("napps.kytos.mef_eline.models.evc.log")
491
    @patch("napps.kytos.mef_eline.models.path.Path.choose_vlans")
492
    @patch("napps.kytos.mef_eline.models.evc.EVC._install_nni_flows")
493
    @patch("napps.kytos.mef_eline.models.evc.EVC._install_uni_flows")
494
    @patch("napps.kytos.mef_eline.models.evc.EVC._install_direct_uni_flows")
495
    @patch("napps.kytos.mef_eline.models.evc.EVC.activate")
496
    @patch("napps.kytos.mef_eline.models.evc.EVC.should_deploy")
497
    def test_deploy_successfully(self, *args):
498
        """Test if all methods to deploy are called."""
499
        # pylint: disable=too-many-locals
500
        (
501
            should_deploy_mock,
502
            activate_mock,
503
            install_direct_uni_flows_mock,
504
            install_uni_flows_mock,
505
            install_nni_flows,
506
            chose_vlans_mock,
507
            log_mock,
508
            _,
509
            requests_mock,
510
        ) = args
511
512
        response = MagicMock()
513
        response.status_code = 201
514
        requests_mock.return_value = response
515
516
        should_deploy_mock.return_value = True
517
        evc = self.create_evc_inter_switch()
518
        deployed = evc.deploy_to_path(evc.primary_links)
519
520
        self.assertEqual(should_deploy_mock.call_count, 1)
521
        self.assertEqual(activate_mock.call_count, 1)
522
        self.assertEqual(install_uni_flows_mock.call_count, 1)
523
        self.assertEqual(install_nni_flows.call_count, 1)
524
        self.assertEqual(chose_vlans_mock.call_count, 1)
525
        log_mock.info.assert_called_with(f"{evc} was deployed.")
526
        self.assertTrue(deployed)
527
528
        # intra switch EVC
529
        evc = self.create_evc_intra_switch()
530
        self.assertTrue(evc.deploy_to_path(evc.primary_links))
531
        self.assertEqual(install_direct_uni_flows_mock.call_count, 1)
532
        self.assertEqual(activate_mock.call_count, 2)
533
        self.assertEqual(log_mock.info.call_count, 2)
534
        log_mock.info.assert_called_with(f"{evc} was deployed.")
535
536
    @patch("requests.post")
537
    @patch("napps.kytos.mef_eline.models.evc.log")
538
    @patch("napps.kytos.mef_eline.models.evc.EVC.discover_new_paths")
539
    @patch("napps.kytos.mef_eline.models.path.Path.choose_vlans")
540
    @patch("napps.kytos.mef_eline.models.evc.EVC._install_nni_flows")
541
    @patch("napps.kytos.mef_eline.models.evc.EVC._install_uni_flows")
542
    @patch("napps.kytos.mef_eline.models.evc.EVC.activate")
543
    @patch("napps.kytos.mef_eline.models.evc.EVC.should_deploy")
544
    @patch("napps.kytos.mef_eline.models.EVC.sync")
545
    def test_deploy_fail(self, *args):
546
        """Test if all methods is ignored when the should_deploy is false."""
547
        # pylint: disable=too-many-locals
548
        (
549
            sync_mock,
550
            should_deploy_mock,
551
            activate_mock,
552
            install_uni_flows_mock,
553
            install_nni_flows,
554
            choose_vlans_mock,
555
            discover_new_paths_mock,
556
            log_mock,
557
            requests_mock,
558
        ) = args
559
560
        response = MagicMock()
561
        response.status_code = 201
562
        requests_mock.return_value = response
563
564
        evc = self.create_evc_inter_switch()
565
        should_deploy_mock.return_value = False
566
        discover_new_paths_mock.return_value = []
567
        deployed = evc.deploy_to_path()
568
569
        self.assertEqual(discover_new_paths_mock.call_count, 1)
570
        self.assertEqual(should_deploy_mock.call_count, 1)
571
        self.assertEqual(activate_mock.call_count, 0)
572
        self.assertEqual(install_uni_flows_mock.call_count, 0)
573
        self.assertEqual(install_nni_flows.call_count, 0)
574
        self.assertEqual(choose_vlans_mock.call_count, 0)
575
        self.assertEqual(log_mock.info.call_count, 0)
576
        self.assertEqual(sync_mock.call_count, 1)
577
        self.assertFalse(deployed)
578
579
        # NoTagAvailable on static path
580
        should_deploy_mock.return_value = True
581
        choose_vlans_mock.side_effect = KytosNoTagAvailableError("error")
582
        self.assertFalse(evc.deploy_to_path(evc.primary_links))
583
584
        # NoTagAvailable on dynamic path
585
        should_deploy_mock.return_value = False
586
        discover_new_paths_mock.return_value = [Path(['a', 'b'])]
587
        choose_vlans_mock.side_effect = KytosNoTagAvailableError("error")
588
        self.assertFalse(evc.deploy_to_path(evc.primary_links))
589
590
    @patch("napps.kytos.mef_eline.models.evc.log")
591
    @patch(
592
        "napps.kytos.mef_eline.models.evc.EVC.discover_new_paths",
593
        return_value=[],
594
    )
595
    @patch("napps.kytos.mef_eline.models.path.Path.choose_vlans")
596
    @patch("napps.kytos.mef_eline.models.evc.EVC._install_nni_flows")
597
    @patch("napps.kytos.mef_eline.models.evc.EVC.should_deploy")
598
    @patch("napps.kytos.mef_eline.models.evc.EVC.remove_current_flows")
599
    @patch("napps.kytos.mef_eline.models.evc.EVC.sync")
600
    def test_deploy_error(self, *args):
601
        """Test if all methods is ignored when the should_deploy is false."""
602
        # pylint: disable=too-many-locals
603
        (
604
            sync_mock,
605
            remove_current_flows,
606
            should_deploy_mock,
607
            install_nni_flows,
608
            choose_vlans_mock,
609
            discover_new_paths,
610
            log_mock,
611
        ) = args
612
613
        install_nni_flows.side_effect = FlowModException
614
        should_deploy_mock.return_value = True
615
        uni_a = get_uni_mocked(
616
            interface_port=2,
617
            tag_value=82,
618
            switch_id="switch_uni_a",
619
            is_valid=True,
620
        )
621
        uni_z = get_uni_mocked(
622
            interface_port=3,
623
            tag_value=83,
624
            switch_id="switch_uni_z",
625
            is_valid=True,
626
        )
627
628
        primary_links = [
629
            get_link_mocked(
630
                endpoint_a_port=9, endpoint_b_port=10, metadata={"s_vlan": 5}
631
            ),
632
            get_link_mocked(
633
                endpoint_a_port=11, endpoint_b_port=12, metadata={"s_vlan": 6}
634
            ),
635
        ]
636
637
        attributes = {
638
            "controller": get_controller_mock(),
639
            "name": "custom_name",
640
            "uni_a": uni_a,
641
            "uni_z": uni_z,
642
            "primary_links": primary_links,
643
            "queue_id": 5,
644
        }
645
        # Setup path to deploy
646
        path = Path()
647
        path.append(primary_links[0])
648
        path.append(primary_links[1])
649
650
        evc = EVC(**attributes)
651
652
        deployed = evc.deploy_to_path(path)
653
654
        self.assertEqual(discover_new_paths.call_count, 0)
655
        self.assertEqual(should_deploy_mock.call_count, 1)
656
        self.assertEqual(install_nni_flows.call_count, 1)
657
        self.assertEqual(choose_vlans_mock.call_count, 1)
658
        self.assertEqual(log_mock.error.call_count, 1)
659
        self.assertEqual(sync_mock.call_count, 0)
660
        self.assertEqual(remove_current_flows.call_count, 2)
661
        self.assertFalse(deployed)
662
663
    @patch("napps.kytos.mef_eline.models.evc.notify_link_available_tags")
664
    @patch("napps.kytos.mef_eline.models.evc.EVC.get_failover_path_candidates")
665
    @patch("napps.kytos.mef_eline.models.evc.EVC._install_nni_flows")
666
    @patch("napps.kytos.mef_eline.models.evc.EVC._install_uni_flows")
667
    @patch("napps.kytos.mef_eline.models.evc.EVC.remove_path_flows")
668
    @patch("napps.kytos.mef_eline.models.EVC.sync")
669
    def test_setup_failover_path(self, *args):
670
        """Test setup_failover_path method."""
671
        (
672
            sync_mock,
673
            remove_path_flows_mock,
674
            install_uni_flows_mock,
675
            install_nni_flows_mock,
676
            get_failover_path_candidates_mock,
677
            notify_mock,
678
        ) = args
679
680
        # case1: early return intra switch
681
        evc1 = self.create_evc_intra_switch()
682
683
        self.assertFalse(evc1.setup_failover_path())
684
        self.assertEqual(sync_mock.call_count, 0)
685
686
        # case2: early return not eligible for path failover
687
        evc2 = self.create_evc_inter_switch()
688
        evc2.is_eligible_for_failover_path = MagicMock(return_value=False)
689
690
        self.assertFalse(evc2.setup_failover_path())
691
        self.assertEqual(sync_mock.call_count, 0)
692
693
        # case3: success failover_path setup
694
        evc2.is_eligible_for_failover_path = MagicMock(return_value=True)
695
        evc2.failover_path = ["link1", "link2"]
696
        path_mock = MagicMock()
697
        path_mock.__iter__.return_value = ["link3"]
698
        get_failover_path_candidates_mock.return_value = [None, path_mock]
699
700
        self.assertTrue(evc2.setup_failover_path())
701
        remove_path_flows_mock.assert_called_with(["link1", "link2"])
702
        path_mock.choose_vlans.assert_called()
703
        notify_mock.assert_called()
704
        install_nni_flows_mock.assert_called_with(path_mock)
705
        install_uni_flows_mock.assert_called_with(path_mock, skip_in=True)
706
        self.assertEqual(evc2.failover_path, path_mock)
707
        self.assertEqual(sync_mock.call_count, 1)
708
709
        # case 4: failed to setup failover_path - No Tag available
710
        evc2.failover_path = []
711
        path_mock.choose_vlans.side_effect = KytosNoTagAvailableError("error")
712
        sync_mock.call_count = 0
713
714
        self.assertFalse(evc2.setup_failover_path())
715
        self.assertEqual(list(evc2.failover_path), [])
716
        self.assertEqual(sync_mock.call_count, 1)
717
718
        # case 5: failed to setup failover_path - FlowMod exception
719
        evc2.failover_path = []
720
        install_nni_flows_mock.side_effect = FlowModException("error")
721
        sync_mock.call_count = 0
722
723
        self.assertFalse(evc2.setup_failover_path())
724
        self.assertEqual(list(evc2.failover_path), [])
725
        self.assertEqual(sync_mock.call_count, 1)
726
727
    @patch("napps.kytos.mef_eline.models.evc.EVC.deploy_to_path")
728
    @patch("napps.kytos.mef_eline.models.evc.EVC.discover_new_paths")
729
    def test_deploy_to_backup_path1(
730
        self, discover_new_paths_mocked, deploy_to_path_mocked
731
    ):
732
        """Test deployment when dynamic_backup_path is False in same switch"""
733
        uni_a = get_uni_mocked(interface_port=2, tag_value=82, is_valid=True)
734
        uni_z = get_uni_mocked(interface_port=3, tag_value=83, is_valid=True)
735
736
        switch = Mock(spec=Switch)
737
        uni_a.interface.switch = switch
738
        uni_z.interface.switch = switch
739
740
        attributes = {
741
            "controller": get_controller_mock(),
742
            "name": "custom_name",
743
            "uni_a": uni_a,
744
            "uni_z": uni_z,
745
            "enabled": True,
746
            "dynamic_backup_path": False,
747
        }
748
749
        evc = EVC(**attributes)
750
        discover_new_paths_mocked.return_value = []
751
        deploy_to_path_mocked.return_value = True
752
753
        deployed = evc.deploy_to_backup_path()
754
755
        deploy_to_path_mocked.assert_called_once_with()
756
        self.assertEqual(deployed, True)
757
758
    @patch("requests.post")
759
    @patch("napps.kytos.mef_eline.controllers.ELineController.upsert_evc")
760
    @patch("napps.kytos.mef_eline.models.evc.log")
761
    @patch("napps.kytos.mef_eline.models.path.Path.choose_vlans")
762
    @patch("napps.kytos.mef_eline.models.evc.EVC._install_nni_flows")
763
    @patch("napps.kytos.mef_eline.models.evc.EVC._install_uni_flows")
764
    @patch("napps.kytos.mef_eline.models.evc.EVC.activate")
765
    @patch("napps.kytos.mef_eline.models.evc.EVC.should_deploy")
766
    @patch("napps.kytos.mef_eline.models.evc.EVC.discover_new_paths")
767
    def test_deploy_without_path_case1(self, *args):
768
        """Test if not path is found a dynamic path is used."""
769
        # pylint: disable=too-many-locals
770
        (
771
            discover_new_paths_mocked,
772
            should_deploy_mock,
773
            activate_mock,
774
            install_uni_flows_mock,
775
            install_nni_flows,
776
            chose_vlans_mock,
777
            log_mock,
778
            _,
779
            requests_mock,
780
        ) = args
781
782
        response = MagicMock()
783
        response.status_code = 201
784
        requests_mock.return_value = response
785
786
        should_deploy_mock.return_value = False
787
        uni_a = get_uni_mocked(
788
            interface_port=2,
789
            tag_value=82,
790
            switch_id="switch_uni_a",
791
            is_valid=True,
792
        )
793
        uni_z = get_uni_mocked(
794
            interface_port=3,
795
            tag_value=83,
796
            switch_id="switch_uni_z",
797
            is_valid=True,
798
        )
799
800
        attributes = {
801
            "controller": get_controller_mock(),
802
            "name": "custom_name",
803
            "uni_a": uni_a,
804
            "uni_z": uni_z,
805
            "enabled": True,
806
            "dynamic_backup_path": False,
807
        }
808
809
        dynamic_backup_path = Path(
810
            [
811
                get_link_mocked(
812
                    endpoint_a_port=9,
813
                    endpoint_b_port=10,
814
                    metadata={"s_vlan": 5},
815
                ),
816
                get_link_mocked(
817
                    endpoint_a_port=11,
818
                    endpoint_b_port=12,
819
                    metadata={"s_vlan": 6},
820
                ),
821
            ]
822
        )
823
824
        evc = EVC(**attributes)
825
        discover_new_paths_mocked.return_value = [dynamic_backup_path]
826
827
        deployed = evc.deploy_to_path()
828
829
        self.assertEqual(should_deploy_mock.call_count, 1)
830
        self.assertEqual(discover_new_paths_mocked.call_count, 1)
831
        self.assertEqual(activate_mock.call_count, 1)
832
        self.assertEqual(install_uni_flows_mock.call_count, 1)
833
        self.assertEqual(install_nni_flows.call_count, 1)
834
        self.assertEqual(chose_vlans_mock.call_count, 1)
835
        log_mock.info.assert_called_with(f"{evc} was deployed.")
836
        self.assertTrue(deployed)
837
838
    @patch("napps.kytos.mef_eline.models.evc.EVCDeploy.deploy_to_primary_path")
839
    @patch("napps.kytos.mef_eline.models.evc.EVCDeploy.deploy_to_backup_path")
840
    @patch("napps.kytos.mef_eline.models.evc.emit_event")
841
    def test_deploy(self, *args):
842
        """Test method deploy"""
843
        (emit_event_mock, deploy_primary_mock, deploy_backup_mock) = args
844
845
        # case 1: deploy to primary
846
        self.evc_deploy.archived = False
847
        deploy_primary_mock.return_value = True
848
        self.assertTrue(self.evc_deploy.deploy())
849
        self.assertEqual(emit_event_mock.call_count, 1)
850
851
        # case 2: deploy to backup
852
        deploy_primary_mock.return_value = False
853
        deploy_backup_mock.return_value = True
854
        self.assertTrue(self.evc_deploy.deploy())
855
        self.assertEqual(emit_event_mock.call_count, 2)
856
857
        # case 3: fail to deploy to primary and backup
858
        deploy_backup_mock.return_value = False
859
        self.assertFalse(self.evc_deploy.deploy())
860
        self.assertEqual(emit_event_mock.call_count, 2)
861
862
        # case 4: archived
863
        self.evc_deploy.archived = True
864
        self.assertFalse(self.evc_deploy.deploy())
865
        self.assertEqual(emit_event_mock.call_count, 2)
866
867
    @patch("napps.kytos.mef_eline.models.evc.EVCDeploy.remove_current_flows")
868
    @patch("napps.kytos.mef_eline.models.evc.EVCDeploy.sync")
869
    @patch("napps.kytos.mef_eline.models.evc.emit_event")
870
    def test_remove(self, *args):
871
        """Test method remove"""
872
        (emit_event_mock, sync_mock, remove_flows_mock) = args
873
        self.evc_deploy.remove()
874
        remove_flows_mock.assert_called()
875
        sync_mock.assert_called()
876
        emit_event_mock.assert_called()
877
        self.assertFalse(self.evc_deploy.is_enabled())
878
879
    @patch("napps.kytos.mef_eline.controllers.ELineController.upsert_evc")
880
    @patch("napps.kytos.mef_eline.models.evc.notify_link_available_tags")
881
    @patch("napps.kytos.mef_eline.models.evc.EVC._send_flow_mods")
882
    @patch("napps.kytos.mef_eline.models.evc.log.error")
883
    def test_remove_current_flows(self, *args):
884
        """Test remove current flows."""
885
        # pylint: disable=too-many-locals
886
        (log_error_mock, send_flow_mods_mocked, notify_mock, _) = args
887
        uni_a = get_uni_mocked(
888
            interface_port=2,
889
            tag_value=82,
890
            switch_id="switch_uni_a",
891
            is_valid=True,
892
        )
893
        uni_z = get_uni_mocked(
894
            interface_port=3,
895
            tag_value=83,
896
            switch_id="switch_uni_z",
897
            is_valid=True,
898
        )
899
900
        switch_a = Switch("00:00:00:00:00:01")
901
        switch_b = Switch("00:00:00:00:00:02")
902
        switch_c = Switch("00:00:00:00:00:03")
903
904
        attributes = {
905
            "controller": get_controller_mock(),
906
            "name": "custom_name",
907
            "uni_a": uni_a,
908
            "uni_z": uni_z,
909
            "active": True,
910
            "enabled": True,
911
            "primary_links": [
912
                get_link_mocked(
913
                    switch_a=switch_a,
914
                    switch_b=switch_b,
915
                    endpoint_a_port=9,
916
                    endpoint_b_port=10,
917
                    metadata={"s_vlan": 5},
918
                ),
919
                get_link_mocked(
920
                    switch_a=switch_b,
921
                    switch_b=switch_c,
922
                    endpoint_a_port=11,
923
                    endpoint_b_port=12,
924
                    metadata={"s_vlan": 6},
925
                ),
926
            ],
927
        }
928
929
        evc = EVC(**attributes)
930
931
        evc.current_path = evc.primary_links
932
        evc.remove_current_flows()
933
        notify_mock.assert_called()
934
935
        self.assertEqual(send_flow_mods_mocked.call_count, 5)
936
        self.assertFalse(evc.is_active())
937
        flows = [
938
            {"cookie": evc.get_cookie(), "cookie_mask": 18446744073709551615}
939
        ]
940
        switch_1 = evc.primary_links[0].endpoint_a.switch
941
        switch_2 = evc.primary_links[0].endpoint_b.switch
942
        send_flow_mods_mocked.assert_any_call(switch_1.id, flows, 'delete',
943
                                              force=True)
944
        send_flow_mods_mocked.assert_any_call(switch_2.id, flows, 'delete',
945
                                              force=True)
946
947
        send_flow_mods_mocked.side_effect = FlowModException("error")
948
        evc.remove_current_flows()
949
        log_error_mock.assert_called()
950
951
    @staticmethod
952
    def create_evc_intra_switch():
953
        """Create intra-switch EVC."""
954
        switch = Mock(spec=Switch)
955
        switch.dpid = 2
956
        switch.id = switch.dpid
957
        interface_a = Interface("eth0", 1, switch)
958
        interface_z = Interface("eth1", 3, switch)
959
        uni_a = get_uni_mocked(
960
            tag_value=82,
961
            is_valid=True,
962
        )
963
        uni_z = get_uni_mocked(
964
            tag_value=84,
965
            is_valid=True,
966
        )
967
        uni_a.interface = interface_a
968
        uni_z.interface = interface_z
969
        attributes = {
970
            "controller": get_controller_mock(),
971
            "name": "custom_name",
972
            "id": "1",
973
            "uni_a": uni_a,
974
            "uni_z": uni_z,
975
            "enabled": True,
976
        }
977
        return EVC(**attributes)
978
979
    @staticmethod
980
    @patch("napps.kytos.mef_eline.models.evc.EVC._send_flow_mods")
981
    def test_deploy_direct_uni_flows(send_flow_mods_mock):
982
        """Test _install_direct_uni_flows."""
983
        evc = TestEVC.create_evc_intra_switch()
984
985
        # Test 1: both UNIs with TAG
986
        expected_dpid = evc.uni_a.interface.switch.id
987
        expected_flows = [
988
            {
989
                "match": {"in_port": 1, "dl_vlan": 82},
990
                "cookie": evc.get_cookie(),
991
                "actions": [
992
                    {"action_type": "set_vlan", "vlan_id": 84},
993
                    {"action_type": "output", "port": 3},
994
                ]
995
            },
996
            {
997
                "match": {"in_port": 3, "dl_vlan": 84},
998
                "cookie": evc.get_cookie(),
999
                "actions": [
1000
                    {"action_type": "set_vlan", "vlan_id": 82},
1001
                    {"action_type": "output", "port": 1},
1002
                ]
1003
            }
1004
        ]
1005
1006
        # pylint: disable=protected-access
1007
        evc._install_direct_uni_flows()
1008
        send_flow_mods_mock.assert_called_once_with(
1009
            expected_dpid, expected_flows
1010
        )
1011
1012
        # Test2: no TAG in UNI_A
1013
        uni_a_tag = evc.uni_a.user_tag
1014
        evc.uni_a.user_tag = None
1015
        expected_flows_no_tag_a = [
1016
            {
1017
                "match": {"in_port": 1},
1018
                "cookie": evc.get_cookie(),
1019
                "actions": [
1020
                    {"action_type": "set_vlan", "vlan_id": 84},
1021
                    {"action_type": "output", "port": 3},
1022
                ]
1023
            },
1024
            {
1025
                "match": {"in_port": 3, "dl_vlan": 84},
1026
                "cookie": evc.get_cookie(),
1027
                "actions": [
1028
                    {"action_type": "pop_vlan"},
1029
                    {"action_type": "output", "port": 1},
1030
                ]
1031
            }
1032
        ]
1033
        evc._install_direct_uni_flows()
1034
        send_flow_mods_mock.assert_called_with(
1035
            expected_dpid, expected_flows_no_tag_a
1036
        )
1037
        evc.uni_a.user_tag = uni_a_tag
1038
1039
        # Test3: no TAG in UNI_Z
1040
        uni_z_tag = evc.uni_z.user_tag
1041
        evc.uni_z.user_tag = None
1042
        expected_flows_no_tag_z = [
1043
            {
1044
                "match": {"in_port": 1, "dl_vlan": 82},
1045
                "cookie": evc.get_cookie(),
1046
                "actions": [
1047
                    {"action_type": "pop_vlan"},
1048
                    {"action_type": "output", "port": 3},
1049
                ]
1050
            },
1051
            {
1052
                "match": {"in_port": 3},
1053
                "cookie": evc.get_cookie(),
1054
                "actions": [
1055
                    {"action_type": "set_vlan", "vlan_id": 82},
1056
                    {"action_type": "output", "port": 1},
1057
                ]
1058
            }
1059
        ]
1060
        evc._install_direct_uni_flows()
1061
        send_flow_mods_mock.assert_called_with(
1062
            expected_dpid, expected_flows_no_tag_z
1063
        )
1064
        evc.uni_z.user_tag = uni_z_tag
1065
1066
        # Test3: no TAG in both UNI_Z and UNI_Z
1067
        evc.uni_a.user_tag = None
1068
        evc.uni_z.user_tag = None
1069
        expected_flows_no_tag = [
1070
            {
1071
                "match": {"in_port": 1},
1072
                "cookie": evc.get_cookie(),
1073
                "actions": [{"action_type": "output", "port": 3}]
1074
            },
1075
            {
1076
                "match": {"in_port": 3},
1077
                "cookie": evc.get_cookie(),
1078
                "actions": [{"action_type": "output", "port": 1}]
1079
            }
1080
        ]
1081
        evc._install_direct_uni_flows()
1082
        send_flow_mods_mock.assert_called_with(
1083
            expected_dpid, expected_flows_no_tag
1084
        )
1085
#
1086
#        print(evc._prepare_direct_uni_flows())
1087
#        evc.uni_a.user_tag = uni_a_tag
1088
#        uni_z_tag = evc.uni_z.user_tag
1089
#        evc.uni_z.user_tag = None
1090
#        print(evc._prepare_direct_uni_flows())
1091
#        evc.uni_z.user_tag = uni_z_tag
1092
#        evc.uni_a.user_tag = None
1093
#        evc.uni_z.user_tag = None
1094
#        print(evc._prepare_direct_uni_flows())
1095
#        self.assertTrue(False)
1096
1097
    def test_is_affected_by_link(self):
1098
        """Test is_affected_by_link method"""
1099
        self.evc_deploy.current_path = Path(['a', 'b', 'c'])
1100
        self.assertTrue(self.evc_deploy.is_affected_by_link('b'))
1101
1102
    def test_is_backup_path_affected_by_link(self):
1103
        """Test is_backup_path_affected_by_link method"""
1104
        self.evc_deploy.backup_path = Path(['a', 'b', 'c'])
1105
        self.assertFalse(self.evc_deploy.is_backup_path_affected_by_link('d'))
1106
1107
    def test_is_primary_path_affected_by_link(self):
1108
        """Test is_primary_path_affected_by_link method"""
1109
        self.evc_deploy.primary_path = Path(['a', 'b', 'c'])
1110
        self.assertTrue(self.evc_deploy.is_primary_path_affected_by_link('c'))
1111
1112
    def test_is_using_primary_path(self):
1113
        """Test is_using_primary_path method"""
1114
        self.evc_deploy.primary_path = Path(['a', 'b', 'c'])
1115
        self.evc_deploy.current_path = Path(['e', 'f', 'g'])
1116
        self.assertFalse(self.evc_deploy.is_using_primary_path())
1117
1118
    def test_is_using_backup_path(self):
1119
        """Test is_using_backup_path method"""
1120
        self.evc_deploy.backup_path = Path(['a', 'b', 'c'])
1121
        self.evc_deploy.current_path = Path(['e', 'f', 'g'])
1122
        self.assertFalse(self.evc_deploy.is_using_backup_path())
1123
1124
    @patch('napps.kytos.mef_eline.models.path.Path.status')
1125
    def test_is_using_dynamic_path(self, mock_status):
1126
        """Test is_using_dynamic_path method"""
1127
        mock_status.return_value = False
1128
        self.evc_deploy.backup_path = Path([])
1129
        self.evc_deploy.primary_path = Path([])
1130
        self.assertFalse(self.evc_deploy.is_using_dynamic_path())
1131
1132
    def test_get_path_status(self):
1133
        """Test get_path_status method"""
1134
        path = Path([])
1135
        self.assertEqual(
1136
            self.evc_deploy.get_path_status(path),
1137
            EntityStatus.DISABLED
1138
        )
1139
        path = Path([
1140
            get_link_mocked(status=EntityStatus.UP),
1141
            get_link_mocked(status=EntityStatus.DOWN)
1142
        ])
1143
        self.assertEqual(
1144
            self.evc_deploy.get_path_status(path),
1145
            EntityStatus.DOWN
1146
        )
1147
        path = Path([
1148
            get_link_mocked(status=EntityStatus.UP),
1149
            get_link_mocked(status=EntityStatus.UP)
1150
        ])
1151
        self.assertEqual(
1152
            self.evc_deploy.get_path_status(path),
1153
            EntityStatus.UP
1154
        )
1155