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

build.tests.unit.models.test_evc_deploy   F

Complexity

Total Complexity 73

Size/Duplication

Total Lines 1593
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 1152
dl 0
loc 1593
rs 1.552
c 0
b 0
f 0
wmc 73

45 Methods

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