Skip to content

Commit

Permalink
Merge branch 'master' into fix/issue_128
Browse files Browse the repository at this point in the history
  • Loading branch information
viniarck committed Jan 12, 2022
2 parents c3b859a + efdb3ab commit 986e437
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 43 deletions.
53 changes: 24 additions & 29 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,51 +1,46 @@
Overview
========

**WARNING: As previously announced on our communication channels, the Kytos
project will enter the "shutdown" phase on May 31, 2021. After this date,
only critical patches (security and core bug fixes) will be accepted, and the
project will be in "critical-only" mode for another six months (until November
30, 2021). For more information visit the FAQ at <https://kytos.io/faq>. We'll
have eternal gratitude to the entire community of developers and users that made
the project so far.**
This Network Application is part of the Kytos-NG project.

|Experimental| |License| |Build| |Coverage| |Quality|
|Stable| |Tag| |License| |Build| |Coverage| |Quality|


.. attention::

THIS NAPP IS STILL EXPERIMENTAL AND IT'S EVENTS, METHODS AND STRUCTURES MAY
CHANGE A LOT ON THE NEXT FEW DAYS/WEEKS, USE IT AT YOUR OWN DISCERNEMENT

This Napp allows a user to create a point to point L2 Ethernet Virtual Circuit.

When fully implemented, this napp will provide a REST API to create/modify/delete circuits. For now, the list of installed circuits is kept in memory,
but it should be later kept in a permanent storage.
Circuits will be installed at the time of request or at a predefined time, and can also have a time to be deleted.
The Napp also will listen for PortStatus events, modifying circuits that use a port that went down.
Features
========
- REST API to create/modify/delete circuits;
- REST API to create/modify/delete circuit scheduling;
- list of circuits in memory and also synchronized to a permanent storage;
- circuits can be installed at time of request or have an installation schedule;
- circuits can use a predefined path or find a dynamic path;
- the NApp will move circuits to another path in case of a link down;
- web UI for circuits management.

The cookie flow field is used to identify to which EVC the flow belongs. The first two bytes of the cookie is a prefix identifying the NApp using it,
and the remaining 14 bytes are the EVC id.

Requirements
============
- kytos/flow_manager
- kytos/pathfinder
- kytos/topology
- amlight/sndtrace_cp


.. TAGs
.. |Experimental| image:: https://img.shields.io/badge/stability-experimental-orange.svg
:target: https://github.com/kytos/mef_eline
.. |License| image:: https://img.shields.io/github/license/kytos/kytos.svg
:target: https://github.com/kytos/mef_eline/blob/master/LICENSE
.. |Build| image:: https://scrutinizer-ci.com/g/kytos/mef_eline/badges/build.png?b=master
.. |Stable| image:: https://img.shields.io/badge/stability-stable-green.svg
:target: https://github.com/kytos-ng/mef_eline
.. |License| image:: https://img.shields.io/github/license/kytos-ng/kytos.svg
:target: https://github.com/kytos-ng/mef_eline/blob/master/LICENSE
.. |Build| image:: https://scrutinizer-ci.com/g/kytos-ng/mef_eline/badges/build.png?b=master
:alt: Build status
:target: https://scrutinizer-ci.com/g/kytos/kytos/?branch=master
.. |Coverage| image:: https://scrutinizer-ci.com/g/kytos/mef_eline/badges/coverage.png?b=master
:target: https://scrutinizer-ci.com/g/kytos-ng/kytos/?branch=master
.. |Coverage| image:: https://scrutinizer-ci.com/g/kytos-ng/mef_eline/badges/coverage.png?b=master
:alt: Code coverage
:target: https://scrutinizer-ci.com/g/kytos/mef_eline/
.. |Quality| image:: https://scrutinizer-ci.com/g/kytos/mef_eline/badges/quality-score.png?b=master
:target: https://scrutinizer-ci.com/g/kytos-ng/mef_eline/
.. |Quality| image:: https://scrutinizer-ci.com/g/kytos-ng/mef_eline/badges/quality-score.png?b=master
:alt: Code-quality score
:target: https://scrutinizer-ci.com/g/kytos/mef_eline/
:target: https://scrutinizer-ci.com/g/kytos-ng/mef_eline/
.. |Tag| image:: https://img.shields.io/github/tag/kytos-ng/mef_eline.svg
:target: https://github.com/kytos-ng/mef_eline/tags
2 changes: 1 addition & 1 deletion models/evc.py
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ def get_cookie(self):
def get_id_from_cookie(cookie):
"""Return the evc id given a cookie value."""
evc_id = cookie - (settings.COOKIE_PREFIX << 56)
return f"{evc_id:14x}"
return f"{evc_id:x}".zfill(14)

def _prepare_flow_mod(self, in_interface, out_interface, queue_id=None):
"""Prepare a common flow mod."""
Expand Down
18 changes: 18 additions & 0 deletions tests/unit/models/test_evc_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,21 @@ def test_get_id_from_cookie():
evc_id = evc.id
assert evc_id
assert evc.get_id_from_cookie(evc.get_cookie()) == evc_id

@staticmethod
def test_get_id_from_cookie_with_leading_zeros():
"""Test get_id_from_cookie with leading zeros."""

attributes = {
"controller": get_controller_mock(),
"name": "circuit_name",
"enable": True,
"uni_a": get_uni_mocked(is_valid=True),
"uni_z": get_uni_mocked(is_valid=True)
}
evc = EVC(**attributes)
evc_id = "0a2d672d99ff41"
# pylint: disable=protected-access
evc._id = evc_id
# pylint: enable=protected-access
assert EVC.get_id_from_cookie(evc.get_cookie()) == evc_id
24 changes: 16 additions & 8 deletions tests/unit/models/test_link_protection.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ def test_handle_link_down_case_1(

evc.current_path = evc.primary_path
evc.activate()
deploy_to_mocked.reset_mock()
current_handle_link_down = evc.handle_link_down()
self.assertEqual(deploy_mocked.call_count, 0)
deploy_to_mocked.assert_called_once()
Expand Down Expand Up @@ -267,13 +268,13 @@ def test_handle_link_down_case_2(
]
backup_path = [
get_link_mocked(
endpoint_a_port=9,
endpoint_a_port=7,
endpoint_b_port=10,
metadata={"s_vlan": 5},
status=EntityStatus.DOWN,
),
get_link_mocked(
endpoint_a_port=11,
endpoint_a_port=15,
endpoint_b_port=12,
metadata={"s_vlan": 6},
status=EntityStatus.UP,
Expand All @@ -291,6 +292,7 @@ def test_handle_link_down_case_2(

evc = EVC(**attributes)
evc.current_path = evc.backup_path
deploy_to_mocked.reset_mock()
current_handle_link_down = evc.handle_link_down()
self.assertEqual(deploy_mocked.call_count, 0)
deploy_to_mocked.assert_called_once()
Expand Down Expand Up @@ -349,6 +351,7 @@ def test_handle_link_down_case_3(

evc = EVC(**attributes)
evc.current_path = evc.backup_path
deploy_to_mocked.reset_mock()
current_handle_link_down = evc.handle_link_down()

self.assertEqual(get_paths_mocked.call_count, 0)
Expand Down Expand Up @@ -420,6 +423,7 @@ def test_handle_link_down_case_4(
evc._storehouse.box = Mock() # pylint: disable=protected-access
evc._storehouse.box.data = {} # pylint: disable=protected-access

deploy_to_mocked.reset_mock()
current_handle_link_down = evc.handle_link_down()
self.assertEqual(deploy_to_mocked.call_count, 1)

Expand Down Expand Up @@ -450,12 +454,12 @@ def test_handle_link_up_case_1(self, deploy_to_mocked, deploy_mocked):
backup_path = [
get_link_mocked(
endpoint_a_port=9,
endpoint_b_port=10,
endpoint_b_port=14,
metadata={"s_vlan": 5},
status=EntityStatus.UP,
),
get_link_mocked(
endpoint_a_port=11,
endpoint_a_port=15,
endpoint_b_port=12,
metadata={"s_vlan": 6},
status=EntityStatus.UP,
Expand All @@ -474,6 +478,7 @@ def test_handle_link_up_case_1(self, deploy_to_mocked, deploy_mocked):

evc = EVC(**attributes)
evc.current_path = evc.primary_path
deploy_to_mocked.reset_mock()
current_handle_link_up = evc.handle_link_up(backup_path[0])
self.assertEqual(deploy_mocked.call_count, 0)
self.assertEqual(deploy_to_mocked.call_count, 0)
Expand Down Expand Up @@ -503,12 +508,12 @@ def test_handle_link_up_case_2(self, deploy_to_path_mocked, deploy_mocked):
backup_path = [
get_link_mocked(
endpoint_a_port=9,
endpoint_b_port=10,
endpoint_b_port=14,
metadata={"s_vlan": 5},
status=EntityStatus.UP,
),
get_link_mocked(
endpoint_a_port=11,
endpoint_a_port=15,
endpoint_b_port=12,
metadata={"s_vlan": 6},
status=EntityStatus.UP,
Expand All @@ -527,6 +532,7 @@ def test_handle_link_up_case_2(self, deploy_to_path_mocked, deploy_mocked):

evc = EVC(**attributes)
evc.current_path = evc.backup_path
deploy_to_path_mocked.reset_mock()
current_handle_link_up = evc.handle_link_up(primary_path[0])
self.assertEqual(deploy_mocked.call_count, 0)
self.assertEqual(deploy_to_path_mocked.call_count, 1)
Expand Down Expand Up @@ -566,13 +572,13 @@ def test_handle_link_up_case_3(
]
backup_path = [
get_link_mocked(
endpoint_a_port=13,
endpoint_a_port=9,
endpoint_b_port=14,
metadata={"s_vlan": 5},
status=EntityStatus.DOWN,
),
get_link_mocked(
endpoint_a_port=11,
endpoint_a_port=15,
endpoint_b_port=12,
metadata={"s_vlan": 6},
status=EntityStatus.UP,
Expand All @@ -596,6 +602,7 @@ def test_handle_link_up_case_3(
evc._storehouse.box.data = {} # pylint: disable=protected-access

evc.current_path = Path([])
deploy_to_path_mocked.reset_mock()
current_handle_link_up = evc.handle_link_up(backup_path[0])

self.assertEqual(get_best_path_mocked.call_count, 0)
Expand Down Expand Up @@ -672,6 +679,7 @@ def test_handle_link_up_case_4(self, *args):
evc._storehouse.box = Mock() # pylint: disable=protected-access
evc._storehouse.box.data = {} # pylint: disable=protected-access

deploy_to_path_mocked.reset_mock()
current_handle_link_up = evc.handle_link_up(backup_path[0])

self.assertEqual(get_best_path_mocked.call_count, 0)
Expand Down
42 changes: 37 additions & 5 deletions ui/k-info-panel/show_circuit.kytos
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
title="col.name"
:candidates="dpids"
@focus="loadDpidNames"
@blur="onblur_dpid"
v-if="compexists && (col.name=='uni_a' || col.name=='uni_z')">
</k-input-auto>
<template v-else>
Expand Down Expand Up @@ -282,6 +283,26 @@ module.exports = {
_this.$kytos.$emit("setNotification" , notification);
});
},
onblur_dpid: function() {
/**
* Update dpid values on event onblur triggered in dpids fields.
* It split the value selected from autocomplete list.
* It is expected the value as "NAME - DPID".
**/
let dpid_a = this.endpoints_data["DPID"][0]["value"];
if(dpid_a.lastIndexOf(' ') > 0) {
let splitted_dpid = dpid_a.split(' ');
this.endpoints_data["Interface"][0]["value"] = splitted_dpid[0];
this.endpoints_data["DPID"][0]["value"] = splitted_dpid[2];
}

let dpid_z = this.endpoints_data["DPID"][1]["value"];
if(dpid_z.lastIndexOf(' ') > 0) {
let splitted_dpid = dpid_z.split(' ');
this.endpoints_data["Interface"][1]["value"] = splitted_dpid[0];
this.endpoints_data["DPID"][1]["value"] = splitted_dpid[2];
}
},
buidEvcView: function(data) {
/**
* Build new EVC object for exibition.
Expand Down Expand Up @@ -400,17 +421,22 @@ module.exports = {
}
if(sw.interfaces) {
$.each(sw.interfaces, function(j , interface) {
// store autocomplete dpids
if(!_this.dpids.includes(interface.id)) {
_this.dpids.push(interface.id);
}
// store interface name data
// store interface name data
let metadata = interface.metadata;
_interface_data[interface.id] = {
"name": interface.name,
"link_name": (metadata && "link_name" in metadata) ? interface.metadata.link_name : "",
"port_name": (metadata && "port_name" in metadata) ? interface.metadata.port_name : ""
};

// store autocomplete dpids
let value = interface.id;
if(interface.name) {
value = interface.name + " - " + value;
}
if(!_this.dpids.includes(value)) {
_this.dpids.push(value);
}
});
}
});
Expand Down Expand Up @@ -680,4 +706,10 @@ module.exports = {
color: #ffc5c5;
background: #be0000;
}
.autocomplete-result-list {
width: 110% !important;
}
.autocomplete-result-list li {
white-space: nowrap;
}
</style>
13 changes: 13 additions & 0 deletions ui/k-toolbar/main.kytos
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,17 @@ module.exports = {
}
this.$kytos.$emit("showInfoPanel", listConnections);
},
set_default_values() {
this.circuit_name = "";
this.endpoint_a = "";
this.endpoint_name_a = "Endpoint name:";
this.tag_type_a = "";
this.tag_value_a = "";
this.endpoint_z = "";
this.endpoint_name_z = "Endpoint name:";
this.tag_type_z = "";
this.tag_value_z = "";
},
post_success(data) {
let notification = {
icon: 'gear',
Expand All @@ -103,6 +114,8 @@ module.exports = {
}

this.$kytos.$emit("setNotification" , notification);
// Clear fields if the POST is a success
this.set_default_values();
},
post_error(data) {
let notification = {
Expand Down

0 comments on commit 986e437

Please sign in to comment.