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

Handle connection deletions in PCE #225

Merged
merged 36 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6ad7aa3
Update delete connection handler
sajith Feb 7, 2024
6b5a6ea
Merge remote-tracking branch 'origin/main' into 224.delete-connection
sajith Mar 6, 2024
f241013
Fix typo
sajith Mar 6, 2024
c53829d
Enable testing with MQ
sajith Mar 7, 2024
c8f2e8c
Merge remote-tracking branch 'origin/main' into 224.delete-connection
sajith Mar 14, 2024
af94ca4
Remove unused MQ_PORT from tox.ini
sajith Mar 15, 2024
cfd1041
Update comment
sajith Mar 15, 2024
406a719
Merge remote-tracking branch 'origin/main' into 224.delete-connection
sajith Mar 18, 2024
925cac2
Use password authentication with RabbitMQ
sajith Feb 7, 2024
ee7ff86
Use os.getenv()
sajith Feb 7, 2024
b98efbd
Add MQ_USER and MQ_PASS to sample env file
sajith Feb 7, 2024
a91d669
Add MQ_USER and MQ_PASS to CI workflow
sajith Feb 7, 2024
935a65d
Use os.getenv()
sajith Feb 7, 2024
2fafd37
additional changes to allow RabbitMQ authentication
Mar 27, 2024
e388a00
Return error from delete_connection()
sajith Apr 10, 2024
87b19fb
Generate a request ID in place_connection()
sajith Apr 10, 2024
087b188
Rename a test method for clarity
sajith Apr 10, 2024
98c532b
Add a more elaborate test for connection deletions
sajith Apr 10, 2024
09b3050
Accept UUID shaped strings as parameters for DELETE /connection
sajith Apr 10, 2024
3146fb8
Merge remote-tracking branch 'origin/main' into 224.delete-connection
sajith Apr 10, 2024
9c4ad60
Describe successful DELETE /connection
sajith Apr 11, 2024
0049500
Make GET /connection accept UUIDs
sajith Apr 11, 2024
e656494
Call it connection_id, not request_id
sajith Apr 11, 2024
7b77fcc
Rename result variable
sajith Apr 11, 2024
f81a4a7
Add a TODO item
sajith Apr 11, 2024
e3af9aa
Respond with connection failures
sajith Apr 11, 2024
7b31f72
Add a TODO
sajith Apr 11, 2024
f09e47c
Assign an issue to TODO comment
sajith Apr 11, 2024
b926c16
Merge branch 'main' into 224.delete-connection
sajith Apr 15, 2024
905df85
Merge remote-tracking branch 'origin/main' into 224.delete-connection
sajith Apr 18, 2024
54fdfcb
Remove minimum/maximum for connection IDs
sajith Apr 18, 2024
6a9baa3
Remove UUID requirement for connection IDs
sajith Apr 18, 2024
c795809
Add a test for a request that has no ID field
sajith Apr 18, 2024
0cbc02d
Avoid generating a connection ID
sajith Apr 18, 2024
407b8b9
Merge remote-tracking branch 'origin/main' into 224.delete-connection
sajith Apr 19, 2024
b381795
Restore UUID requirement
sajith Apr 19, 2024
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
61 changes: 50 additions & 11 deletions sdx_controller/controllers/connection_controller.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import json
import logging
import uuid

import connexion
from flask import current_app
Expand Down Expand Up @@ -32,7 +31,30 @@ def delete_connection(connection_id):

:rtype: None
"""
return "do some magic!"
logger.info(
f"Handling delete (connecton id: {connection_id}) "
f"with te_manager: {current_app.te_manager}"
)

# # Looking up by UUID do not seem work yet. Will address in
# # https://github.com/atlanticwave-sdx/sdx-controller/issues/252.
#
# value = db_instance.read_from_db(f"{connection_id}")
# print(f"value: {value}")
# if not value:
# return "Not found", 404

try:
# TODO: pce's unreserve_vlan() method silently returns even if the
# connection_id is not found. This should in fact be an error.
#
# https://github.com/atlanticwave-sdx/pce/issues/180
current_app.te_manager.unreserve_vlan(connection_id)
sajith marked this conversation as resolved.
Show resolved Hide resolved
except Exception as e:
logger.info(f"Delete failed (connection id: {connection_id}): {e}")
return "Failed, reason: {e}", 500

return "OK", 200


def getconnection_by_id(connection_id):
Expand Down Expand Up @@ -66,18 +88,35 @@ def place_connection(body):

logger.info("Placing connection. Saving to database.")

if "id" in body:
connection_id = body["id"]
else:
connection_id = uuid.uuid4()
body["id"] = connection_id
connection_id = body["id"]

db_instance.add_key_value_pair_to_db(connection_id, json.dumps(body))
logger.info("Saving to database complete.")

logger.info(f"Handling request with te_manager: {current_app.te_manager}")
logger.info(
f"Handling request {connection_id} with te_manager: {current_app.te_manager}"
)

reason, code = connection_handler.place_connection(current_app.te_manager, body)
logger.info(f"place_connection result: reason='{reason}', code={code}")

return reason, code
logger.info(
f"place_connection result: ID: {connection_id} reason='{reason}', code={code}"
)

response = {
"connection_id": connection_id,
"status": "OK" if code == 200 else "Failure",
"reason": reason,
}

# # TODO: our response is supposed to be shaped just like request
# # ('#/components/schemas/connection'), and in that case the below
# # code would be a quick implementation.
# #
# # https://github.com/atlanticwave-sdx/sdx-controller/issues/251
# response = body

# response["id"] = connection_id
# response["status"] = "success" if code == 200 else "failure"
# response["reason"] = reason # `reason` is not present in schema though.

return response, code
13 changes: 6 additions & 7 deletions sdx_controller/swagger/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,8 @@ paths:
style: simple
explode: false
schema:
maximum: 10
minimum: 1
type: integer
format: int64
type: string
format: uuid
sajith marked this conversation as resolved.
Show resolved Hide resolved
responses:
"200":
description: successful operation
Expand Down Expand Up @@ -205,10 +203,11 @@ paths:
style: simple
explode: false
schema:
minimum: 1
type: integer
format: int64
type: string
format: uuid
responses:
"200":
description: successful operation
"400":
description: Invalid ID supplied
"404":
Expand Down
90 changes: 87 additions & 3 deletions sdx_controller/test/test_connection_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
class TestConnectionController(BaseTestCase):
"""ConnectionController integration test stubs"""

def test_delete_connection(self):
def test_delete_connection_no_setup(self):
"""
Test case for delete_connection.
Test case for delete_connection().

Delete connection order by ID.
"""
Expand All @@ -28,6 +28,50 @@ def test_delete_connection(self):
)
self.assert200(response, f"Response body is : {response.data.decode('utf-8')}")

def test_delete_connection_with_setup(self):
"""
Test case for delete_connection()

Set up a connection request, get the connection ID from the
response, and then do `DELETE /connection/:connection_id`
"""
# set up temanager connection first
for idx, topology_file in enumerate(
[
TestData.TOPOLOGY_FILE_AMLIGHT,
TestData.TOPOLOGY_FILE_SAX,
TestData.TOPOLOGY_FILE_ZAOXI,
]
):
topology = json.loads(topology_file.read_text())
self.te_manager.add_topology(topology)

request_body = TestData.CONNECTION_REQ.read_text()

connection_response = self.client.open(
f"{BASE_PATH}/connection",
method="POST",
data=request_body,
content_type="application/json",
)

print(f"Response body: {connection_response.data.decode('utf-8')}")

self.assertStatus(connection_response, 200)

connection_id = connection_response.get_json().get("connection_id")
print(f"Deleting request_id: {connection_id}")

delete_response = self.client.open(
f"{BASE_PATH}/connection/{connection_id}",
method="DELETE",
)

self.assert200(
delete_response,
f"Response body is : {delete_response.data.decode('utf-8')}",
)

def test_getconnection_by_id(self):
"""
Test case for getconnection_by_id.
Expand Down Expand Up @@ -106,6 +150,44 @@ def test_place_connection_with_zaoxi(self):
"""
self.__test_with_one_topology(TestData.TOPOLOGY_FILE_ZAOXI)

def test_place_connection_no_id(self):
"""
Test place_connection() with a request that has no ID field.
"""
# Remove ID
request = json.loads(TestData.CONNECTION_REQ.read_text())
request.pop("id")
request = json.dumps(request)

print(f"request: {request} {type(request)}")

response = self.client.open(
f"{BASE_PATH}/connection",
method="POST",
data=request,
content_type="application/json",
)

print(f"response: {response}")
print(f"Response body is : {response.data.decode('utf-8')}")

# Expect a 400 response because the required ID field is
# missing from the request.
self.assertStatus(response, 400)

# JSON response should have a body like:
#
# {
# "detail": "'id' is a required property",
# "status": 400,
# "title": "Bad Request",
# "type": "about:blank"
# }

response = response.get_json()
self.assertEqual(response["status"], 400)
self.assertEqual(response["detail"], "'id' is a required property")

def test_place_connection_with_three_topologies(self):
"""
Test case for place_connection.
Expand Down Expand Up @@ -139,7 +221,9 @@ def test_place_connection_with_three_topologies_added_in_sequence(self):
"""
Test case for place_connection.

Place the same connection request while adding topologies.
Keep placing the same connection request while adding
topologies. The first few requests should fail, and the final
one eventually succeed.
"""
for idx, topology_file in enumerate(
[
Expand Down