Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added vlan_range support #407

Merged
merged 12 commits into from
Nov 22, 2023
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ Added
- Added a UI button for redeploying an EVC.
- UNI tag_type are now accepted as string.
- EVCs now listen to ``switch.interface.(link_up|link_down|created|deleted)`` events for activation/deactivation
- Circuits with a vlan range are supported now. The ranges follows ``list[list[int]]`` format and both UNIs vlan should have the same ranges.

Changed
=======
- EVCs will try to maintain their current_path on link status changes
- UNIs now will use and free tags from ``Interface.available_tags``.
- UNI tag_type is changed to string from 1, 2 and 3 values to ``"vlan"``, ``"vlan_qinq"`` and ``"mpls"`` respectively.
- Add ``set_vlan`` only if UNI A vlan and UNI z vlan are different.
- Updated ``openapi.yml``, ``Tag`` now can accept ``array`` as ``value``.

Deprecated
==========
Expand Down
5 changes: 4 additions & 1 deletion db/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,14 @@ class CircuitScheduleDoc(BaseModel):
class TAGDoc(BaseModel):
"""TAG model"""
tag_type: str
value: Union[int, str]
value: Union[int, str, list[list[int]]]
mask_list: Optional[list[str, int]]

@validator('value')
def validate_value(cls, value):
"""Validate value when is a string"""
if isinstance(value, list):
return value
if isinstance(value, int):
return value
if isinstance(value, str) and value in ("any", "untagged"):
Expand Down
49 changes: 29 additions & 20 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,22 @@

from kytos.core import KytosNApp, log, rest
from kytos.core.events import KytosEvent
from kytos.core.exceptions import KytosTagError
from kytos.core.helpers import (alisten_to, listen_to, load_spec,
validate_openapi)
from kytos.core.interface import TAG, UNI
from kytos.core.interface import TAG, UNI, TAGRange
from kytos.core.link import Link
from kytos.core.rest_api import (HTTPException, JSONResponse, Request,
get_json_or_400)
from kytos.core.tag_ranges import get_tag_ranges
from napps.kytos.mef_eline import controllers, settings
from napps.kytos.mef_eline.exceptions import DisabledSwitch, InvalidPath
from napps.kytos.mef_eline.models import (EVC, DynamicPathManager, EVCDeploy,
Path)
from napps.kytos.mef_eline.scheduler import CircuitSchedule, Scheduler
from napps.kytos.mef_eline.utils import (aemit_event, check_disabled_component,
emit_event, map_evc_event_content)
emit_event, get_vlan_tags_and_masks,
map_evc_event_content)


# pylint: disable=too-many-public-methods
Expand Down Expand Up @@ -199,6 +202,7 @@ def get_circuit(self, request: Request) -> JSONResponse:
log.debug("get_circuit result %s %s", circuit, status)
return JSONResponse(circuit, status_code=status)

# pylint: disable=too-many-branches, too-many-statements
@rest("/v2/evc/", methods=["POST"])
@validate_openapi(spec)
def create_circuit(self, request: Request) -> JSONResponse:
Expand Down Expand Up @@ -231,10 +235,9 @@ def create_circuit(self, request: Request) -> JSONResponse:

try:
evc = self._evc_from_dict(data)
except ValueError as exception:
except (ValueError, KytosTagError) as exception:
log.debug("create_circuit result %s %s", exception, 400)
raise HTTPException(400, detail=str(exception)) from exception

try:
check_disabled_component(evc.uni_a, evc.uni_z)
except DisabledSwitch as exception:
Expand Down Expand Up @@ -269,20 +272,23 @@ def create_circuit(self, request: Request) -> JSONResponse:
detail=f"backup_path is not valid: {exception}"
) from exception

# verify duplicated evc
if self._is_duplicated_evc(evc):
result = "The EVC already exists."
log.debug("create_circuit result %s %s", result, 409)
raise HTTPException(409, detail=result)

if not evc._tag_lists_equal():
detail = "UNI_A and UNI_Z tag lists should be the same."
raise HTTPException(400, detail=detail)

try:
evc._validate_has_primary_or_dynamic()
except ValueError as exception:
raise HTTPException(400, detail=str(exception)) from exception

try:
self._use_uni_tags(evc)
except ValueError as exception:
except KytosTagError as exception:
raise HTTPException(400, detail=str(exception)) from exception

# save circuit
Expand Down Expand Up @@ -313,14 +319,11 @@ def create_circuit(self, request: Request) -> JSONResponse:
@staticmethod
def _use_uni_tags(evc):
uni_a = evc.uni_a
try:
evc._use_uni_vlan(uni_a)
except ValueError as err:
raise err
evc._use_uni_vlan(uni_a)
try:
uni_z = evc.uni_z
evc._use_uni_vlan(uni_z)
except ValueError as err:
except KytosTagError as err:
evc.make_uni_vlan_available(uni_a)
raise err

Expand Down Expand Up @@ -364,10 +367,7 @@ def update(self, request: Request) -> JSONResponse:
enable, redeploy = evc.update(
**self._evc_dict_with_instances(data)
)
except ValidationError as exception:
raise HTTPException(400, detail=str(exception)) from exception
except ValueError as exception:
log.error(exception)
except (ValueError, KytosTagError, ValidationError) as exception:
log.debug("update result %s %s", exception, 400)
raise HTTPException(400, detail=str(exception)) from exception
except DisabledSwitch as exception:
Expand All @@ -377,6 +377,11 @@ def update(self, request: Request) -> JSONResponse:
detail=f"Path is not valid: {exception}"
) from exception

if self._is_duplicated_evc(evc):
result = "The EVC already exists."
log.debug("create_circuit result %s %s", result, 409)
raise HTTPException(409, detail=result)

if evc.is_active():
if enable is False: # disable if active
with evc.lock:
Expand Down Expand Up @@ -716,7 +721,8 @@ def _is_duplicated_evc(self, evc):

"""
for circuit in tuple(self.circuits.values()):
if not circuit.archived and circuit.shares_uni(evc):
if (not circuit.archived and circuit._id != evc._id
and circuit.shares_uni(evc)):
return True
return False

Expand Down Expand Up @@ -906,12 +912,11 @@ def _load_evc(self, circuit_dict):
"""Load one EVC from mongodb to memory."""
try:
evc = self._evc_from_dict(circuit_dict)
except ValueError as exception:
except (ValueError, KytosTagError) as exception:
log.error(
f"Could not load EVC: dict={circuit_dict} error={exception}"
)
return None

if evc.archived:
return None

Expand Down Expand Up @@ -1001,11 +1006,15 @@ def _uni_from_dict(self, uni_dict):
tag_type = tag_dict.get("tag_type")
tag_type = tag_convert.get(tag_type, tag_type)
tag_value = tag_dict.get("value")
tag = TAG(tag_type, tag_value)
if isinstance(tag_value, list):
tag_value = get_tag_ranges(tag_value)
mask_list = get_vlan_tags_and_masks(tag_value)
tag = TAGRange(tag_type, tag_value, mask_list)
else:
tag = TAG(tag_type, tag_value)
else:
tag = None
uni = UNI(interface, tag)

return uni

def _link_from_dict(self, link_dict):
Expand Down
Loading
Loading