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

feature: Allow creation for "any" and "untagged" as EVCs #258

Merged
merged 36 commits into from
Mar 14, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
1080ed5
Removed required property from UNI tag
Alopalao Feb 2, 2023
d6772b3
Added support to str in tag.value
Alopalao Feb 7, 2023
fc66391
Merge branch 'master' into untagged_uni
Alopalao Feb 7, 2023
165c431
Extended changes to more flow types
Alopalao Feb 8, 2023
0f2aa8c
Added new static method _get_value_from_uni_tag()
Alopalao Feb 8, 2023
a5369f0
Fixed linter
Alopalao Feb 8, 2023
eee89e8
Reversed tox.ini
Alopalao Feb 8, 2023
31e0cb1
Added support for intra-switch EVC
Alopalao Feb 9, 2023
eac6bf0
Added more possible strings to `value` from TagDoc
Alopalao Feb 15, 2023
4cadff0
Added support for `untagged`
Alopalao Feb 21, 2023
6f82d45
Corrected EVC combinations
Alopalao Mar 1, 2023
8115423
Fixed linter
Alopalao Mar 1, 2023
bb7fceb
Removed required property from UNI tag
Alopalao Feb 2, 2023
4c768cc
Added support to str in tag.value
Alopalao Feb 7, 2023
81d5ae6
Extended changes to more flow types
Alopalao Feb 8, 2023
6d91bd7
Added new static method _get_value_from_uni_tag()
Alopalao Feb 8, 2023
60626a3
Fixed linter
Alopalao Feb 8, 2023
82a9869
Reversed tox.ini
Alopalao Feb 8, 2023
ebd6dcc
Added support for intra-switch EVC
Alopalao Feb 9, 2023
b4bbe20
Added more possible strings to `value` from TagDoc
Alopalao Feb 15, 2023
9bace29
Added support for `untagged`
Alopalao Feb 21, 2023
df23b06
Corrected EVC combinations
Alopalao Mar 1, 2023
ab731a4
Fixed linter
Alopalao Mar 1, 2023
e3b994b
Merge branch 'untagged_uni' of https://github.com/Alopalao/mef_eline …
Alopalao Mar 1, 2023
72c25d9
Added support to str in tag.value
Alopalao Feb 7, 2023
d877469
Added new static method _get_value_from_uni_tag()
Alopalao Feb 8, 2023
17acb03
Added more possible strings to `value` from TagDoc
Alopalao Feb 15, 2023
4c9ffc9
Added support for `untagged`
Alopalao Feb 21, 2023
4a407e3
Corrected EVC combinations
Alopalao Mar 1, 2023
e976108
Merge branch 'untagged_uni' of https://github.com/Alopalao/mef_eline …
Alopalao Mar 6, 2023
59258b8
Reduced if statements
Alopalao Mar 6, 2023
ad2e558
Changed priorities depending on new cases
Alopalao Mar 7, 2023
b5e4fbe
Reversed string vlan, temporal
Alopalao Mar 7, 2023
aa791e6
Polished code
Alopalao Mar 10, 2023
128f80c
Added new dictionary to EVC, `special_cases`
Alopalao Mar 10, 2023
5c98c93
Added exceptio handling in update()
Alopalao Mar 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions controllers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import Dict, Optional

import pymongo
from pydantic import ValidationError
from pymongo.collection import ReturnDocument
from pymongo.errors import AutoReconnect
from tenacity import retry_if_exception_type, stop_after_attempt, wait_random
Expand Down Expand Up @@ -71,12 +72,16 @@ def get_circuit(self, circuit_id: str) -> Optional[Dict]:
def upsert_evc(self, evc: Dict) -> Optional[Dict]:
"""Update or insert an EVC"""
utc_now = datetime.utcnow()
model = EVCBaseDoc(
**{
**evc,
**{"_id": evc["id"]}
}
)
try:
model = EVCBaseDoc(
**{
**evc,
**{"_id": evc["id"]}
}
)
except ValidationError as err:
viniarck marked this conversation as resolved.
Show resolved Hide resolved
raise err

updated = self.db.evcs.find_one_and_update(
{"_id": evc["id"]},
{
Expand Down
16 changes: 13 additions & 3 deletions db/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
# pylint: disable=no-self-argument,no-name-in-module

from datetime import datetime
from typing import Dict, List, Literal, Optional
from typing import Dict, List, Literal, Optional, Union

from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, validator


class DocumentBaseModel(BaseModel):
Expand Down Expand Up @@ -38,7 +38,17 @@ class CircuitScheduleDoc(BaseModel):
class TAGDoc(BaseModel):
"""TAG model"""
tag_type: int
value: int
value: Union[int, str]

@validator('value')
def validate_value(cls, value):
"""Validate value when is a string"""
if isinstance(value, int):
return value
if isinstance(value, str) and value in ("any", "untagged"):
return value
raise ValueError("value as string allows 'any', 'untagged' and the ",
"format 'n/n'")
viniarck marked this conversation as resolved.
Show resolved Hide resolved


class UNIDoc(BaseModel):
Expand Down
10 changes: 7 additions & 3 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from threading import Lock

from flask import jsonify, request
from pydantic import ValidationError
from werkzeug.exceptions import (BadRequest, Conflict, Forbidden,
MethodNotAllowed, NotFound,
UnsupportedMediaType)
Expand Down Expand Up @@ -218,12 +219,15 @@ def create_circuit(self, data):
except ValueError as exception:
raise BadRequest(str(exception)) from exception

# save circuit
try:
evc.sync()
except ValidationError as exception:
viniarck marked this conversation as resolved.
Show resolved Hide resolved
raise BadRequest(str(exception)) from exception

# store circuit in dictionary
self.circuits[evc.id] = evc

# save circuit
evc.sync()

# Schedule the circuit deploy
self.sched.add(evc)

Expand Down
80 changes: 50 additions & 30 deletions models/evc.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,10 @@ def _validate(self, **kwargs):
raise ValueError(f"{attribute} is an invalid UNI.")

if not uni.is_valid():
tag = uni.user_tag.value
try:
tag = uni.user_tag.value
except AttributeError:
tag = None
message = f"VLAN tag {tag} is not available in {attribute}"
raise ValueError(message)

Expand Down Expand Up @@ -811,8 +814,8 @@ def get_failover_flows(self):

def _prepare_direct_uni_flows(self):
"""Prepare flows connecting two UNIs for intra-switch EVC."""
vlan_a = self.uni_a.user_tag.value if self.uni_a.user_tag else None
vlan_z = self.uni_z.user_tag.value if self.uni_z.user_tag else None
vlan_a = self._get_value_from_uni_tag(self.uni_a)
vlan_z = self._get_value_from_uni_tag(self.uni_z)

is_EVPL = (vlan_a is not None)
flow_mod_az = self._prepare_flow_mod(
Expand All @@ -825,27 +828,31 @@ def _prepare_direct_uni_flows(self):
self.queue_id, is_EVPL
)

if vlan_a and vlan_z:
if vlan_a is not None and vlan_z is not None:
flow_mod_az["match"]["dl_vlan"] = vlan_a
flow_mod_za["match"]["dl_vlan"] = vlan_z
flow_mod_az["actions"].insert(
0, {"action_type": "set_vlan", "vlan_id": vlan_z}
)
flow_mod_za["actions"].insert(
0, {"action_type": "set_vlan", "vlan_id": vlan_a}
)
elif vlan_a:
if vlan_z not in ("4096/4096", 0):
flow_mod_az["actions"].insert(
0, {"action_type": "set_vlan", "vlan_id": vlan_z}
)
if vlan_a not in ("4096/4096", 0):
flow_mod_za["actions"].insert(
0, {"action_type": "set_vlan", "vlan_id": vlan_a}
)
elif vlan_a is not None:
flow_mod_az["match"]["dl_vlan"] = vlan_a
flow_mod_az["actions"].insert(0, {"action_type": "pop_vlan"})
flow_mod_za["actions"].insert(
0, {"action_type": "set_vlan", "vlan_id": vlan_a}
)
elif vlan_z:
if vlan_a not in ("4096/4096", 0):
flow_mod_za["actions"].insert(
0, {"action_type": "set_vlan", "vlan_id": vlan_a}
)
elif vlan_z is not None:
flow_mod_za["match"]["dl_vlan"] = vlan_z
flow_mod_za["actions"].insert(0, {"action_type": "pop_vlan"})
flow_mod_az["actions"].insert(
0, {"action_type": "set_vlan", "vlan_id": vlan_z}
)
if vlan_z not in ("4096/4096", 0):
flow_mod_az["actions"].insert(
0, {"action_type": "set_vlan", "vlan_id": vlan_z}
)
return (
self.uni_a.interface.switch.id, [flow_mod_az, flow_mod_za]
)
Expand Down Expand Up @@ -896,6 +903,19 @@ def _install_nni_flows(self, path=None):
for dpid, flows in self._prepare_nni_flows(path).items():
self._send_flow_mods(dpid, flows)

@staticmethod
def _get_value_from_uni_tag(uni):
"""Returns the value from tag. In case of any and untagged
it should return 4096/4096 and 0 respectively"""
special = {"any": "4096/4096", "untagged": 0}

if uni.user_tag:
value = uni.user_tag.value
if isinstance(value, str):
return special.get(value, value)
return value
return None

def _prepare_uni_flows(self, path=None, skip_in=False, skip_out=False):
"""Prepare flows to install UNIs."""
uni_flows = {}
Expand All @@ -904,10 +924,10 @@ def _prepare_uni_flows(self, path=None, skip_in=False, skip_out=False):
return uni_flows

# Determine VLANs
in_vlan_a = self.uni_a.user_tag.value if self.uni_a.user_tag else None
in_vlan_a = self._get_value_from_uni_tag(self.uni_a)
out_vlan_a = path[0].get_metadata("s_vlan").value

in_vlan_z = self.uni_z.user_tag.value if self.uni_z.user_tag else None
in_vlan_z = self._get_value_from_uni_tag(self.uni_z)
out_vlan_z = path[-1].get_metadata("s_vlan").value

# Flows for the first UNI
Expand Down Expand Up @@ -1034,7 +1054,6 @@ def _prepare_nni_flow(self, *args, queue_id=None):
in_interface, out_interface, queue_id
)
flow_mod["match"]["dl_vlan"] = in_vlan

new_action = {"action_type": "set_vlan", "vlan_id": out_vlan}
flow_mod["actions"].insert(0, new_action)

Expand All @@ -1061,27 +1080,28 @@ def _prepare_push_flow(self, *args, queue_id=None):
flow_mod = self._prepare_flow_mod(
in_interface, out_interface, queue_id, is_EVPL
)

# the service tag must be always pushed
new_action = {"action_type": "set_vlan", "vlan_id": out_vlan}
flow_mod["actions"].insert(0, new_action)
if in_vlan not in ("4096/4096", 0):
new_action = {"action_type": "set_vlan", "vlan_id": out_vlan}
flow_mod["actions"].insert(0, new_action)

new_action = {"action_type": "push_vlan", "tag_type": "s"}
flow_mod["actions"].insert(0, new_action)

if in_vlan:
if in_vlan is not None:
# if in_vlan is set, it must be included in the match
flow_mod["match"]["dl_vlan"] = in_vlan
if new_c_vlan:
if new_c_vlan is not None:
# new_in_vlan is set, so an action to set it is necessary
new_action = {"action_type": "set_vlan", "vlan_id": new_c_vlan}
flow_mod["actions"].insert(0, new_action)
if not in_vlan:
if new_c_vlan not in ("4096/4096", 0):
new_action = {"action_type": "set_vlan", "vlan_id": new_c_vlan}
flow_mod["actions"].insert(0, new_action)
if in_vlan is None:
# new_in_vlan is set, but in_vlan is not, so there was no
# vlan set; then it is set now
new_action = {"action_type": "push_vlan", "tag_type": "c"}
flow_mod["actions"].insert(0, new_action)
elif in_vlan:
elif in_vlan is not None:
# in_vlan is set, but new_in_vlan is not, so the existing vlan
# must be removed
new_action = {"action_type": "pop_vlan"}
Expand Down
9 changes: 4 additions & 5 deletions openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -598,16 +598,15 @@ components:

Tag: # Can be referenced via '#/components/schemas/Tag'
type: object
required:
- tag_type
- value
properties:
tag_type:
type: integer
format: int32
value:
type: integer
format: int32
oneOf:
- type: integer
format: int32
- type: string
viniarck marked this conversation as resolved.
Show resolved Hide resolved

CircuitSchedule: # Can be referenced via '#/components/schemas/CircuitSchedule'
type: object
Expand Down
Loading