Skip to content

Commit 5ba3b50

Browse files
S0okJuhalucinor
authored andcommitted
feat: Add get-domain, get-domains tool (#40)
* feat(identity): Add get-domain, get-domains(#31) - Change response/keystone.py -> response/identity - Add get-domain, get-domains tool * feat(identity): Register tool(#31)
1 parent da41f58 commit 5ba3b50

File tree

3 files changed

+135
-2
lines changed

3 files changed

+135
-2
lines changed

src/openstack_mcp_server/tools/identity_tools.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from fastmcp import FastMCP
22

33
from .base import get_openstack_conn
4-
from .response.keystone import Region
4+
from .response.identity import Region, Domain
55

66

77
class IdentityTools:
@@ -19,6 +19,9 @@ def register_tools(self, mcp: FastMCP):
1919
mcp.tool()(self.create_region)
2020
mcp.tool()(self.delete_region)
2121
mcp.tool()(self.update_region)
22+
23+
mcp.tool()(self.get_domains)
24+
mcp.tool()(self.get_domain)
2225

2326
def get_regions(self) -> list[Region]:
2427
"""
@@ -100,3 +103,33 @@ def update_region(self, id: str, description: str = "") -> Region:
100103
id=updated_region.id,
101104
description=updated_region.description,
102105
)
106+
107+
def get_domains(self) -> list[Domain]:
108+
"""
109+
Get the list of Identity domains.
110+
111+
:return: A list of Domain objects representing the domains.
112+
"""
113+
conn = get_openstack_conn()
114+
115+
domain_list = []
116+
for domain in conn.identity.domains():
117+
domain_list.append(
118+
Domain(id=domain.id, name=domain.name, description=domain.description, is_enabled=domain.is_enabled),
119+
)
120+
return domain_list
121+
122+
def get_domain(self, id: str) -> Domain:
123+
"""
124+
Get a domain.
125+
126+
:param id: The ID of the domain. (required)
127+
128+
:return: The Domain object.
129+
"""
130+
conn = get_openstack_conn()
131+
132+
domain = conn.identity.get_domain(domain=id)
133+
134+
return Domain(id=domain.id, name=domain.name, description=domain.description, is_enabled=domain.is_enabled)
135+

src/openstack_mcp_server/tools/response/keystone.py renamed to src/openstack_mcp_server/tools/response/identity.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,9 @@
66
class Region(BaseModel):
77
id: str
88
description: str = ""
9+
10+
class Domain(BaseModel):
11+
id: str
12+
name: str
13+
description: str = ""
14+
is_enabled: bool = False

tests/tools/test_identity_tools.py

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
from openstack import exceptions
66

7-
from openstack_mcp_server.tools.identity_tools import IdentityTools, Region
7+
from openstack_mcp_server.tools.identity_tools import IdentityTools
8+
from openstack_mcp_server.tools.response.identity import Region, Domain
89

910

1011
class TestIdentityTools:
@@ -334,3 +335,96 @@ def test_get_region_not_found(self, mock_get_openstack_conn_identity):
334335
mock_conn.identity.get_region.assert_called_once_with(
335336
region="RegionOne",
336337
)
338+
339+
def test_get_domains_success(self, mock_get_openstack_conn_identity):
340+
"""Test getting identity domains successfully."""
341+
mock_conn = mock_get_openstack_conn_identity
342+
343+
# Create mock domain objects
344+
mock_domain1 = Mock()
345+
mock_domain1.id = "domainone"
346+
mock_domain1.name = "DomainOne"
347+
mock_domain1.description = "Domain One description"
348+
mock_domain1.is_enabled = True
349+
350+
mock_domain2 = Mock()
351+
mock_domain2.id = "domaintwo"
352+
mock_domain2.name = "DomainTwo"
353+
mock_domain2.description = "Domain Two description"
354+
mock_domain2.is_enabled = False
355+
356+
# Configure mock domain.domains()
357+
mock_conn.identity.domains.return_value = [mock_domain1, mock_domain2]
358+
359+
# Test get_domains()
360+
identity_tools = self.get_identity_tools()
361+
result = identity_tools.get_domains()
362+
363+
# Verify results
364+
assert result == [
365+
Domain(id="domainone", name="DomainOne", description="Domain One description", is_enabled=True),
366+
Domain(id="domaintwo", name="DomainTwo", description="Domain Two description", is_enabled=False),
367+
]
368+
369+
# Verify mock calls
370+
mock_conn.identity.domains.assert_called_once()
371+
372+
def test_get_domains_empty_list(self, mock_get_openstack_conn_identity):
373+
"""Test getting identity domains when there are no domains."""
374+
mock_conn = mock_get_openstack_conn_identity
375+
376+
# Empty domain list
377+
mock_conn.identity.domains.return_value = []
378+
379+
# Test get_domains()
380+
identity_tools = self.get_identity_tools()
381+
result = identity_tools.get_domains()
382+
383+
# Verify results
384+
assert result == []
385+
386+
# Verify mock calls
387+
mock_conn.identity.domains.assert_called_once()
388+
389+
def test_get_domain_success(self, mock_get_openstack_conn_identity):
390+
"""Test getting a identity domain successfully."""
391+
mock_conn = mock_get_openstack_conn_identity
392+
393+
# Create mock domain object
394+
mock_domain = Mock()
395+
mock_domain.id = "domainone"
396+
mock_domain.name = "DomainOne"
397+
mock_domain.description = "Domain One description"
398+
mock_domain.is_enabled = True
399+
400+
# Configure mock domain.get_domain()
401+
mock_conn.identity.get_domain.return_value = mock_domain
402+
403+
# Test get_domain()
404+
identity_tools = self.get_identity_tools()
405+
result = identity_tools.get_domain(id="domainone")
406+
407+
# Verify results
408+
assert result == Domain(id="domainone", name="DomainOne", description="Domain One description", is_enabled=True)
409+
410+
# Verify mock calls
411+
mock_conn.identity.get_domain.assert_called_once_with(domain="domainone")
412+
413+
def test_get_domain_not_found(self, mock_get_openstack_conn_identity):
414+
"""Test getting a identity domain that does not exist."""
415+
mock_conn = mock_get_openstack_conn_identity
416+
417+
# Configure mock to raise NotFoundException
418+
mock_conn.identity.get_domain.side_effect = exceptions.NotFoundException(
419+
"Domain 'domainone' not found",
420+
)
421+
422+
# Test get_domain()
423+
identity_tools = self.get_identity_tools()
424+
425+
# Verify exception is raised
426+
with pytest.raises(exceptions.NotFoundException, match="Domain 'domainone' not found"):
427+
identity_tools.get_domain(id="domainone")
428+
429+
# Verify mock calls
430+
mock_conn.identity.get_domain.assert_called_once_with(domain="domainone")

0 commit comments

Comments
 (0)