From 1450b5205905ee700df748b5aaa381f7f28aaae9 Mon Sep 17 00:00:00 2001 From: Dynatrace-Jeroen-Hautekeete Date: Mon, 9 Feb 2026 23:14:31 +0100 Subject: [PATCH 1/5] Tag and MZ source enrichment https://docs.dynatrace.com/docs/whats-new/dynatrace-api/sprint-323 --- dynatrace/environment_v2/custom_tags.py | 16 ++++++++++++++++ dynatrace/environment_v2/monitored_entities.py | 8 ++++---- dynatrace/environment_v2/schemas.py | 6 +++++- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/dynatrace/environment_v2/custom_tags.py b/dynatrace/environment_v2/custom_tags.py index 473825c..1269f28 100644 --- a/dynatrace/environment_v2/custom_tags.py +++ b/dynatrace/environment_v2/custom_tags.py @@ -140,3 +140,19 @@ class TagContext(Enum): def __str__(self) -> str: return self.value + + +class EnrichedTagDto(METag): + def _create_from_raw_data(self, raw_element: Dict[str, Any]): + super(raw_element) + self.source: Optional[TagSource] = ( TagSource(raw_element.get("source")) if "source" in raw_element.keys() else None ) + self.source_setting: Optional[str] = raw_element.get("sourceSetting") + + +class TagSource(Enum): + USER = "User provided tags" + AUTO = "Auto tags" + ENVT = "Environment tags" + + def __str__(self) -> str: + return self.value diff --git a/dynatrace/environment_v2/monitored_entities.py b/dynatrace/environment_v2/monitored_entities.py index 91128d6..7cc02db 100644 --- a/dynatrace/environment_v2/monitored_entities.py +++ b/dynatrace/environment_v2/monitored_entities.py @@ -21,8 +21,8 @@ from requests import Response from dynatrace.dynatrace_object import DynatraceObject -from dynatrace.environment_v2.custom_tags import METag -from dynatrace.environment_v2.schemas import ManagementZone +from dynatrace.environment_v2.custom_tags import EnrichedTagDto +from dynatrace.environment_v2.schemas import EnrichedManagementZoneDto from dynatrace.http_client import HttpClient from dynatrace.pagination import PaginatedList from dynatrace.utils import int64_to_datetime, timestamp_to_string @@ -189,7 +189,7 @@ def _create_from_raw_data(self, raw_element: Dict[str, Any]): key: [EntityId(raw_element=entity) for entity in entities] for key, entities in raw_element.get("toRelationships", {}).items() } - self.management_zones: List[ManagementZone] = [ManagementZone(raw_element=m) for m in + self.management_zones: List[EnrichedManagementZoneDto] = [EnrichedManagementZoneDto(raw_element=m) for m in raw_element.get("managementZones", [])] self.icon: Optional[ EntityIcon] = EntityIcon(raw_element=raw_element.get("icon")) if raw_element.get("icon") else None @@ -197,7 +197,7 @@ def _create_from_raw_data(self, raw_element: Dict[str, Any]): self.type: str | None = raw_element.get('type') self.entity_id: str = raw_element["entityId"] self.properties: Optional[Dict[str, Any]] = raw_element.get("properties", {}) - self.tags: List[METag] = [METag(raw_element=tag) for tag in raw_element.get("tags", [])] + self.tags: List[EnrichedTagDto] = [EnrichedTagDto(raw_element=tag) for tag in raw_element.get("tags", [])] class EntityShortRepresentation(DynatraceObject): diff --git a/dynatrace/environment_v2/schemas.py b/dynatrace/environment_v2/schemas.py index fd254c5..6eb001b 100644 --- a/dynatrace/environment_v2/schemas.py +++ b/dynatrace/environment_v2/schemas.py @@ -49,4 +49,8 @@ def _create_from_raw_data(self, raw_element: Dict[str, Any]): def to_json(self) -> Dict[str, Any]: return {"name": self.name, "id": self.id} - + +class EnrichedManagementZoneDto(ManagementZone): + def _create_from_raw_data(self, raw_element: Dict[str, Any]): + super(raw_element) + self.source_setting: Optional[str] = raw_element.get("sourceSetting") From 30a6960cceeaaef8f74f8de37baff46fb7e3ecbe Mon Sep 17 00:00:00 2001 From: Dynatrace-Jeroen-Hautekeete Date: Thu, 21 May 2026 17:13:18 +0200 Subject: [PATCH 2/5] Fix super usage --- dynatrace/environment_v2/custom_tags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dynatrace/environment_v2/custom_tags.py b/dynatrace/environment_v2/custom_tags.py index 1269f28..9693141 100644 --- a/dynatrace/environment_v2/custom_tags.py +++ b/dynatrace/environment_v2/custom_tags.py @@ -144,7 +144,7 @@ def __str__(self) -> str: class EnrichedTagDto(METag): def _create_from_raw_data(self, raw_element: Dict[str, Any]): - super(raw_element) + super()._create_from_raw_data(raw_element) self.source: Optional[TagSource] = ( TagSource(raw_element.get("source")) if "source" in raw_element.keys() else None ) self.source_setting: Optional[str] = raw_element.get("sourceSetting") From 16566906f7bf5d03f13d5fddc10610f2257c699c Mon Sep 17 00:00:00 2001 From: Dynatrace-Jeroen-Hautekeete Date: Thu, 21 May 2026 17:33:20 +0200 Subject: [PATCH 3/5] Fix super usage --- dynatrace/environment_v2/schemas.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dynatrace/environment_v2/schemas.py b/dynatrace/environment_v2/schemas.py index 6eb001b..689b00e 100644 --- a/dynatrace/environment_v2/schemas.py +++ b/dynatrace/environment_v2/schemas.py @@ -52,5 +52,5 @@ def to_json(self) -> Dict[str, Any]: class EnrichedManagementZoneDto(ManagementZone): def _create_from_raw_data(self, raw_element: Dict[str, Any]): - super(raw_element) + super()._create_from_raw_data(raw_element) self.source_setting: Optional[str] = raw_element.get("sourceSetting") From 322218ffafb154da28363ec03be70940d4bf74bd Mon Sep 17 00:00:00 2001 From: Dynatrace-Jeroen-Hautekeete Date: Tue, 26 May 2026 23:31:55 +0200 Subject: [PATCH 4/5] OneAgent on a host API Completed implementation of missing fields --- dynatrace/environment_v1/oneagents.py | 368 +++++++++++++++++-- dynatrace/environment_v1/smartscape_hosts.py | 1 + 2 files changed, 342 insertions(+), 27 deletions(-) diff --git a/dynatrace/environment_v1/oneagents.py b/dynatrace/environment_v1/oneagents.py index ffa77e0..d9268f9 100644 --- a/dynatrace/environment_v1/oneagents.py +++ b/dynatrace/environment_v1/oneagents.py @@ -23,10 +23,9 @@ from dynatrace.http_client import HttpClient from dynatrace.utils import datetime_to_int64 - -class ConfiguredMonitoringMode(Enum): - CLOUD_INFRASTRUCTURE = "CLOUD_INFRASTRUCTURE" - FULL_STACK = "FULL_STACK" +from dynatrace.environment_v1.smartscape_hosts import AgentVersion, HostGroup, MonitoringMode, OSArchitecture, TagInfo +from dynatrace.environment_v2.custom_tags import METag +from dynatrace.environment_v2.monitored_entities import EntityShortRepresentation class UpdateStatus(Enum): @@ -44,6 +43,7 @@ class UpdateStatus(Enum): class MonitoringType(Enum): CLOUD_INFRASTRUCTURE = "CLOUD_INFRASTRUCTURE" + DISCOVERY = "DISCOVERY" FULL_STACK = "FULL_STACK" STANDALONE = "STANDALONE" @@ -72,6 +72,163 @@ class OsType(Enum): ZOS = "ZOS" +class CloudType(Enum): + AZURE = "AZURE" + EC2 = "EC2" + GOOGLE_CLOUD_PLATFORM = "GOOGLE_CLOUD_PLATFORM" + OPENSTACK = "OPENSTACK" + ORACLE = "ORACLE" + UNRECOGNIZED = "UNRECOGNIZED" + + +class AutoInjection(Enum): + DISABLED_MANUALLY = "DISABLED_MANUALLY" + DISABLED_ON_INSTALLATION = "DISABLED_ON_INSTALLATION" + DISABLED_ON_SANITY_CHECK = "DISABLED_ON_SANITY_CHECK" + ENABLED = "ENABLED" + FAILED_ON_INSTALLATION = "FAILED_ON_INSTALLATION" + + +class DetailedAvailabilityState(Enum): + CRASHED_FAILURE = "CRASHED_FAILURE" + CRASHED_UNKNOWN = "CRASHED_UNKNOWN" + LOST_AGENT_UPGRADE_FAILED = "LOST_AGENT_UPGRADE_FAILED" + LOST_CONNECTION = "LOST_CONNECTION" + LOST_UNKNOWN = "LOST_UNKNOWN" + MONITORED = "MONITORED" + MONITORED_AGENT_ENABLED = "MONITORED_AGENT_ENABLED" + MONITORED_AGENT_REGISTERED = "MONITORED_AGENT_REGISTERED" + MONITORED_AGENT_UPGRADE_STARTED = "MONITORED_AGENT_UPGRADE_STARTED" + MONITORED_AGENT_VERSION_ACCEPTED = "MONITORED_AGENT_VERSION_ACCEPTED" + MONITORED_ENABLED = "MONITORED_ENABLED" + PRE_MONITORED = "PRE_MONITORED" + SHUTDOWN_AGENT_LOST = "SHUTDOWN_AGENT_LOST" + SHUTDOWN_GRACEFUL = "SHUTDOWN_GRACEFUL" + SHUTDOWN_K8S_NODE_SHUTDOWN = "SHUTDOWN_K8S_NODE_SHUTDOWN" + SHUTDOWN_SPOT_INSTANCE = "SHUTDOWN_SPOT_INSTANCE" + SHUTDOWN_STOPPED = "SHUTDOWN_STOPPED" + SHUTDOWN_UNKNOWN = "SHUTDOWN_UNKNOWN" + SHUTDOWN_UNKNOWN_UNEXPECTED = "SHUTDOWN_UNKNOWN_UNEXPECTED" + UNKNOWN = "UNKNOWN" + UNMONITORED_AGENT_DISABLED = "UNMONITORED_AGENT_DISABLED" + UNMONITORED_AGENT_LOST = "UNMONITORED_AGENT_LOST" + UNMONITORED_AGENT_MIGRATED = "UNMONITORED_AGENT_MIGRATED" + UNMONITORED_AGENT_RESTART_TRIGGERED = "UNMONITORED_AGENT_RESTART_TRIGGERED" + UNMONITORED_AGENT_STOPPED = "UNMONITORED_AGENT_STOPPED" + UNMONITORED_AGENT_UNINSTALLED = "UNMONITORED_AGENT_UNINSTALLED" + UNMONITORED_AGENT_UNREGISTERED = "UNMONITORED_AGENT_UNREGISTERED" + UNMONITORED_AGENT_UPGRADE_FAILED = "UNMONITORED_AGENT_UPGRADE_FAILED" + UNMONITORED_AGENT_VERSION_REJECTED = "UNMONITORED_AGENT_VERSION_REJECTED" + UNMONITORED_DISABLED = "UNMONITORED_DISABLED" + UNMONITORED_ID_CHANGED = "UNMONITORED_ID_CHANGED" + UNMONITORED_TERMINATED = "UNMONITORED_TERMINATED" + UNMONITORED_UNKNOWN = "UNMONITORED_UNKNOWN" + + +class VersionIs(Enum): + EQUAL = "EQUAL" + GREATER = "GREATER" + GREATER_EQUAL = "GREATER_EQUAL" + LOWER = "LOWER" + LOWER_EQUAL = "LOWER_EQUAL" + + +class TechnologyModuleType(Enum): + APACHE = "APACHE" + DOT_NET = "DOT_NET" + DUMPPROC = "DUMPPROC" + GO = "GO" + IBM_INTEGRATION_BUS = "IBM_INTEGRATION_BUS" + IIS = "IIS" + JAVA = "JAVA" + LOG_ANALYTICS = "LOG_ANALYTICS" + NETTRACER = "NETTRACER" + NETWORK = "NETWORK" + NGINX = "NGINX" + NODE_JS = "NODE_JS" + OPENTRACINGNATIVE = "OPENTRACINGNATIVE" + PHP = "PHP" + PROCESS = "PROCESS" + PYTHON = "PYTHON" + RUBY = "RUBY" + SDK = "SDK" + UPDATER = "UPDATER" + VARNISH = "VARNISH" + Z_OS = "Z_OS" + + +class PluginState(Enum): + DISABLED = "DISABLED" + ERROR_AUTH = "ERROR_AUTH" + ERROR_COMMUNICATION_FAILURE = "ERROR_COMMUNICATION_FAILURE" + ERROR_CONFIG = "ERROR_CONFIG" + ERROR_TIMEOUT = "ERROR_TIMEOUT" + ERROR_UNKNOWN = "ERROR_UNKNOWN" + INCOMPATIBLE = "INCOMPATIBLE" + LIMIT_REACHED = "LIMIT_REACHED" + NOTHING_TO_REPORT = "NOTHING_TO_REPORT" + OK = "OK" + STATE_TYPE_UNKNOWN = "STATE_TYPE_UNKNOWN" + UNINITIALIZED = "UNINITIALIZED" + UNSUPPORTED = "UNSUPPORTED" + WAITING_FOR_STATE = "WAITING_FOR_STATE" + + +class AzureComputeModeName(Enum): + DEDICATED = "DEDICATED" + SHARED = "SHARED" + + +class AzureSku(Enum): + BASIC = "BASIC" + DYNAMIC = "DYNAMIC" + FREE = "FREE" + PREMIUM = "PREMIUM" + SHARED = "SHARED" + STANDARD = "STANDARD" + + +class Bitness(Enum): + x32bit = "32bit" + x64bit = "64bit" + + +class HypervisorType(Enum): + AHV = "AHV" + AWS_NITRO = "AWS_NITRO" + GVISOR = "GVISOR" + HYPERV = "HYPERV" + KVM = "KVM" + LPAR = "LPAR" + QEMU = "QEMU" + UNRECOGNIZED = "UNRECOGNIZED" + VIRTUALBOX = "VIRTUALBOX" + VMWARE = "VMWARE" + WPAR = "WPAR" + XEN = "XEN" + + +class PaasType(Enum): + AWS_ECS_EC2 = "AWS_ECS_EC2" + AWS_ECS_FARGATE = "AWS_ECS_FARGATE" + AWS_LAMBDA = "AWS_LAMBDA" + AZURE_FUNCTIONS = "AZURE_FUNCTIONS" + AZURE_WEBSITES = "AZURE_WEBSITES" + CLOUD_FOUNDRY = "CLOUD_FOUNDRY" + GOOGLE_APP_ENGINE = "GOOGLE_APP_ENGINE" + GOOGLE_CLOUD_RUN = "GOOGLE_CLOUD_RUN" + HEROKU = "HEROKU" + KUBERNETES = "KUBERNETES" + OPENSHIFT = "OPENSHIFT" + + +class UserLevel(Enum): + NON_SUPERUSER = "NON_SUPERUSER" + NON_SUPERUSER_STRICT = "NON_SUPERUSER_STRICT" + SUPERUSER = "SUPERUSER" + + +# https://docs.dynatrace.com/docs/dynatrace-api/environment-api/oneagent-on-a-host/get-all-hosts-with-oneagents class OneAgentOnAHostService: def __init__(self, http_client: HttpClient): self.__http_client = http_client @@ -84,16 +241,32 @@ def list( relative_time: Optional[str] = None, tag: Optional[List[str]] = None, entity: Optional[List[str]] = None, - mz_id: Optional[str] = None, + mz_id: Optional[str] = None, # must be parseable as integer management_zone: Optional[str] = None, network_zone_id: Optional[str] = None, host_group_id: Optional[str] = None, host_group_name: Optional[str] = None, os_type: Optional[Union[OsType, str]] = None, + cloud_type: Optional[Union[CloudType, str]] = None, + auto_injection: Optional[Union[AutoInjection, str]] = None, availability_state: Optional[Union[AvailabilityState, str]] = None, + detailed_availability_state: Optional[Union[DetailedAvailabilityState, str]] = None, monitoring_type: Optional[Union[MonitoringType, str]] = None, + agent_version_is: Optional[Union[VersionIs, str]] = None, + agent_version_number: Optional[str] = None, auto_update: Optional[Union[AutoUpdate, str]] = None, update_status: Optional[Union[UpdateStatus, str]] = None, + faulty_version: Optional[bool] = None, + unlicensed: Optional[bool] = None, + activegate_id: Optional[str] = None, # Use "DIRECT_COMMUNICATION" keyword to find the hosts not connected to any ActiveGate. + technology_module_type: Optional[Union[TechnologyModuleType, str]] = None, + technology_module_version_is: Optional[Union[VersionIs, str]] = None, + technology_module_version_number: Optional[str] = None, + technology_module_faulty_version: Optional[bool] = None, + plugin_name: Optional[str] = None, + plugin_version_is: Optional[Union[VersionIs, str]] = None, + plugin_version_number: Optional[str] = None, + plugin_state: Optional[Union[PluginState, str]] = None, ) -> PaginatedList["HostAgentInfo"]: """ Lists OneAgents on a Host. Older API - timestamps are of type integer and therefore require conversion @@ -111,53 +284,194 @@ def list( "hostGroupId": host_group_id, "hostGroupName": host_group_name, "osType": OsType(os_type).value if os_type else None, + "cloudType": CloudType(cloud_type).value if cloud_type else None, + "autoInjection": AutoInjection(auto_injection).value if auto_injection else None, "availabilityState": AvailabilityState(availability_state).value if availability_state else None, + "detailedAvailabilityState": DetailedAvailabilityState(detailed_availability_state).value if detailed_availability_state else None, "monitoringType": MonitoringType(monitoring_type).value if monitoring_type else None, + "agentVersionIs": VersionIs(agent_version_is).value if agent_version_is else None, + "agentVersionNumber": agent_version_number, "autoUpdateSetting": AutoUpdate(auto_update).value if auto_update else None, "updateStatus": UpdateStatus(update_status).value if update_status else None, + "faultyVersion": faulty_version, + "unlicensed": unlicensed, + "activeGateId": activegate_id, + "technologyModuleType": TechnologyModuleType(technology_module_type).value if technology_module_type else None, + "technologyModuleVersionIs": VersionIs(technology_module_version_is).value if technology_module_version_is else None, + "technologyModuleVersionNumber": technology_module_version_number, + "technologyModuleFaultyVersion": technology_module_faulty_version, + "pluginName": plugin_name, + "pluginVersionIs": VersionIs(plugin_version_is).value if plugin_version_is else None, + "pluginVersionNumber": plugin_version_number, + "pluginState": plugin_state, } return PaginatedList(HostAgentInfo, self.__http_client, "/api/v1/oneagents", params, list_item="hosts") -# todo - create class objects for ModuleInfo[] and PluginInfo[] class HostAgentInfo(DynatraceObject): def _create_from_raw_data(self, raw_element): - self.host_info: HostInfo = HostInfo(raw_element=raw_element.get("hostInfo")) - self.faulty_version: bool = raw_element.get("faultyVersion") self.active: bool = raw_element.get("active") - self.configured_monitoring_mode: ConfiguredMonitoringMode = ConfiguredMonitoringMode(raw_element.get("configuredMonitoringMode")) - self.monitoring_type: MonitoringType = MonitoringType(raw_element.get("monitoringType")) self.auto_update: AutoUpdate = AutoUpdate(raw_element.get("autoUpdateSetting")) - self.update_status: UpdateStatus = UpdateStatus(raw_element.get("updateStatus")) + self.availability_state: AvailabilityState = AvailabilityState(raw_element.get("availabilityState")) self.available_versions: str = raw_element.get("availableVersions", []) self.config_monitoring_enabled: bool = raw_element.get("configuredMonitoringEnabled") - self.availability_state: AvailabilityState = AvailabilityState(raw_element.get("availabilityState")) - self.activegate_id: int = raw_element.get("currentActiveGateId") + self.configured_monitoring_mode: MonitoringType = MonitoringType(raw_element.get("configuredMonitoringMode")) + self.activegate_id: Optional[int] = raw_element.get("currentActiveGateId") # DEPRECATED + self.current_activegate_ids: List[str] = raw_element.get("currentActiveGateIds") self.networkzone_id: str = raw_element.get("currentNetworkZoneId") + self.detailed_availability_state: DetailedAvailabilityState = DetailedAvailabilityState(raw_element.get("detailedAvailabilityState")) + self.faulty_version: bool = raw_element.get("faultyVersion") + self.host_info: HostInfo = HostInfo(raw_element=raw_element.get("hostInfo")) + self.modules: List[ModuleInfo] = [ModuleInfo(raw_element=mi) for mi in raw_element.get("modules",[])] + self.monitoring_type: Optional[MonitoringType] = MonitoringType(raw_element.get("monitoringType")) if raw_element.get("monitoringType") else None + self.plugins: List[PluginInfo] = [PluginInfo(raw_element=pi) for pi in raw_element.get("plugins",[])] + self.unlicensed: bool = raw_element.get("unlicensed") + self.update_status: UpdateStatus = UpdateStatus(raw_element.get("updateStatus")) -# todo - incomplete + firstSeenTimestamp is of type integer, how do we work with that here? class HostInfo(DynatraceObject): def _create_from_raw_data(self, raw_element): - self.entity_id: str = raw_element.get("entityId") - self.display_name: str = raw_element.get("displayName") - self.discovered_name: str = raw_element.get("discoveredName") + self.agent_version: AgentVersion = AgentVersion(raw_element.get("agentVersion")) + self.ami_id: Optional[str] = raw_element.get("amiId") + self.auto_injection: Optional[AutoInjection] = AutoInjection(raw_element.get("autoInjection")) if raw_element.get("autoInjection") else None + self.auto_scaling_group: Optional[str] = raw_element.get("autoScalingGroup") + self.aws_instance_id: Optional[str] = raw_element.get("awsInstanceId") + self.aws_instance_type: Optional[str] = raw_element.get("awsInstanceType") + self.aws_name_tag: Optional[str] = raw_element.get("awsNameTag") + self.aws_security_group: Optional[List[str]] = raw_element.get("awsSecurityGroup") + self.azure_compute_mode_name: Optional[AzureComputeModeName] = AzureComputeModeName(raw_element.get("azureComputeModeName")) if raw_element.get("azureComputeModeName") else None + self.azure_environment: Optional[str] = raw_element.get("azureEnvironment") + self.azure_host_names: Optional[List[str]] = raw_element.get("azureHostNames") + self.azure_resource_group_namet: Optional[str] = raw_element.get("azureResourceGroupName") + self.azure_resourceId: Optional[str] = raw_element.get("azureResourceId") + self.azure_site_names: Optional[List[str]] = raw_element.get("azureSiteNames") + self.azure_sku: Optional[AzureSku] = AzureSku(raw_element.get("azureSku")) if raw_element.get("azureSku") else None + self.azure_vm_name: Optional[str] = raw_element.get("azureVmName") + self.azure_vm__scale_set_name: Optional[str] = raw_element.get("azureVmScaleSetName") + self.azure_vm_size_label: Optional[str] = raw_element.get("azureVmSizeLabel") + self.azure_zone: Optional[str] = raw_element.get("azureZone") + + self.beanstalk_environment_name: Optional[str] = raw_element.get("beanstalkEnvironmentName") + self.bitness: Optional[Bitness] = Bitness(raw_element.get("bitness")) if raw_element.get("bitness") else None + self.bosh_availability_zone: Optional[str] = raw_element.get("boshAvailabiityZone") + self.bosh_deployment_id: Optional[str] = raw_element.get("boshDeploymentId") + self.bosh_instance_id: Optional[str] = raw_element.get("boshInstanceId") + self.bosh_instance_name: Optional[str] = raw_element.get("boshInstanceName") + self.bosh_name: Optional[str] = raw_element.get("boshName") + self.bosh_stem_cell_version: Optional[str] = raw_element.get("boshStemCellVersion") + + self.cloud_platform_verndorversion: Optional[str] = raw_element.get("cloudPlatformVendorVersion") + self.cloud_type: Optional[CloudType] = CloudType(raw_element.get("cloudType")) if raw_element.get("cloudTupe") else None self.consumed_host_units: str = raw_element.get("consumedHostUnits") - self.os_version: str = raw_element.get("osVersion") + self.cpu_cores: int = raw_element.get("cpuCores") + self.customized_name: str = raw_element.get("customizedName") + + self.discovered_name: str = raw_element.get("discoveredName") + self.display_name: str = raw_element.get("displayName") + + self.entity_id: str = raw_element.get("entityId") + self.esxi_host_name: Optional[str] = raw_element.get("esxiHostName") + + self.first_seen_timestamp: int = raw_element.get("firstSeenTimestamp") + self.from_relationships: Optional[any] = raw_element.get("fromRelationships") + + self.gce_instance_id: Optional[str] = raw_element.get("gceInstanceId") + self.gce_instance_name: Optional[str] = raw_element.get("gceInstanceName") + self.gce_machine_type: Optional[str] = raw_element.get("gceMachineType") + self.gce_project: Optional[str] = raw_element.get("gceProject") + self.gce_project_id: Optional[str] = raw_element.get("gceProjectId") + self.gce_public_ipaddresses: Optional[List[str]] = raw_element.get("gcePublicIpAddresses") + self.gcp_zone: Optional[str] = raw_element.get("gcpZone") + self.host_group: HostGroup = HostGroup(raw_element=raw_element.get("hostGroup")) - self.tags: List[TagInfo] = [TagInfo(raw_element=t) for t in raw_element.get("tags", [])] + self.hypervisor_type: Optiona[HypervisorType] = HypervisorType(raw_element.get("hypervisorType")) if raw_element.get("hypervisorType") else None + + self.ip_addresses: Optional[List[str]] = raw_element.get("ipAddresses") + self.is_monitoring_candidate: bool = raw_element.get("isMonitoringCandidate") + + self.kubernetes_cluster: Optional[str] = raw_element.get("kubernetesCluster") + self.kubernetes_labels: Optional[any] = raw_element.get("kubernetesLabels") + self.kubernetes_node: Optional[str] = raw_element.get("kubernetesNode") + + self.last_seen_timestamp: int = raw_element.get("lastSeenTimestamp") + self.local_host_name: str = raw_element.get("localHostName") + self.local_ip: str = raw_element.get("localIp") + + self.logical_cpu_cores: int = raw_element.get("logicalCpuCores") + self.logical_cpus: int = raw_element.get("logicalCpus") + + self.management_zones: List[EntityShortRepresentation] = [EntityShortRepresentation(raw_element=esr) for esr in raw_element.get("managementZones",[])] + self.monitoring_mode: MonitoringMode = MonitoringMode(raw_element.get("monitoringMode")) + + self.network_zone_id: str = raw_element.get("networkZoneId") + + self.one_agent_custom_host_name: str = raw_element.get("oneAgentCustomHostName") + self.openstack_instance_type: str = raw_element.get("openStackInstanceType") + self.openstack_av_zone: str = raw_element.get("openstackAvZone") + self.openstack_compute_node_name: str = raw_element.get("openstackComputeNodeName") + self.openstack_project_name: str = raw_element.get("openstackProjectName") + self.openstack_security_groups: str = raw_element.get("openstackSecurityGroups") + self.openstack_vm_name: str = raw_element.get("openstackVmName") + self.os_architecture: Optional[OSArchitecture] = OSArchitecture(raw_element.get("osArchitecture")) if raw_element.get("osArchitecture") else None self.os_type: Optional[OsType] = OsType(raw_element.get("osType")) if raw_element.get("osType") else None + self.os_version: str = raw_element.get("osVersion") + + self.paas_agent_versions : List[AgentVersion] = [AgentVersion(raw_element=pav) for pav in raw_element.get("paasAgentVersions",[])] + self.paas_memory_limit: Optional[int] = raw_element.get("paasMemoryLimit") + self.paas_type: Optional[PaasType] = PaasType(raw_element.get("paasType")) if raw_element.get("paasType") else None + self.public_host_name: str = raw_element.get("publicHostName") + self.public_ip: str = raw_element.get("publicIp") + + self.scale_set_name: str = raw_element.get("scaleSetName") + self.simultaneous_multi_threading: Optional[int] = raw_element.get("simultaneousMultiThreading") + self.software_technologies: List[TechnologyInfo] = [TechnologyInfo(raw_element=ti) for ti in raw_element.get("softwareTechnologies",[])] + + self.tags: List[METag] = [METag(raw_element=t) for t in raw_element.get("tags", [])] + self.to_relationships: Optional[any] = raw_element.get("toRelationships") + + self.user_level: Optional[UserLevel] = UserLevel(raw_element.get("userLevel")) if raw_element.get("userLevel") else None + self.virtual_cpus: Optional[int] = raw_element.get("virtualCpus") + self.vmware_name: Optional[str] = raw_element.get("vmwareName") -class HostGroup(DynatraceObject): + self.zos_cpu_model_number: Optional[str] = raw_element.get("zosCPUModelNumber") + self.zos_cpu_serial_number: Optional[str] = raw_element.get("zosCPUSerialNumber") + self.zos_lpa_name: Optional[str] = raw_element.get("zosLpaName") + self.zos_system_name: Optional[str] = raw_element.get("zosSystemName") + self.zos_total_general_purpose_processors: Optional[int] = raw_element.get("zosTotalGeneralPurposeProcessors") + self.zos_total_physical_memory: Optional[int] = raw_element.get("zosTotalPhysicalMemory") + self.zos_total_ziip_processors: Optional[int] = raw_element.get("zosTotalZiipProcessors") + self.zos_virtualization: Optional[str] = raw_element.get("zosVirtualization") + + +class TechnologyInfo(DynatraceObject): + def _create_from_raw_data(self, raw_element): + self.edition: str = raw_element.get("edition") + self.type: str = raw_element.get("type") + self.version: str = raw_element.get("version") + + +class ModuleInfo(DynatraceObject): + def _create_from_raw_data(self, raw_element): + self.instances: List[ModuleInstance] = [ModuleInstance(raw_element=i) for i in raw_element.get("instances",[])] + self.module_type: str = raw_element.get("moduleType") + + +class ModuleInstance(DynatraceObject): + def _create_from_raw_data(self, raw_element): + self.active: bool = raw_element.get("active") + self.faulty_version: bool = raw_element.get("faulty_Version") + self.instance_name: str = raw_element.get("instanceName") + self.module_version: str = raw_element.get("moduleVersion") + + +class PluginInfo(DynatraceObject): def _create_from_raw_data(self, raw_element): - self.me_id: str = raw_element.get("meId") - self.name: str = raw_element.get("name") + self.instances: List[PluginInstance] = [PluginInstance(raw_element=i) for i in raw_element.get("instances",[])] + self.plugin_name: str = raw_element.get("pluginName") -class TagInfo(DynatraceObject): - # todo - convert context to Enum +class PluginInstance(DynatraceObject): def _create_from_raw_data(self, raw_element): - self.context: str = raw_element.get("context") - self.key: str = raw_element.get("key") - self.value: str = raw_element.get("value") + self.plugin_version: str = raw_element.get("pluginVersion") + self.state: PluginState = PluginState(raw_element.get("state")) diff --git a/dynatrace/environment_v1/smartscape_hosts.py b/dynatrace/environment_v1/smartscape_hosts.py index b4da55e..dc62dad 100644 --- a/dynatrace/environment_v1/smartscape_hosts.py +++ b/dynatrace/environment_v1/smartscape_hosts.py @@ -48,6 +48,7 @@ class OSArchitecture(Enum): class MonitoringMode(Enum): + DISCOVERY = "DISCOVERY" FULL_STACK = "FULL_STACK" INFRASTRUCTURE = "INFRASTRUCTURE" OFF = "OFF" From 55cee2504c9f13815365e09587f9fa5f7b6f754e Mon Sep 17 00:00:00 2001 From: Dynatrace-Jeroen-Hautekeete Date: Tue, 26 May 2026 23:52:35 +0200 Subject: [PATCH 5/5] Fix SonarCloud suggestion --- dynatrace/environment_v2/custom_tags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dynatrace/environment_v2/custom_tags.py b/dynatrace/environment_v2/custom_tags.py index 9693141..48148b0 100644 --- a/dynatrace/environment_v2/custom_tags.py +++ b/dynatrace/environment_v2/custom_tags.py @@ -145,7 +145,7 @@ def __str__(self) -> str: class EnrichedTagDto(METag): def _create_from_raw_data(self, raw_element: Dict[str, Any]): super()._create_from_raw_data(raw_element) - self.source: Optional[TagSource] = ( TagSource(raw_element.get("source")) if "source" in raw_element.keys() else None ) + self.source: Optional[TagSource] = TagSource(raw_element.get("source")) if raw_element.get("source") else None self.source_setting: Optional[str] = raw_element.get("sourceSetting")