Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2c6f093
Define base tools related to mixins
gregory-lvtx Jun 2, 2026
c22c50f
Use _list for acknowledgements
gregory-lvtx Jun 2, 2026
fee6719
Use _list for commands
gregory-lvtx Jun 2, 2026
e480128
Use _list, _delete for downtimes
gregory-lvtx Jun 2, 2026
87a7b98
Use _list for monitoring servers
gregory-lvtx Jun 2, 2026
434d0c5
Use _list, _create, _delete for host categories
gregory-lvtx Jun 2, 2026
85480bd
Use _list, _create, _delete for host groups
gregory-lvtx Jun 2, 2026
0075a4f
Use _list, _create, _delete for host severities
gregory-lvtx Jun 2, 2026
fbc5108
Use _list, _create, _delete, _patch for host templates
gregory-lvtx Jun 2, 2026
deb0af1
Use _list, _create, _delete, _patch for hosts
gregory-lvtx Jun 2, 2026
cf46004
Use _list, _create, _delete, _patch for hosts
gregory-lvtx Jun 2, 2026
ff8931d
Use _list for service groups
gregory-lvtx Jun 2, 2026
65d1d1e
Fix typos
gregory-lvtx Jun 2, 2026
c22b09c
Write unit test for _list method
gregory-lvtx Jun 3, 2026
ffdc40a
Write unit test for _patch method
gregory-lvtx Jun 3, 2026
b48046b
Remove old _list method
gregory-lvtx Jun 3, 2026
a3961eb
Add extras parameter to list mixin to use it for resource
gregory-lvtx Jun 3, 2026
4175d0e
Use generic _list method for resources
gregory-lvtx Jun 3, 2026
5143c52
Update host group configuration model to simplify update tool
gregory-lvtx Jun 3, 2026
a4c7004
Inherit update mixin from read one for typing issue
gregory-lvtx Jun 4, 2026
7c5c209
Define generic method to update resource
gregory-lvtx Jun 4, 2026
2f767f7
Use generic _update for host categories
gregory-lvtx Jun 4, 2026
d2f3a05
Use generic _update for host severities
gregory-lvtx Jun 4, 2026
dc92e78
Use generic _update for host groups
gregory-lvtx Jun 4, 2026
1109836
No need icon class anymore
gregory-lvtx Jun 4, 2026
bfc8282
Keep only hosts ids to be aligned with base params
gregory-lvtx Jun 4, 2026
1e45940
Test _delete with one succes and one error
gregory-lvtx Jun 4, 2026
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
5 changes: 3 additions & 2 deletions centreon_mcp/components/acknowledgement.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
from fastmcp import FastMCP
from pydantic import Field

from centreon_mcp.components.base import _list
from centreon_mcp.types.acknowledgement import (
Acknowledgement,
AcknowledgementParams,
AcknowledgementResource,
)
from centreon_mcp.utils import logger
from centreon_mcp.utils.base import BaseFilter, BaseOrder, _list
from centreon_mcp.utils.base import BaseFilter, BaseOrder

acknowledgement = FastMCP()

Expand Down Expand Up @@ -53,7 +54,7 @@ async def list_acknowledgements(
List all acknowledgements in real-time monitoring.
"""
logger.info("Executing tool list_acknowledgements")
return await _list(Acknowledgement, AcknowledgementOrder, filters, limit, page, order)
return await _list(Acknowledgement, filters, limit, page, order)


@acknowledgement.tool(
Expand Down
72 changes: 72 additions & 0 deletions centreon_mcp/components/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import asyncio
import json
from collections.abc import Sequence
from typing import Any

from pydantic import BaseModel

from centreon_mcp.utils.base import BaseFilter, BaseOrder
from centreon_mcp.utils.mixins import (
CreateMixin,
DeleteMixin,
ListMixin,
PatchMixin,
UpdateMixin,
)


async def _list[CentreonModel: ListMixin](
model: type[CentreonModel],
filters: Sequence[BaseFilter] | None = None,
limit: int = 10,
page: int = 1,
order: BaseOrder | None = None,
extras: dict[str, Any] | None = None,
) -> list[CentreonModel]:
"""
Generic function to list resources based on provided filters, pagination and order.
"""
search = json.dumps(BaseFilter.join(filters))
sort_by = order.model_dump_json() if order else None
Comment thread
denrou marked this conversation as resolved.
return await model.list(search, limit, page, sort_by, extras)


async def _delete[CentreonModel: DeleteMixin](
Comment thread
gregory-lvtx marked this conversation as resolved.
model: type[CentreonModel], model_ids: list[int]
) -> dict[int, bool | BaseException]:
"""
Generic function to delete multiple resources based on their ids.
"""
tasks = [asyncio.create_task(model.delete(model_id)) for model_id in model_ids]
results = await asyncio.gather(*tasks, return_exceptions=True)
return dict(zip(model_ids, results, strict=True))


async def _create[CentreonModel: CreateMixin](
model: type[CentreonModel], params: BaseModel
) -> bool:
"""
Generic function to create a resource based on params.
"""
return await model.create(params)


async def _patch[CentreonModel: PatchMixin](
model: type[CentreonModel], model_id: int, params: BaseModel
) -> bool:
"""
Generic function to patch a resource based on params.
"""
return await model.patch(model_id, params)


async def _update[CentreonModel: UpdateMixin, FullParams: BaseModel](
model: type[CentreonModel], full_params_cls: type[FullParams], model_id: int, params: BaseModel
) -> bool:
"""
Generic function to update a resource from params.
"""
current = await model.get(model_id)
data = current.model_dump(exclude={"id"}, exclude_none=True) # type: ignore
data |= params.model_dump(exclude_none=True)
return await model.update(model_id, full_params_cls(**data))
5 changes: 3 additions & 2 deletions centreon_mcp/components/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
from fastmcp import FastMCP
from pydantic import Field

from centreon_mcp.components.base import _list
from centreon_mcp.types.command import Command, CommandParams, CommandType
from centreon_mcp.utils import logger
from centreon_mcp.utils.base import BaseFilter, BaseOrder, _list
from centreon_mcp.utils.base import BaseFilter, BaseOrder

command = FastMCP()

Expand Down Expand Up @@ -42,7 +43,7 @@ async def list_commands(
to avoid retrieving all commands except if explicitly intended.
"""
logger.info("Executing tool list_commands")
return await _list(Command, CommandOrder, filters, limit, page, order)
return await _list(Command, filters, limit, page, order)


@command.tool(
Expand Down
10 changes: 4 additions & 6 deletions centreon_mcp/components/downtime.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import asyncio
from typing import Annotated, Literal

from fastmcp import FastMCP
from pydantic import Field

from centreon_mcp.components.base import _delete, _list
from centreon_mcp.types.downtime import Downtime, DowntimeParams, DowntimeResource
from centreon_mcp.types.host import HostState
from centreon_mcp.utils import logger
from centreon_mcp.utils.base import BaseFilter, BaseOrder, _list
from centreon_mcp.utils.base import BaseFilter, BaseOrder

downtime = FastMCP()

Expand Down Expand Up @@ -59,7 +59,7 @@ async def list_downtimes(
to avoid retrieving all downtimes except if explicitly intended.
"""
logger.info("Executing tool list_downtimes")
return await _list(Downtime, DowntimeOrder, filters, limit, page, order)
return await _list(Downtime, filters, limit, page, order)


@downtime.tool(
Expand Down Expand Up @@ -95,6 +95,4 @@ async def cancel_downtimes(downtime_ids: list[int]) -> dict[int, bool | BaseExce
Use tools `list_downtimes` first to get downtime IDs.
"""
logger.info("Executing tool cancel_downtimes")
tasks = [asyncio.create_task(Downtime.delete(downtime_id)) for downtime_id in downtime_ids]
results = await asyncio.gather(*tasks, return_exceptions=True)
return dict(zip(downtime_ids, results, strict=True))
return await _delete(Downtime, downtime_ids)
14 changes: 6 additions & 8 deletions centreon_mcp/components/host.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import asyncio
import json
from typing import Annotated, Literal

from fastmcp import FastMCP
from pydantic import Field

from centreon_mcp.components.base import _create, _delete, _list, _patch
from centreon_mcp.types.host import (
Host,
HostConfiguration,
Expand All @@ -13,7 +13,7 @@
HostStatusCount,
)
from centreon_mcp.utils import logger
from centreon_mcp.utils.base import BaseFilter, BaseOrder, _list
from centreon_mcp.utils.base import BaseFilter, BaseOrder

host = FastMCP()

Expand Down Expand Up @@ -87,7 +87,7 @@ async def list_host_configurations(
to avoid retrieving all host configurations except if explicitly intended.
"""
logger.info("Executing tool list_host_configurations")
return await _list(HostConfiguration, HostConfigurationOrder, filters, limit, page, order)
return await _list(HostConfiguration, filters, limit, page, order)


@host.tool(
Expand All @@ -104,7 +104,7 @@ async def create_host_configuration(params: HostConfigurationFullParams) -> bool
Create a host configuration from params.
"""
logger.info("Executing tool create_host_configuration")
return await HostConfiguration.create(params)
return await _create(HostConfiguration, params)


@host.tool(
Expand All @@ -121,7 +121,7 @@ async def update_host_configuration(host_id: int, params: HostConfigurationParti
Update a host configuration from params.
"""
logger.info("Executing tool update_host_configuration")
return await HostConfiguration.patch(host_id, params)
return await _patch(HostConfiguration, host_id, params)


@host.tool(
Expand All @@ -138,6 +138,4 @@ async def delete_host_configurations(host_ids: list[int]) -> dict[int, bool | Ba
Delete multiple host configurations.
"""
logger.info("Executing tool delete_host_configurations")
tasks = [asyncio.create_task(HostConfiguration.delete(host_id)) for host_id in host_ids]
results = await asyncio.gather(*tasks, return_exceptions=True)
return dict(zip(host_ids, results, strict=True))
return await _delete(HostConfiguration, host_ids)
24 changes: 7 additions & 17 deletions centreon_mcp/components/host_category.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import asyncio
from typing import Annotated, Literal

from fastmcp import FastMCP
from pydantic import Field

from centreon_mcp.components.base import _create, _delete, _list, _update
from centreon_mcp.types.host_category import (
HostCategoryConfiguration,
HostCategoryConfigurationFullParams,
HostCategoryConfigurationPartialParams,
)
from centreon_mcp.utils import logger
from centreon_mcp.utils.base import BaseFilter, BaseOrder, _list
from centreon_mcp.utils.base import BaseFilter, BaseOrder

host_category = FastMCP()

Expand Down Expand Up @@ -47,9 +47,7 @@ async def list_host_category_configurations(
to avoid retrieving all host categories except if explicitly intended.
"""
logger.info("Executing tool list_host_category_configurations")
return await _list(
HostCategoryConfiguration, HostCategoryConfigurationOrder, filters, limit, page, order
)
return await _list(HostCategoryConfiguration, filters, limit, page, order)


@host_category.tool(
Expand All @@ -66,7 +64,7 @@ async def create_host_category_configuration(params: HostCategoryConfigurationFu
Create a host category configuration.
"""
logger.info("Executing tool create_host_category_configuration")
return await HostCategoryConfiguration.create(params)
return await _create(HostCategoryConfiguration, params)


@host_category.tool(
Expand All @@ -85,11 +83,8 @@ async def update_host_category_configuration(
Update a host category from params.
"""
logger.info("Executing tool update_host_category_configuration")
host_category = await HostCategoryConfiguration.get(host_category_id)
data = host_category.model_dump(exclude={"id"}, exclude_none=True)
data |= params.model_dump(exclude_none=True)
return await HostCategoryConfiguration.update(
host_category_id, HostCategoryConfigurationFullParams(**data)
return await _update(
HostCategoryConfiguration, HostCategoryConfigurationFullParams, host_category_id, params
)


Expand All @@ -109,9 +104,4 @@ async def delete_host_category_configurations(
Delete multiple host category configurations.
"""
logger.info("Executing tool delete_host_category_configurations")
tasks = [
asyncio.create_task(HostCategoryConfiguration.delete(host_category_id))
for host_category_id in host_category_ids
]
results = await asyncio.gather(*tasks, return_exceptions=True)
return dict(zip(host_category_ids, results, strict=True))
return await _delete(HostCategoryConfiguration, host_category_ids)
31 changes: 9 additions & 22 deletions centreon_mcp/components/host_group.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import asyncio
from typing import Annotated, Literal

from fastmcp import FastMCP
from pydantic import Field

from centreon_mcp.components.base import _create, _delete, _list, _update
from centreon_mcp.types.host import HostState
from centreon_mcp.types.host_group import (
HostGroup,
Expand All @@ -12,7 +12,7 @@
HostGroupConfigurationPartialParams,
)
from centreon_mcp.utils import logger
from centreon_mcp.utils.base import BaseFilter, BaseOrder, _list
from centreon_mcp.utils.base import BaseFilter, BaseOrder

host_group = FastMCP()

Expand Down Expand Up @@ -53,7 +53,7 @@ async def list_host_groups(
to avoid retrieving all host groups except if explicitly intended.
"""
logger.info("Executing tool list_host_groups")
return await _list(HostGroup, HostGroupOrder, filters, limit, page, order)
return await _list(HostGroup, filters, limit, page, order)


class HostGroupConfigurationOrder(BaseOrder):
Expand Down Expand Up @@ -88,9 +88,7 @@ async def list_host_group_configurations(
to avoid retrieving all host groups except if explicitly intended.
"""
logger.info("Executing tool list_hostgroup_configurations")
return await _list(
HostGroupConfiguration, HostGroupConfigurationOrder, filters, limit, page, order
)
return await _list(HostGroupConfiguration, filters, limit, page, order)


@host_group.tool(
Expand All @@ -107,7 +105,7 @@ async def create_host_group_configuration(params: HostGroupConfigurationFullPara
Create a hostgroup.
"""
logger.info("Executing tool create_hostgroup_configuration")
return await HostGroupConfiguration.create(params)
return await _create(HostGroupConfiguration, params)


@host_group.tool(
Expand All @@ -123,17 +121,11 @@ async def update_host_group_configuration(
host_group_id: int, params: HostGroupConfigurationPartialParams
) -> bool:
"""
Update a host group from params.
Update a host group from params. Just need to get host_group_id first.
"""
logger.info("Executing tool update_host_group_configuration")
hostgroup = await HostGroupConfiguration.get(host_group_id)
data = hostgroup.model_dump(exclude={"id", "is_activated", "icon", "hosts"}, exclude_none=True)
data["icon_id"] = hostgroup.icon.id if hostgroup.icon else None
data["hosts"] = [host.id for host in hostgroup.hosts if host.id not in params.hosts_removed]
data["hosts"] += [host_id for host_id in params.hosts_added if host_id not in data["hosts"]]
data |= params.model_dump(exclude_none=True)
return await HostGroupConfiguration.update(
host_group_id, HostGroupConfigurationFullParams(**data)
return await _update(
HostGroupConfiguration, HostGroupConfigurationFullParams, host_group_id, params
)


Expand All @@ -153,9 +145,4 @@ async def delete_host_group_configurations(
Delete multiple host group configurations.
"""
logger.info("Executing tool delete_host_group_configurations")
tasks = [
asyncio.create_task(HostGroupConfiguration.delete(hostgroup_id))
for hostgroup_id in hostgroup_ids
]
results = await asyncio.gather(*tasks, return_exceptions=True)
return dict(zip(hostgroup_ids, results, strict=True))
return await _delete(HostGroupConfiguration, hostgroup_ids)
Loading
Loading