diff --git a/src/openstack_mcp_server/tools/identity_tools.py b/src/openstack_mcp_server/tools/identity_tools.py index 1010cf6..b639f8a 100644 --- a/src/openstack_mcp_server/tools/identity_tools.py +++ b/src/openstack_mcp_server/tools/identity_tools.py @@ -28,6 +28,7 @@ def register_tools(self, mcp: FastMCP): mcp.tool()(self.get_projects) mcp.tool()(self.get_project) + mcp.tool()(self.create_project) def get_regions(self) -> list[Region]: """ @@ -269,3 +270,41 @@ def get_project(self, name: str) -> Project: domain_id=project.domain_id, parent_id=project.parent_id, ) + + def create_project( + self, + name: str, + description: str | None = None, + is_enabled: bool = True, + domain_id: str | None = None, + parent_id: str | None = None, + ) -> Project: + """ + Create a new 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 created Project object. + """ + conn = get_openstack_conn() + + project = conn.identity.create_project( + name=name, + description=description, + is_enabled=is_enabled, + domain_id=domain_id, + parent_id=parent_id, + ) + + return Project( + id=project.id, + name=project.name, + description=project.description, + is_enabled=project.is_enabled, + domain_id=project.domain_id, + parent_id=project.parent_id, + ) diff --git a/tests/tools/test_identity_tools.py b/tests/tools/test_identity_tools.py index 7245c90..ccb99e7 100644 --- a/tests/tools/test_identity_tools.py +++ b/tests/tools/test_identity_tools.py @@ -853,3 +853,86 @@ def test_get_project_not_found(self, mock_get_openstack_conn_identity): name_or_id="ProjectOne", ignore_missing=False, ) + + def test_create_project_success_with_all_fields( + self, mock_get_openstack_conn_identity + ): + """Test creating 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.create_project() + mock_conn.identity.create_project.return_value = mock_project + + # Test create_project() + identity_tools = self.get_identity_tools() + result = identity_tools.create_project( + 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.create_project.assert_called_once_with( + name="ProjectOne", + description="Project One description", + is_enabled=True, + domain_id="domain1111111111111111111111111", + parent_id="parentproject1111111111111111111", + ) + + def test_create_project_without_all_fields( + self, mock_get_openstack_conn_identity + ): + """Test creating a identity project without all fields.""" + mock_conn = mock_get_openstack_conn_identity + + mock_conn.identity.create_project.side_effect = ( + exceptions.BadRequestException( + "Field required", + ) + ) + + # Test create_project() + identity_tools = self.get_identity_tools() + + with pytest.raises( + exceptions.BadRequestException, + match="Field required", + ): + identity_tools.create_project( + name="ProjectOne", + description="Project One description", + is_enabled=True, + domain_id=None, + parent_id=None, + ) + + # Verify mock calls + mock_conn.identity.create_project.assert_called_once_with( + name="ProjectOne", + description="Project One description", + is_enabled=True, + domain_id=None, + parent_id=None, + )