Skip to content

Commit 289e331

Browse files
committed
svm handling: cleanup SVM and volume when project is deleted
This behaves more or less the same as removal of the UNDERSTACK_SVM tag
1 parent 2965bdf commit 289e331

File tree

4 files changed

+159
-1
lines changed

4 files changed

+159
-1
lines changed

python/understack-workflows/tests/test_keystone_project.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from understack_workflows.oslo_event.keystone_project import KeystoneProjectEvent
1111
from understack_workflows.oslo_event.keystone_project import _keystone_project_tags
1212
from understack_workflows.oslo_event.keystone_project import handle_project_created
13+
from understack_workflows.oslo_event.keystone_project import handle_project_deleted
1314
from understack_workflows.oslo_event.keystone_project import handle_project_updated
1415

1516

@@ -593,3 +594,133 @@ def test_handle_project_updated_output_files_written(
593594
call("/var/run/argo/output.svm_name", "w"),
594595
]
595596
mock_open.assert_has_calls(expected_calls, any_order=True)
597+
598+
599+
class TestHandleProjectDeleted:
600+
"""Test cases for handle_project_deleted function."""
601+
602+
@pytest.fixture
603+
def mock_conn(self):
604+
"""Create a mock OpenStack connection."""
605+
return MagicMock()
606+
607+
@pytest.fixture
608+
def mock_nautobot(self):
609+
"""Create a mock Nautobot instance."""
610+
return MagicMock()
611+
612+
@pytest.fixture
613+
def valid_delete_event_data(self):
614+
"""Create valid delete event data for testing."""
615+
return {
616+
"event_type": "identity.project.deleted",
617+
"payload": {"target": {"id": "test-project-123"}},
618+
}
619+
620+
def test_handle_project_deleted_wrong_event_type(self, mock_conn, mock_nautobot):
621+
"""Test handling event with wrong event type."""
622+
event_data = {
623+
"event_type": "identity.project.created",
624+
"payload": {"target": {"id": "test-project-123"}},
625+
}
626+
627+
result = handle_project_deleted(mock_conn, mock_nautobot, event_data)
628+
assert result == 1
629+
630+
@patch("understack_workflows.oslo_event.keystone_project.NetAppManager")
631+
def test_handle_project_deleted_svm_exists(
632+
self,
633+
mock_netapp_class,
634+
mock_conn,
635+
mock_nautobot,
636+
valid_delete_event_data,
637+
):
638+
"""Test project deletion when SVM exists."""
639+
mock_netapp_manager = MagicMock()
640+
mock_netapp_manager.check_if_svm_exists.return_value = True
641+
mock_netapp_class.return_value = mock_netapp_manager
642+
643+
result = handle_project_deleted(
644+
mock_conn, mock_nautobot, valid_delete_event_data
645+
)
646+
647+
assert result == 0
648+
mock_netapp_manager.check_if_svm_exists.assert_called_once_with(
649+
project_id="test-project-123"
650+
)
651+
mock_netapp_manager.cleanup_project.assert_called_once_with("test-project-123")
652+
653+
@patch("understack_workflows.oslo_event.keystone_project.NetAppManager")
654+
def test_handle_project_deleted_svm_does_not_exist(
655+
self,
656+
mock_netapp_class,
657+
mock_conn,
658+
mock_nautobot,
659+
valid_delete_event_data,
660+
):
661+
"""Test project deletion when SVM does not exist."""
662+
mock_netapp_manager = MagicMock()
663+
mock_netapp_manager.check_if_svm_exists.return_value = False
664+
mock_netapp_class.return_value = mock_netapp_manager
665+
666+
result = handle_project_deleted(
667+
mock_conn, mock_nautobot, valid_delete_event_data
668+
)
669+
670+
assert result == 0
671+
mock_netapp_manager.check_if_svm_exists.assert_called_once_with(
672+
project_id="test-project-123"
673+
)
674+
mock_netapp_manager.cleanup_project.assert_not_called()
675+
676+
@patch("understack_workflows.oslo_event.keystone_project.NetAppManager")
677+
def test_handle_project_deleted_netapp_manager_failure(
678+
self,
679+
mock_netapp_class,
680+
mock_conn,
681+
mock_nautobot,
682+
valid_delete_event_data,
683+
):
684+
"""Test handling when NetAppManager creation fails during deletion."""
685+
mock_netapp_class.side_effect = Exception("NetApp connection failed")
686+
687+
result = handle_project_deleted(
688+
mock_conn, mock_nautobot, valid_delete_event_data
689+
)
690+
691+
assert result == 1 # Should return 1 on exception
692+
mock_netapp_class.assert_called_once()
693+
694+
@patch("understack_workflows.oslo_event.keystone_project.NetAppManager")
695+
def test_handle_project_deleted_cleanup_failure(
696+
self,
697+
mock_netapp_class,
698+
mock_conn,
699+
mock_nautobot,
700+
valid_delete_event_data,
701+
):
702+
"""Test handling when cleanup_project fails during deletion."""
703+
mock_netapp_manager = MagicMock()
704+
mock_netapp_manager.check_if_svm_exists.return_value = True
705+
mock_netapp_manager.cleanup_project.side_effect = Exception("Cleanup failed")
706+
mock_netapp_class.return_value = mock_netapp_manager
707+
708+
result = handle_project_deleted(
709+
mock_conn, mock_nautobot, valid_delete_event_data
710+
)
711+
712+
assert result == 1 # Should return 1 on exception
713+
mock_netapp_manager.check_if_svm_exists.assert_called_once_with(
714+
project_id="test-project-123"
715+
)
716+
mock_netapp_manager.cleanup_project.assert_called_once_with("test-project-123")
717+
718+
def test_handle_project_deleted_invalid_event_data(self, mock_conn, mock_nautobot):
719+
"""Test handling deletion with invalid event data."""
720+
invalid_event_data = {
721+
"event_type": "identity.project.deleted",
722+
"payload": {}, # Missing target
723+
}
724+
725+
with pytest.raises(Exception, match="no target information in payload"):
726+
handle_project_deleted(mock_conn, mock_nautobot, invalid_event_data)

python/understack-workflows/understack_workflows/main/openstack_oslo_event.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class NoEventHandlerError(Exception):
6666
"baremetal.port.delete.end": ironic_port.handle_port_delete,
6767
"identity.project.created": keystone_project.handle_project_created,
6868
"identity.project.updated": keystone_project.handle_project_updated,
69+
"identity.project.deleted": keystone_project.handle_project_deleted,
6970
}
7071

7172

python/understack-workflows/understack_workflows/oslo_event/keystone_project.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,31 @@ def handle_project_updated(
118118
return 0
119119

120120

121+
def handle_project_deleted(
122+
conn: Connection, _nautobot: Nautobot, event_data: dict
123+
) -> int:
124+
if event_data.get("event_type") != "identity.project.deleted":
125+
logger.error("Received event that is not identity.project.deleted")
126+
return 1
127+
128+
event = KeystoneProjectEvent.from_event_dict(event_data)
129+
logger.info("Starting ONTAP SVM and Volume delete workflow.")
130+
try:
131+
netapp_manager = NetAppManager()
132+
svm_exists = netapp_manager.check_if_svm_exists(project_id=event.project_id)
133+
134+
if svm_exists:
135+
logger.info("SVM for project %s exists - cleaning it up.", event.project_id)
136+
netapp_manager.cleanup_project(event.project_id)
137+
else:
138+
logger.info("SVM for project %s did not exist.", event.project_id)
139+
_save_output("svm_created", str(False))
140+
except Exception as e:
141+
logger.error(e)
142+
return 1
143+
return 0
144+
145+
121146
def _create_svm_and_volume(netapp_manager, event) -> str:
122147
svm_name = netapp_manager.create_svm(
123148
project_id=event.project_id, aggregate_name=AGGREGATE_NAME

workflows/openstack/sensors/sensor-keystone-oslo-event.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ metadata:
1010
1111
- identity.project.created
1212
- identity.project.updated
13-
- other events are silently ignored now
13+
- identity.project.deleted
1414
1515
Resulting code should be very similar to:
1616
@@ -39,6 +39,7 @@ spec:
3939
value:
4040
- "identity.project.created"
4141
- "identity.project.updated"
42+
- "identity.project.deleted"
4243
template:
4344
serviceAccountName: sensor-submit-workflow
4445
triggers:

0 commit comments

Comments
 (0)