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

build.tests.unit.models.test_evc_deploy   F

Complexity

Total Complexity 74

Size/Duplication

Total Lines 1519
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 1107
dl 0
loc 1519
rs 1.652
c 0
b 0
f 0
wmc 74

44 Methods

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

How to fix   Complexity   

Complexity

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

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

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