Test Failed
Pull Request — master (#258)
by
unknown
03:34
created

TestEVC.test_prepare_push_flow()   F

Complexity

Conditions 19

Size

Total Lines 77
Code Lines 64

Duplication

Lines 26
Ratio 33.77 %

Importance

Changes 0
Metric Value
cc 19
eloc 64
nop 1
dl 26
loc 77
rs 0.5999
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like build.tests.unit.models.test_evc_deploy.TestEVC.test_prepare_push_flow() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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