diff --git a/cmk/base/plugins/agent_based/aws_status.py b/cmk/base/plugins/agent_based/aws_status.py index 5ef966abf66..aee00318395 100644 --- a/cmk/base/plugins/agent_based/aws_status.py +++ b/cmk/base/plugins/agent_based/aws_status.py @@ -116,7 +116,7 @@ class Section: def parse_string_table(string_table: type_defs.StringTable) -> Section: - agent_output = AgentOutput.parse_raw(string_table[0][0]) + agent_output = AgentOutput.model_validate_json(string_table[0][0]) return Section( discovery_param=agent_output.discovery_param, aws_rss_feed=AWSRSSFeed.parse_rss(agent_output.rss_str), diff --git a/cmk/base/plugins/agent_based/azure_status.py b/cmk/base/plugins/agent_based/azure_status.py index eefa82df90c..bc25a631b42 100644 --- a/cmk/base/plugins/agent_based/azure_status.py +++ b/cmk/base/plugins/agent_based/azure_status.py @@ -33,7 +33,7 @@ class AzureStatusesPerRegion(BaseModel, frozen=True): def parse_azure_status(string_table: StringTable) -> AzureStatusesPerRegion: - azure_status = AzureStatus.parse_raw(string_table[0][0]) + azure_status = AzureStatus.model_validate_json(string_table[0][0]) regions: dict[str, list[AzureIssue]] = {r: [] for r in azure_status.regions} for issue in azure_status.issues: diff --git a/cmk/base/plugins/agent_based/cmk_agent_ctl_status.py b/cmk/base/plugins/agent_based/cmk_agent_ctl_status.py index 7615537d526..ab9eead0661 100644 --- a/cmk/base/plugins/agent_based/cmk_agent_ctl_status.py +++ b/cmk/base/plugins/agent_based/cmk_agent_ctl_status.py @@ -16,7 +16,7 @@ def parse_cmk_agent_ctl_status(string_table: StringTable) -> ControllerSection | except IndexError: return None - return ControllerSection.parse_raw(json_str) + return ControllerSection.model_validate_json(json_str) register.agent_section( diff --git a/cmk/base/plugins/agent_based/cmk_update_agent_status.py b/cmk/base/plugins/agent_based/cmk_update_agent_status.py index 57774902518..8f6b9cd8383 100644 --- a/cmk/base/plugins/agent_based/cmk_update_agent_status.py +++ b/cmk/base/plugins/agent_based/cmk_update_agent_status.py @@ -2,7 +2,6 @@ # Copyright (C) 2019 Checkmk GmbH - License: GNU General Public License v2 # This file is part of Checkmk (https://checkmk.com). It is subject to the terms and # conditions defined in the file COPYING, which is part of this source code package. - from cmk.plugins.lib.checkmk import CMKAgentUpdateSection from .agent_based_api.v1 import register @@ -13,8 +12,8 @@ def _parse_cmk_update_agent_status(string_table: StringTable) -> CMKAgentUpdateS """parse cmk_update_agent_status""" try: - return CMKAgentUpdateSection.parse_raw(string_table[0][0]) - except IndexError: + return CMKAgentUpdateSection.model_validate_json(string_table[0][0]) + except (IndexError, ValueError): return None diff --git a/cmk/base/plugins/agent_based/gcp_status.py b/cmk/base/plugins/agent_based/gcp_status.py index 9896160c486..d39f88002b5 100644 --- a/cmk/base/plugins/agent_based/gcp_status.py +++ b/cmk/base/plugins/agent_based/gcp_status.py @@ -62,7 +62,7 @@ class Section: def parse(string_table: StringTable) -> Section: - output = AgentOutput.parse_raw(string_table[0][0]) + output = AgentOutput.model_validate_json(string_table[0][0]) data: dict[str, list[Incident]] = {} for incident in output.health_info: for location in incident.currently_affected_locations: diff --git a/cmk/base/plugins/agent_based/graylog_failures.py b/cmk/base/plugins/agent_based/graylog_failures.py index 200e765f6bb..a870309f916 100644 --- a/cmk/base/plugins/agent_based/graylog_failures.py +++ b/cmk/base/plugins/agent_based/graylog_failures.py @@ -21,7 +21,7 @@ class FailureMessage(BaseModel): def to_human_readable(self) -> Iterable[str]: yield from ( f"{field_name.title()}: {field_value}" - for field_name, field_value in self.dict(exclude_none=True).items() + for field_name, field_value in self.model_dump(exclude_none=True).items() ) diff --git a/cmk/base/plugins/agent_based/kube_deployment_conditions.py b/cmk/base/plugins/agent_based/kube_deployment_conditions.py index bb07368006b..066cd961cc6 100644 --- a/cmk/base/plugins/agent_based/kube_deployment_conditions.py +++ b/cmk/base/plugins/agent_based/kube_deployment_conditions.py @@ -61,7 +61,7 @@ def condition_levels(params: Mapping[str, VSResultAge], condition: str) -> tuple def _check( now: float, params: Mapping[str, VSResultAge], section: DeploymentConditions ) -> CheckResult: - conditions = section.dict() + conditions = section.model_dump() if all( condition["status"] is CONDITIONS_OK_MAPPINGS[name] for name, condition in conditions.items() diff --git a/cmk/base/plugins/agent_based/kube_node_container_count.py b/cmk/base/plugins/agent_based/kube_node_container_count.py index 9b7bdd3affa..96b1c47e4d6 100644 --- a/cmk/base/plugins/agent_based/kube_node_container_count.py +++ b/cmk/base/plugins/agent_based/kube_node_container_count.py @@ -33,7 +33,7 @@ def discovery(section: ContainerCount) -> DiscoveryResult: def check(params: KubeContainersLevelsUpperLower, section: ContainerCount) -> CheckResult: """Computes `total` and uses `check_levels` for each section element, setting levels from `params` individually""" - section_dict = section.dict() + section_dict = section.model_dump() section_dict["total"] = sum(section_dict.values()) for name, value in section_dict.items(): level_count_name = cast(CountName, name) diff --git a/cmk/base/plugins/agent_based/kube_node_count.py b/cmk/base/plugins/agent_based/kube_node_count.py index 9e516e6755b..c4a990d3002 100644 --- a/cmk/base/plugins/agent_based/kube_node_count.py +++ b/cmk/base/plugins/agent_based/kube_node_count.py @@ -81,7 +81,7 @@ class KubeNodeCountVSResult(TypedDict): def parse(string_table: StringTable) -> NodeCount: - return NodeCount.parse_raw(string_table[0][0]) + return NodeCount.model_validate_json(string_table[0][0]) def discovery(section: NodeCount) -> DiscoveryResult: diff --git a/cmk/base/plugins/agent_based/kube_replicas.py b/cmk/base/plugins/agent_based/kube_replicas.py index c7b0b4338f2..04439c663d6 100644 --- a/cmk/base/plugins/agent_based/kube_replicas.py +++ b/cmk/base/plugins/agent_based/kube_replicas.py @@ -74,7 +74,7 @@ def parse_kube_strategy(string_table: StringTable) -> UpdateStrategy: def parse_kube_controller_spec(string_table: StringTable) -> ControllerSpec: - return ControllerSpec.parse_raw(string_table[0][0]) + return ControllerSpec.model_validate_json(string_table[0][0]) register.agent_section( diff --git a/cmk/base/plugins/agent_based/openshift_queries.py b/cmk/base/plugins/agent_based/openshift_queries.py index ef78ea5c7b0..ca730387fc0 100644 --- a/cmk/base/plugins/agent_based/openshift_queries.py +++ b/cmk/base/plugins/agent_based/openshift_queries.py @@ -20,7 +20,7 @@ def parse(string_table: StringTable) -> OpenShiftEndpoint: - return OpenShiftEndpoint.parse_raw(string_table[0][0]) + return OpenShiftEndpoint.model_validate_json(string_table[0][0]) register.agent_section( diff --git a/cmk/base/plugins/agent_based/prometheus_cpu.py b/cmk/base/plugins/agent_based/prometheus_cpu.py index af11636db78..007d5c65c1d 100644 --- a/cmk/base/plugins/agent_based/prometheus_cpu.py +++ b/cmk/base/plugins/agent_based/prometheus_cpu.py @@ -23,7 +23,7 @@ def parse(string_table: type_defs.StringTable) -> cpu.Section: >>> parse([['{"load1": 1.06, "load5": 1.68, "load15": 1.41, "num_cpus": 8}']]) Section(load=Load(load1=1.06, load5=1.68, load15=1.41), num_cpus=8, threads=None, type=) """ - parsed_section = CPULoad.parse_raw(string_table[0][0]) + parsed_section = CPULoad.model_validate_json(string_table[0][0]) return cpu.Section( cpu.Load( load1=parsed_section.load1, diff --git a/cmk/base/plugins/agent_based/prometheus_uptime.py b/cmk/base/plugins/agent_based/prometheus_uptime.py index 8f8756773a1..a921572fea1 100644 --- a/cmk/base/plugins/agent_based/prometheus_uptime.py +++ b/cmk/base/plugins/agent_based/prometheus_uptime.py @@ -20,7 +20,7 @@ def parse(string_table: type_defs.StringTable) -> uptime.Section: >>> parse([['{"seconds": 2117}']]) Section(uptime_sec=2117, message=None) """ - seconds = Uptime.parse_raw(string_table[0][0]).seconds + seconds = Uptime.model_validate_json(string_table[0][0]).seconds return uptime.Section(uptime_sec=seconds, message=None) diff --git a/cmk/gui/background_job/_store.py b/cmk/gui/background_job/_store.py index 102b34814c8..07df5a46977 100644 --- a/cmk/gui/background_job/_store.py +++ b/cmk/gui/background_job/_store.py @@ -106,7 +106,7 @@ def exists(self) -> bool: return self._jobstatus_path.exists() def write(self, status: JobStatusSpec) -> None: - store.save_object_to_file(self._jobstatus_path, status.dict()) + store.save_object_to_file(self._jobstatus_path, status.model_dump()) def update(self, params: JobStatusSpecUpdate) -> None: if not self._jobstatus_path.parent.exists(): diff --git a/cmk/gui/dashboard/dashlet/dashlets/graph.py b/cmk/gui/dashboard/dashlet/dashlets/graph.py index 6e687bffc85..f16e62f5089 100644 --- a/cmk/gui/dashboard/dashlet/dashlets/graph.py +++ b/cmk/gui/dashboard/dashlet/dashlets/graph.py @@ -287,7 +287,7 @@ def _reload_js(self) -> str: return "dashboard_render_graph(%d, %s, %s, %s)" % ( self._dashlet_id, - self._graph_specification.json(), + self._graph_specification.model_dump_json(), json.dumps( default_dashlet_graph_render_options() # Something is wrong with the typing here. self._dashlet_spec is a subclass of diff --git a/cmk/gui/global_config.py b/cmk/gui/global_config.py index 28708bf5c67..59b77c4d31a 100755 --- a/cmk/gui/global_config.py +++ b/cmk/gui/global_config.py @@ -38,7 +38,8 @@ class GlobalConfig(BaseModel): def load_global_config() -> GlobalConfig: path = cse_config_dir / "global-config.json" try: - return GlobalConfig.parse_file(path) + with open(path, encoding="utf-8") as file: + return GlobalConfig.model_validate_json(file.read()) except Exception as e: LOGGER.debug("Failed to load config from %s: %s", path, e) return GlobalConfig( diff --git a/cmk/gui/graphing/_graph_images.py b/cmk/gui/graphing/_graph_images.py index 10ea648f5d3..1e6195de4f3 100644 --- a/cmk/gui/graphing/_graph_images.py +++ b/cmk/gui/graphing/_graph_images.py @@ -235,7 +235,7 @@ def graph_recipes_for_api_request( if consolidation_function := api_request.get("consolidation_function"): graph_recipes = [ - graph_recipe.copy(update={"consolidation_function": consolidation_function}) + graph_recipe.model_copy(update={"consolidation_function": consolidation_function}) for graph_recipe in graph_recipes ] diff --git a/cmk/gui/graphing/_graph_specification.py b/cmk/gui/graphing/_graph_specification.py index bb8be60ab9c..05e6c4133f6 100644 --- a/cmk/gui/graphing/_graph_specification.py +++ b/cmk/gui/graphing/_graph_specification.py @@ -10,7 +10,7 @@ from dataclasses import dataclass from typing import Annotated, Callable, Literal, Self, Union -from pydantic import BaseModel, Field, SerializeAsAny, TypeAdapter, validator +from pydantic import BaseModel, Field, field_validator, SerializeAsAny, TypeAdapter from typing_extensions import TypedDict from livestatus import SiteId @@ -460,7 +460,6 @@ class GraphRecipe(GraphRecipeBase, frozen=True): # https://docs.pydantic.dev/2.4/concepts/serialization/#serializing-with-duck-typing specification: SerializeAsAny[GraphSpecification] - @validator("specification", pre=True) - @classmethod + @field_validator("specification", mode="before") def parse_specification(cls, value: Mapping[str, object]) -> GraphSpecification: return parse_raw_graph_specification(value) diff --git a/cmk/gui/graphing/_html_render.py b/cmk/gui/graphing/_html_render.py index 48eb56a950d..e9b72195f7e 100644 --- a/cmk/gui/graphing/_html_render.py +++ b/cmk/gui/graphing/_html_render.py @@ -177,7 +177,7 @@ def _render_graph_html( "cmk.graphs.create_graph(%s, %s, %s, %s);" % ( json.dumps(html_code), - graph_artwork.json(), + graph_artwork.model_dump_json(), graph_render_config.model_dump_json(), json.dumps(_graph_ajax_context(graph_artwork, graph_data_range, graph_render_config)), ) @@ -194,7 +194,7 @@ def _graph_ajax_context( graph_render_config: GraphRenderConfig, ) -> dict[str, Any]: return { - "definition": graph_artwork.definition.dict(), + "definition": graph_artwork.definition.model_dump(), "data_range": graph_data_range.model_dump(), "render_config": graph_render_config.model_dump(), "display_id": graph_artwork.display_id, @@ -637,7 +637,7 @@ def _render_ajax_graph( save_graph_pin() if request.has_var("consolidation_function"): - graph_recipe = graph_recipe.copy( + graph_recipe = graph_recipe.model_copy( update={"consolidation_function": request.var("consolidation_function")} ) @@ -664,10 +664,10 @@ def _render_ajax_graph( return { "html": html_code, - "graph": graph_artwork.dict(), + "graph": graph_artwork.model_dump(), "context": { "graph_id": context["graph_id"], - "definition": graph_recipe.dict(), + "definition": graph_recipe.model_dump(), "data_range": graph_data_range.model_dump(), "render_config": graph_render_config.model_dump(), }, @@ -812,7 +812,7 @@ def _render_graph_container_html( output += HTMLWriter.render_javascript( "cmk.graphs.load_graph_content(%s, %s, %s, %s)" % ( - graph_recipe.json(), + graph_recipe.model_dump_json(), graph_data_range.model_dump_json(), graph_render_config.model_dump_json(), json.dumps(graph_display_id), diff --git a/cmk/gui/http.py b/cmk/gui/http.py index 6f3efc3cb52..594a9c8d5ec 100644 --- a/cmk/gui/http.py +++ b/cmk/gui/http.py @@ -380,7 +380,7 @@ def get_model_mandatory( ) -> Model_T: """Try to convert the value of an HTTP request variable to a given pydantic model""" try: - return model.parse_raw(mandatory_parameter(varname, self.var(varname))) + return model.model_validate_json(mandatory_parameter(varname, self.var(varname))) except ValueError as exception: raise MKUserError(varname, _("The value is not valid: '%s'") % exception) diff --git a/cmk/gui/watolib/services.py b/cmk/gui/watolib/services.py index 4c043d5faf3..ea07e63ff40 100644 --- a/cmk/gui/watolib/services.py +++ b/cmk/gui/watolib/services.py @@ -857,7 +857,7 @@ def _cleaned_up_status(job_status: JobStatusSpec) -> JobStatusSpec: "JobException": [], "JobResult": job_status.loginfo["JobResult"], } - return job_status.copy( + return job_status.model_copy( update={"state": JobStatusStates.FINISHED, "loginfo": new_loginfo}, deep=True, # not sure, better play it safe. ) diff --git a/cmk/plugins/lib/checkmk.py b/cmk/plugins/lib/checkmk.py index 37583a36e51..ae922e223d9 100644 --- a/cmk/plugins/lib/checkmk.py +++ b/cmk/plugins/lib/checkmk.py @@ -12,7 +12,7 @@ from pyasn1.error import PyAsn1Error from pyasn1.type.useful import GeneralizedTime -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator CheckmkSection = Mapping[str, str | None] CmkUpdateAgentStatus = Mapping[str, str] @@ -53,8 +53,7 @@ class CertInfoController(BaseModel): to: datetime issuer: str - @validator("to", pre=True) - @classmethod + @field_validator("to", mode="before") def _parse_cert_validity(cls, value: str | datetime) -> datetime: return ( value @@ -104,8 +103,7 @@ class CertInfo(BaseModel): signature_algorithm: str | None = Field(None) common_name: str | None = Field(None) - @validator("not_after", pre=True) - @classmethod + @field_validator("not_after", mode="before") def _validate_not_after(cls, value: str | datetime | None) -> datetime | None: """convert not_after from str to datetime diff --git a/cmk/plugins/lib/kube_resources.py b/cmk/plugins/lib/kube_resources.py index 3eeab3efb1c..378ced9224e 100644 --- a/cmk/plugins/lib/kube_resources.py +++ b/cmk/plugins/lib/kube_resources.py @@ -318,7 +318,10 @@ def performance_cpu( no new data is available. """ if section_kube_performance_cpu is not None: - host_value_store[value_store_key] = (current_timestamp, section_kube_performance_cpu.json()) + host_value_store[value_store_key] = ( + current_timestamp, + section_kube_performance_cpu.model_dump_json(), + ) return section_kube_performance_cpu if (timestamped_usage := host_value_store.get(value_store_key)) is not None: diff --git a/cmk/special_agents/agent_aws.py b/cmk/special_agents/agent_aws.py index 2ad328685ab..04c113fe116 100644 --- a/cmk/special_agents/agent_aws.py +++ b/cmk/special_agents/agent_aws.py @@ -5830,7 +5830,7 @@ def _compute_content( return AWSComputedContent(clusters, raw_content.cache_timestamp) def _create_results(self, computed_content: AWSComputedContent) -> list[AWSSectionResult]: - return [AWSSectionResult("", [c.dict() for c in computed_content.content])] + return [AWSSectionResult("", [c.model_dump() for c in computed_content.content])] class ECS(AWSSectionCloudwatch): @@ -6179,7 +6179,7 @@ def _compute_content( return AWSComputedContent((clusters, nodes), raw_content.cache_timestamp) def _create_results(self, computed_content: AWSComputedContent) -> list[AWSSectionResult]: - return [AWSSectionResult("", [c.dict() for c in computed_content.content[0]])] + return [AWSSectionResult("", [c.model_dump() for c in computed_content.content[0]])] class ElastiCache(AWSSectionCloudwatch): diff --git a/cmk/special_agents/agent_aws_status.py b/cmk/special_agents/agent_aws_status.py index 382cb810503..5db57e14d32 100644 --- a/cmk/special_agents/agent_aws_status.py +++ b/cmk/special_agents/agent_aws_status.py @@ -64,7 +64,7 @@ def write_section(args: Args, get_rss: typing.Callable[[], requests.Response] = rss_str=response.text, ) with agent_common.SectionWriter("aws_status") as writer: - writer.append(section.json()) + writer.append(section.model_dump_json()) return 0 diff --git a/cmk/special_agents/agent_azure_status.py b/cmk/special_agents/agent_azure_status.py index d3e4f7424b8..9b5bcbe9320 100644 --- a/cmk/special_agents/agent_azure_status.py +++ b/cmk/special_agents/agent_azure_status.py @@ -101,7 +101,7 @@ def write_section(args: Args) -> int: azure_status = AzureStatus(link=feed.feed.link, regions=selected_regions, issues=azure_issues) with SectionWriter("azure_status") as writer: - writer.append_json(azure_status.dict()) + writer.append_json(azure_status.model_dump()) return 0 diff --git a/cmk/special_agents/agent_gcp_status.py b/cmk/special_agents/agent_gcp_status.py index 2e720a4fcae..4d67088c144 100644 --- a/cmk/special_agents/agent_gcp_status.py +++ b/cmk/special_agents/agent_gcp_status.py @@ -43,7 +43,7 @@ class AgentOutput(pydantic.BaseModel): def _health_serializer(section: AgentOutput) -> None: with agent_common.SectionWriter("gcp_status") as w: - w.append(section.json()) + w.append(section.model_dump_json()) def parse_arguments(argv: Sequence[str] | None) -> Args: diff --git a/cmk/special_agents/agent_kube.py b/cmk/special_agents/agent_kube.py index 1ee3109223c..e7ca12e3b0a 100644 --- a/cmk/special_agents/agent_kube.py +++ b/cmk/special_agents/agent_kube.py @@ -400,7 +400,7 @@ def _write_sections(sections: Mapping[str, Callable[[], section.Section | None]] for section_name, section_call in sections.items(): if section_output := section_call(): with SectionWriter(section_name) as writer: - writer.append(section_output.json()) + writer.append(section_output.model_dump_json()) def namespaced_name_from_metadata(metadata: api.MetaData) -> str: @@ -704,7 +704,7 @@ def _group_metadata_by_node( for node_collector in node_collectors_metadata: components = nodes_components.setdefault(node_collector.node, {}) - for component, version in node_collector.components.dict().items(): + for component, version in node_collector.components.model_dump().items(): if version is not None: components[component] = section.NodeComponent( collector_type=node_collector.collector_type, @@ -730,7 +730,7 @@ def write_cluster_collector_info_section( processing_log=processing_log, cluster_collector=cluster_collector, nodes=node_collectors_metadata, - ).json() + ).model_dump_json() ) @@ -1137,7 +1137,7 @@ def main(args: list[str] | None = None) -> int: # pylint: disable=too-many-bran metadata = request_cluster_collector( query.CollectorPath.metadata, usage_config, - section.Metadata.parse_raw, + section.Metadata.model_validate_json, ) supported_collector_version = 1 @@ -1281,7 +1281,7 @@ def main(args: list[str] | None = None) -> int: # pylint: disable=too-many-bran section.CollectorProcessingLogs( container=collector_container_logs[-1], machine=collector_machine_logs[-1], - ).json() + ).model_dump_json() ) except Exception as e: if arguments.debug: diff --git a/cmk/special_agents/utils/node_exporter.py b/cmk/special_agents/utils/node_exporter.py index e35d743f982..a5a3c101e08 100644 --- a/cmk/special_agents/utils/node_exporter.py +++ b/cmk/special_agents/utils/node_exporter.py @@ -388,7 +388,7 @@ def uptime_summary(self) -> dict[str, SectionStr]: return { sample["labels"]["instance"]: _create_section( "prometheus_uptime_v1:sep(0)", - [Uptime.model_validate({"seconds": sample["value"]}).json()], + [Uptime.model_validate({"seconds": sample["value"]}).model_dump_json()], ) for sample in uptime_samples } @@ -405,7 +405,9 @@ def cpu_summary(self) -> dict[str, SectionStr]: raw = node_to_raw.setdefault(instance, {}) raw[key] = sample["value"] return { - node: _create_section("prometheus_cpu_v1:sep(0)", [CPULoad.model_validate(raw).json()]) + node: _create_section( + "prometheus_cpu_v1:sep(0)", [CPULoad.model_validate(raw).model_dump_json()] + ) for node, raw in node_to_raw.items() } diff --git a/cmk/special_agents/utils_kubernetes/api_server.py b/cmk/special_agents/utils_kubernetes/api_server.py index dafdbaa5587..d7d3a3fae73 100644 --- a/cmk/special_agents/utils_kubernetes/api_server.py +++ b/cmk/special_agents/utils_kubernetes/api_server.py @@ -13,9 +13,9 @@ from collections.abc import Mapping, Sequence from dataclasses import dataclass +import pydantic import requests from kubernetes import client # type: ignore[import] -from pydantic import parse_obj_as from cmk.special_agents.utils_kubernetes import query from cmk.special_agents.utils_kubernetes.controllers import ( @@ -467,6 +467,14 @@ def parse_api_data( ] cluster_details = api.ClusterDetails(api_health=api_health, version=git_version) + + def _parse_obj_as( + model: type[list[api.PersistentVolume]], + expr: typing.Sequence[typing.Any], + ) -> typing.Sequence[api.PersistentVolume]: + adapter = pydantic.TypeAdapter(model) + return adapter.validate_python(expr) + return APIData( cron_jobs=cron_jobs, deployments=deployments, @@ -477,7 +485,7 @@ def parse_api_data( nodes=nodes, pods=pods, persistent_volume_claims=persistent_volume_claims, - persistent_volumes=parse_obj_as(list[api.PersistentVolume], raw_persistent_volumes), + persistent_volumes=_parse_obj_as(list[api.PersistentVolume], raw_persistent_volumes), kubelet_open_metrics=[ kubelet_metric_sample for dump in kubelet_open_metrics_dumps diff --git a/cmk/special_agents/utils_kubernetes/performance.py b/cmk/special_agents/utils_kubernetes/performance.py index c03463a4a87..061976ab29e 100755 --- a/cmk/special_agents/utils_kubernetes/performance.py +++ b/cmk/special_agents/utils_kubernetes/performance.py @@ -160,7 +160,8 @@ def _create_cpu_rate_metrics( def _load_containers_store(container_store_path: Path) -> ContainersStore: common.LOGGER.debug("Load previous cycle containers store from %s", container_store_path) try: - return ContainersStore.parse_file(container_store_path) + with open(container_store_path, encoding="utf-8") as file: + return ContainersStore.model_validate_json(file.read()) except FileNotFoundError as e: common.LOGGER.info("Could not find metrics file. This is expected if the first run.") common.LOGGER.debug("Exception: %s", e) @@ -175,8 +176,8 @@ def _persist_containers_store( ) -> None: common.LOGGER.debug("Persisting current containers store under %s", container_store_path) container_store_path.parent.mkdir(parents=True, exist_ok=True) - with open(container_store_path, "w") as f: - f.write(containers_store.json(by_alias=True)) + with open(container_store_path, "w", encoding="utf-8") as f: + f.write(containers_store.model_dump_json(by_alias=True)) def _determine_cpu_rate_metrics( diff --git a/cmk/special_agents/utils_kubernetes/transform.py b/cmk/special_agents/utils_kubernetes/transform.py index 9934916ae96..9c59401c0a3 100644 --- a/cmk/special_agents/utils_kubernetes/transform.py +++ b/cmk/special_agents/utils_kubernetes/transform.py @@ -9,11 +9,12 @@ from __future__ import annotations +import typing from collections.abc import Iterable, Mapping, Sequence from typing import cast, Literal, TypeAlias, TypeVar +import pydantic from kubernetes import client # type: ignore[import] -from pydantic import parse_obj_as from . import transform_json from .schemata import api @@ -72,6 +73,13 @@ def expect_value(v: T | None) -> T: def pod_spec(pod: client.V1Pod) -> api.PodSpec: spec: client.V1PodSpec = expect_value(pod.spec) + + def _parse_obj_as( + model: type[list[T]], expr: typing.Sequence[T] | None + ) -> typing.Sequence[T] | None: + adapter = pydantic.TypeAdapter(model) + return adapter.validate_python(expr) + return api.PodSpec( node=spec.node_name, host_network=spec.host_network, @@ -83,7 +91,7 @@ def pod_spec(pod: client.V1Pod) -> api.PodSpec: ), priority_class_name=spec.priority_class_name, active_deadline_seconds=spec.active_deadline_seconds, - volumes=parse_obj_as(list[api.Volume], spec.volumes) if spec.volumes else None, + volumes=_parse_obj_as(list[api.Volume], spec.volumes) if spec.volumes else None, ) diff --git a/cmk/utils/prediction/_prediction.py b/cmk/utils/prediction/_prediction.py index 830be002f95..2764dd79861 100644 --- a/cmk/utils/prediction/_prediction.py +++ b/cmk/utils/prediction/_prediction.py @@ -108,8 +108,8 @@ def save_prediction( data: PredictionData, ) -> None: self._dir.mkdir(exist_ok=True, parents=True) - self._info_file(info.name).write_text(info.json()) - self._data_file(info.name).write_text(data.json()) + self._info_file(info.name).write_text(info.model_dump_json()) + self._data_file(info.name).write_text(data.model_dump_json()) def remove_prediction(self, timegroup: Timegroup) -> None: self._data_file(timegroup).unlink(missing_ok=True) @@ -118,7 +118,7 @@ def remove_prediction(self, timegroup: Timegroup) -> None: def get_info(self, timegroup: Timegroup) -> PredictionInfo | None: file_path = self._info_file(timegroup) try: - return PredictionInfo.parse_raw(file_path.read_text()) + return PredictionInfo.model_validate_json(file_path.read_text()) except FileNotFoundError: logger.log(VERBOSE, "No prediction info for group %s available.", timegroup) return None @@ -126,7 +126,7 @@ def get_info(self, timegroup: Timegroup) -> PredictionInfo | None: def get_data(self, timegroup: Timegroup) -> PredictionData | None: file_path = self._data_file(timegroup) try: - return PredictionData.parse_raw(file_path.read_text()) + return PredictionData.model_validate_json(file_path.read_text()) except FileNotFoundError: logger.log(VERBOSE, "No prediction for group %s available.", timegroup) return None diff --git a/cmk/utils/prediction/_query.py b/cmk/utils/prediction/_query.py index 56e00c292a4..b125482f9e2 100644 --- a/cmk/utils/prediction/_query.py +++ b/cmk/utils/prediction/_query.py @@ -31,14 +31,14 @@ def query_available_predictions(self) -> Iterator[PredictionInfo]: self._filter_prediction_files_by_metric(self._query_prediction_files()) ) yield from ( - PredictionInfo.parse_raw(self._query_prediction_file_content(prediction_file)) + PredictionInfo.model_validate_json(self._query_prediction_file_content(prediction_file)) for prediction_file in available_prediction_files if prediction_file.suffix == INFO_FILE_SUFFIX and prediction_file.with_suffix(DATA_FILE_SUFFIX) in available_prediction_files ) def query_prediction_data(self, time_group: Timegroup) -> PredictionData: - return PredictionData.parse_raw( + return PredictionData.model_validate_json( self._query_prediction_file_content( Path(pnp_cleanup(self.metric_name), time_group), ) diff --git a/packages/cmk-agent-receiver/cmk/agent_receiver/checkmk_rest_api.py b/packages/cmk-agent-receiver/cmk/agent_receiver/checkmk_rest_api.py index 795cb67d17b..a335a187e8d 100644 --- a/packages/cmk-agent-receiver/cmk/agent_receiver/checkmk_rest_api.py +++ b/packages/cmk-agent-receiver/cmk/agent_receiver/checkmk_rest_api.py @@ -296,7 +296,7 @@ def _parse_error_response_body(body: str) -> str: 'Insufficient permissions - Details: You need permission xyz.' """ try: - error_descr = _RestApiErrorDescr.parse_raw(body) + error_descr = _RestApiErrorDescr.model_validate_json(body) except Exception: # pylint: disable=broad-exception-caught return body return error_descr.title + (f" - Details: {error_descr.detail}" if error_descr.detail else "") diff --git a/packages/cmk-agent-receiver/cmk/agent_receiver/utils.py b/packages/cmk-agent-receiver/cmk/agent_receiver/utils.py index bcbbce58a6a..c92b674136e 100644 --- a/packages/cmk-agent-receiver/cmk/agent_receiver/utils.py +++ b/packages/cmk-agent-receiver/cmk/agent_receiver/utils.py @@ -45,7 +45,8 @@ class R4R: def read(cls, uuid: UUID4) -> Self: for status in R4RStatus: if (path := r4r_dir() / status.name / f"{uuid}.json").exists(): - request = RequestForRegistration.parse_file(path) + with open(path, encoding="utf-8") as file: + request = RequestForRegistration.model_validate_json(file.read()) # access time is used to determine when to remove registration request file with suppress(OSError): os.utime(path, None) @@ -58,7 +59,10 @@ def write(self) -> None: parents=True, exist_ok=True, ) - (target_path := target_dir / f"{self.request.uuid}.json").write_text(self.request.json()) + (target_path := target_dir / f"{self.request.uuid}.json").write_text( + self.request.model_dump_json(), + encoding="utf-8", + ) target_path.chmod(0o660) diff --git a/packages/cmk-mkp-tool/cmk/mkp_tool/cli.py b/packages/cmk-mkp-tool/cmk/mkp_tool/cli.py index b352de8f3ac..0e2db550e30 100644 --- a/packages/cmk-mkp-tool/cmk/mkp_tool/cli.py +++ b/packages/cmk-mkp-tool/cmk/mkp_tool/cli.py @@ -219,7 +219,7 @@ def _command_inspect( manifest = extract_manifest(file_content) - sys.stdout.write(f"{manifest.json() if args.json else _to_text(manifest)}\n") + sys.stdout.write(f"{manifest.model_dump_json() if args.json else _to_text(manifest)}\n") return 0 @@ -247,7 +247,7 @@ def _command_show_all( ) if args.json: - sys.stdout.write(f"{stored_manifests.json()}\n") + sys.stdout.write(f"{stored_manifests.model_dump_json()}\n") return 0 # I don't think this is very useful, but we include it for consistency. @@ -282,7 +282,7 @@ def _command_show( manifest = extract_manifest( package_store.read_bytes(_get_package_id(args.name, args.version, package_store)) ) - sys.stdout.write(f"{manifest.json() if args.json else _to_text(manifest)}\n") + sys.stdout.write(f"{manifest.model_dump_json() if args.json else _to_text(manifest)}\n") return 0 @@ -339,7 +339,7 @@ def _command_list( ) if args.json: - sys.stdout.write(f"{classified_manifests.json()}\n") + sys.stdout.write(f"{classified_manifests.model_dump_json()}\n") return 0 enabled_ids = {m.id for m in classified_manifests.enabled} diff --git a/tests/unit/cmk/base/plugins/agent_based/test_kube_cpu.py b/tests/unit/cmk/base/plugins/agent_based/test_kube_cpu.py index b8321fb8d19..1a79959f516 100644 --- a/tests/unit/cmk/base/plugins/agent_based/test_kube_cpu.py +++ b/tests/unit/cmk/base/plugins/agent_based/test_kube_cpu.py @@ -106,7 +106,7 @@ def test_stored_usage_value() -> None: value_store = { "cpu_usage": ( TIMESTAMP - ONE_MINUTE * 1, - PerformanceUsage(resource=Cpu(type_="cpu", usage=USAGE)).json(), + PerformanceUsage(resource=Cpu(type_="cpu", usage=USAGE)).model_dump_json(), ) } performance_cpu = cmk.plugins.lib.kube_resources.performance_cpu( @@ -119,7 +119,7 @@ def test_stored_outdated_usage_value() -> None: value_store = { "cpu_usage": ( TIMESTAMP - ONE_MINUTE * 2, - PerformanceUsage(resource=Cpu(type_="cpu", usage=USAGE)).json(), + PerformanceUsage(resource=Cpu(type_="cpu", usage=USAGE)).model_dump_json(), ) } diff --git a/tests/unit/cmk/base/plugins/agent_based/test_kube_deployment_conditions.py b/tests/unit/cmk/base/plugins/agent_based/test_kube_deployment_conditions.py index 816fdbb2473..76cc238c655 100644 --- a/tests/unit/cmk/base/plugins/agent_based/test_kube_deployment_conditions.py +++ b/tests/unit/cmk/base/plugins/agent_based/test_kube_deployment_conditions.py @@ -139,14 +139,14 @@ def check_result(params: Mapping[str, VSResultAge], section: DeploymentCondition def test_ok_state_mappings_match_conditions() -> None: assert all( condition in kube_deployment_conditions.CONDITIONS_OK_MAPPINGS - for condition in DeploymentConditions.schema()["properties"] + for condition in DeploymentConditions.model_json_schema()["properties"] ) def test_parse(string_table: StringTable) -> None: section = kube_deployment_conditions.parse(string_table) assert section is not None - assert len(section.dict()) == 3 + assert len(section.model_dump()) == 3 def test_discovery(section: DeploymentConditions) -> None: diff --git a/tests/unit/cmk/base/plugins/agent_based/test_kube_node_container_count.py b/tests/unit/cmk/base/plugins/agent_based/test_kube_node_container_count.py index 5b8f4e6cb4e..cb34d9943ad 100644 --- a/tests/unit/cmk/base/plugins/agent_based/test_kube_node_container_count.py +++ b/tests/unit/cmk/base/plugins/agent_based/test_kube_node_container_count.py @@ -109,11 +109,11 @@ def check_result(section, params): def test_check_yields_check_results(check_result: CheckResult, section: ContainerCount) -> None: - assert len(list(check_result)) == 2 * len(section.dict()) + 2 + assert len(list(check_result)) == 2 * len(section.model_dump()) + 2 def test_check_yields_results(check_result: CheckResult, section: ContainerCount) -> None: - expected = len(section.dict()) + 1 + expected = len(section.model_dump()) + 1 assert len([r for r in check_result if isinstance(r, Result)]) == expected @@ -122,12 +122,12 @@ def test_check_all_states_ok(check_result: CheckResult) -> None: def test_check_yields_metrics(check_result: CheckResult, section: ContainerCount) -> None: - expected = len(section.dict()) + 1 + expected = len(section.model_dump()) + 1 assert len([m for m in check_result if isinstance(m, Metric)]) == expected def test_check_all_metrics_values(check_result: CheckResult, section: ContainerCount) -> None: - expected = [*section.dict().values(), sum(section.dict().values())] + expected = [*section.model_dump().values(), sum(section.model_dump().values())] assert [m.value for m in check_result if isinstance(m, Metric)] == expected @@ -140,13 +140,13 @@ def test_check_issues_expected_check_levels_calls( check_levels: MagicMock, check_result: CheckResult, section: ContainerCount ) -> None: list(check_result) - assert check_levels.call_count == len(section.dict()) + 1 + assert check_levels.call_count == len(section.model_dump()) + 1 def test_check_calls_check_levels_with_values( check_levels: MagicMock, check_result: CheckResult, section: ContainerCount ) -> None: - expected_values = [*section.dict().values(), sum(section.dict().values())] + expected_values = [*section.model_dump().values(), sum(section.model_dump().values())] list(check_result) actual_values = [call.args[0] for call in check_levels.call_args_list] assert actual_values == expected_values @@ -175,7 +175,9 @@ def test_check_calls_check_levels_with_levels_default( def test_check_calls_check_levels_with_metric_name( check_levels: MagicMock, check_result: CheckResult, section: ContainerCount ) -> None: - expected_metrics = [f"kube_node_container_count_{name}" for name in [*section.dict(), "total"]] + expected_metrics = [ + f"kube_node_container_count_{name}" for name in [*section.model_dump(), "total"] + ] list(check_result) actual_metrics = [call.kwargs["metric_name"] for call in check_levels.call_args_list] assert actual_metrics == expected_metrics @@ -184,7 +186,7 @@ def test_check_calls_check_levels_with_metric_name( def test_check_calls_check_levels_with_labels( check_levels: MagicMock, check_result: CheckResult, section: ContainerCount ) -> None: - expected_labels = [f"{name.title()}" for name in [*section.dict(), "total"]] + expected_labels = [f"{name.title()}" for name in [*section.model_dump(), "total"]] list(check_result) actual_labels = [call.kwargs["label"] for call in check_levels.call_args_list] assert actual_labels == expected_labels diff --git a/tests/unit/cmk/special_agents/agent_kube/test_node.py b/tests/unit/cmk/special_agents/agent_kube/test_node.py index ee15a1fbd00..6506111ac8f 100644 --- a/tests/unit/cmk/special_agents/agent_kube/test_node.py +++ b/tests/unit/cmk/special_agents/agent_kube/test_node.py @@ -105,7 +105,7 @@ def test_conditions_returns_all_native_conditions() -> None: ) node_conditions = _conditions(api_node) assert node_conditions is not None - conditions_dict = node_conditions.dict() + conditions_dict = node_conditions.model_dump() assert len(conditions_dict) == len(NATIVE_NODE_CONDITION_TYPES) assert all( condition_type.lower() in conditions_dict for condition_type in NATIVE_NODE_CONDITION_TYPES @@ -123,7 +123,7 @@ def test_conditions_respects_status_conditions() -> None: node_conditions = _conditions(api_node) assert node_conditions is not None - conditions_dict = node_conditions.dict() + conditions_dict = node_conditions.model_dump() assert len(conditions_dict) == len(native_conditions) assert all( conditions_dict[condition.type_.lower()]["status"] == condition.status @@ -218,9 +218,9 @@ def test_api_node_container_count( ) node_container_count = _container_count(api_node) assert isinstance(node_container_count, section.ContainerCount) - assert node_container_count.dict()[container_status_state.value] == pod_containers_count + assert node_container_count.model_dump()[container_status_state.value] == pod_containers_count assert all( count == 0 - for state, count in node_container_count.dict().items() + for state, count in node_container_count.model_dump().items() if state != container_status_state.value ) diff --git a/tests/unit/cmk/special_agents/test_agent_aws_status.py b/tests/unit/cmk/special_agents/test_agent_aws_status.py index 692e875a349..808e039b716 100644 --- a/tests/unit/cmk/special_agents/test_agent_aws_status.py +++ b/tests/unit/cmk/special_agents/test_agent_aws_status.py @@ -42,7 +42,9 @@ def _get_rss() -> requests.Response: captured = capsys.readouterr() assert captured.out.split("\n") == [ "<<>>", - agent_aws_status.AgentOutput(discovery_param=discovery_param, rss_str=RSS_STR).json(), + agent_aws_status.AgentOutput( + discovery_param=discovery_param, rss_str=RSS_STR + ).model_dump_json(), "", ] assert captured.err == "" diff --git a/tests/unit/cmk/special_agents/test_agent_datadog.py b/tests/unit/cmk/special_agents/test_agent_datadog.py index 47b1376ca18..38fa907e815 100644 --- a/tests/unit/cmk/special_agents/test_agent_datadog.py +++ b/tests/unit/cmk/special_agents/test_agent_datadog.py @@ -187,7 +187,7 @@ def fixture_datadog_api( ) -> MockDatadogAPI: return MockDatadogAPI( page_to_data={ - 0: {"events": [event.dict() for event in events]}, + 0: {"events": [event.model_dump() for event in events]}, 1: {"events": []}, } ) @@ -323,11 +323,11 @@ def fixture_datadog_api( return MockDatadogAPI( page_to_data={ None: { - "data": [log.dict() for log in logs[:3]], + "data": [log.model_dump() for log in logs[:3]], "meta": {"page": {"after": "next"}}, }, "next": { - "data": [log.dict() for log in logs[3:]], + "data": [log.model_dump() for log in logs[3:]], }, } ) diff --git a/tests/unit/cmk/special_agents/test_agent_gcp_status.py b/tests/unit/cmk/special_agents/test_agent_gcp_status.py index 12af4098cbe..1c18ae116bc 100644 --- a/tests/unit/cmk/special_agents/test_agent_gcp_status.py +++ b/tests/unit/cmk/special_agents/test_agent_gcp_status.py @@ -25,5 +25,5 @@ def _health_info() -> str: lines = captured.out.rstrip().split("\n") # Assert assert lines[0] == "<<>>" - assert lines[1] == output.json() + assert lines[1] == output.model_dump_json() assert len(lines) == 2 diff --git a/tests/unit/cmk/update_config/plugins/actions/test_prediction_files_cleanup.py b/tests/unit/cmk/update_config/plugins/actions/test_prediction_files_cleanup.py index ee849c3463d..7cc6968905f 100644 --- a/tests/unit/cmk/update_config/plugins/actions/test_prediction_files_cleanup.py +++ b/tests/unit/cmk/update_config/plugins/actions/test_prediction_files_cleanup.py @@ -27,7 +27,7 @@ def test_ok_files_are_kept(tmp_path: Path) -> None: range=(23, 42), dsname="kuchen_count", params=PredictionParameters(horizon=3, period="wday"), - ).json(), + ).model_dump_json(), ) data_file.write_text( PredictionData( @@ -37,7 +37,7 @@ def test_ok_files_are_kept(tmp_path: Path) -> None: ], data_twindow=[1, 10], step=2, - ).json(), + ).model_dump_json(), ) RemoveUnreadablePredictions.cleanup_unreadable_files(tmp_path) @@ -59,7 +59,7 @@ def test_corrupt_files_are_removed(tmp_path: Path) -> None: ], data_twindow=[1, 10], step=2, - ).json(), + ).model_dump_json(), ) RemoveUnreadablePredictions.cleanup_unreadable_files(tmp_path) diff --git a/tests/unit/cmk/utils/prediction/test_query.py b/tests/unit/cmk/utils/prediction/test_query.py index fb2f2b7d473..3b11d58dfd6 100644 --- a/tests/unit/cmk/utils/prediction/test_query.py +++ b/tests/unit/cmk/utils/prediction/test_query.py @@ -45,7 +45,7 @@ def test_query_available_predictions(self, mock_livestatus: MockLiveStatusConnec "other_metric/everyday.info", "other_metric/everyday", ], - f"prediction_file:file:{querier.metric_name}/everyday.info": expected_prediction_info.json().encode(), + f"prediction_file:file:{querier.metric_name}/everyday.info": expected_prediction_info.model_dump_json().encode(), } ], site=SiteName("local"), @@ -87,7 +87,7 @@ def test_query_prediction_data(self, mock_livestatus: MockLiveStatusConnection) { "host_name": str(querier.host_name), "description": str(querier.service_name), - f"prediction_file:file:{querier.metric_name}/everyday": expected_prediction_data.json().encode(), + f"prediction_file:file:{querier.metric_name}/everyday": expected_prediction_data.model_dump_json().encode(), } ], site=SiteName("local"), diff --git a/tests/unit/cmk/utils/prediction/test_services_prediction.py b/tests/unit/cmk/utils/prediction/test_services_prediction.py index 351b89e8faa..22adc652333 100644 --- a/tests/unit/cmk/utils/prediction/test_services_prediction.py +++ b/tests/unit/cmk/utils/prediction/test_services_prediction.py @@ -193,7 +193,7 @@ def test_calculate_data_for_prediction( data_for_pred = _prediction._calculate_data_for_prediction(raw_slices) - expected_reference = _prediction.PredictionData.parse_raw( + expected_reference = _prediction.PredictionData.model_validate_json( ( repo_path() / "tests/unit/cmk/utils/prediction/test-files/output" @@ -202,7 +202,9 @@ def test_calculate_data_for_prediction( ).read_text() ) - assert expected_reference.dict(exclude={"points"}) == data_for_pred.dict(exclude={"points"}) + assert expected_reference.model_dump(exclude={"points"}) == data_for_pred.model_dump( + exclude={"points"} + ) assert len(expected_reference.points) == len(data_for_pred.points) for cal, ref in zip(data_for_pred.points, expected_reference.points): assert cal == pytest.approx(ref, rel=1e-12, abs=1e-12)