Skip to content
Merged
47 changes: 47 additions & 0 deletions src/openstack_mcp_server/tools/identity_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def register_tools(self, mcp: FastMCP):
mcp.tool()(self.get_project)
mcp.tool()(self.create_project)
mcp.tool()(self.delete_project)
mcp.tool()(self.update_project)

def get_regions(self) -> list[Region]:
"""
Expand Down Expand Up @@ -319,3 +320,49 @@ def delete_project(self, id: str) -> None:
conn = get_openstack_conn()
conn.identity.delete_project(project=id, ignore_missing=False)
return None

def update_project(
self,
id: str,
name: str | None = None,
description: str | None = None,
is_enabled: bool | None = None,
domain_id: str | None = None,
parent_id: str | None = None,
) -> Project:
"""
Update a project.

:param id: The ID of the project.
:param name: The name of the project.
:param description: The description of the project.
:param is_enabled: Whether the project is enabled.
:param domain_id: The ID of the domain.
:param parent_id: The ID of the parent project.

:return: The updated Project object.
"""
conn = get_openstack_conn()

args = {}
if name is not None:
args["name"] = name
if description is not None:
args["description"] = description
if is_enabled is not None:
args["is_enabled"] = is_enabled
if domain_id is not None:
args["domain_id"] = domain_id
if parent_id is not None:
args["parent_id"] = parent_id

updated_project = conn.identity.update_project(project=id, **args)

return Project(
id=updated_project.id,
name=updated_project.name,
description=updated_project.description,
is_enabled=updated_project.is_enabled,
domain_id=updated_project.domain_id,
parent_id=updated_project.parent_id,
)
70 changes: 70 additions & 0 deletions tests/tools/test_identity_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -983,3 +983,73 @@ def test_delete_project_not_found(self, mock_get_openstack_conn_identity):
project="project1111111111111111111111111",
ignore_missing=False,
)

def test_update_project_success(self, mock_get_openstack_conn_identity):
"""Test updating a identity project successfully."""
mock_conn = mock_get_openstack_conn_identity

# Create mock project object
mock_project = Mock()
mock_project.id = "project1111111111111111111111111"
mock_project.name = "ProjectOne"
mock_project.description = "Project One description"
mock_project.is_enabled = True
mock_project.domain_id = "domain1111111111111111111111111"
mock_project.parent_id = "parentproject1111111111111111111"

# Configure mock project.update_project()
mock_conn.identity.update_project.return_value = mock_project

# Test update_project()
identity_tools = self.get_identity_tools()
result = identity_tools.update_project(
id="project1111111111111111111111111",
name="ProjectOne",
description="Project One description",
is_enabled=True,
domain_id="domain1111111111111111111111111",
parent_id="parentproject1111111111111111111",
)

# Verify results
assert result == Project(
id="project1111111111111111111111111",
name="ProjectOne",
description="Project One description",
is_enabled=True,
domain_id="domain1111111111111111111111111",
parent_id="parentproject1111111111111111111",
)

# Verify mock calls
mock_conn.identity.update_project.assert_called_once_with(
project="project1111111111111111111111111",
name="ProjectOne",
description="Project One description",
is_enabled=True,
domain_id="domain1111111111111111111111111",
parent_id="parentproject1111111111111111111",
)

def test_update_project_empty_id(self, mock_get_openstack_conn_identity):
"""Test updating a identity project with an empty ID."""
mock_conn = mock_get_openstack_conn_identity

# Configure mock to raise BadRequestException
mock_conn.identity.update_project.side_effect = (
exceptions.BadRequestException(
"Field required",
)
)

# Test update_project()
identity_tools = self.get_identity_tools()

with pytest.raises(
exceptions.BadRequestException,
match="Field required",
):
identity_tools.update_project(id="")

# Verify mock calls
mock_conn.identity.update_project.assert_called_once_with(project="")