From e6add58be72ccfa192bfdaef74d26333371d5f55 Mon Sep 17 00:00:00 2001
From: "th.l" <thl-cmk@outlook.com>
Date: Mon, 22 Jul 2024 21:26:33 +0200
Subject: [PATCH] update project

---
 README.md                                     |   2 +-
 mkp/vsphere_topo-0.0.5-20240717.mkp           | Bin 0 -> 7740 bytes
 .../vsphere_topo/agent_based/packages.py      | 137 ++++++------------
 .../vsphere_topo/constants.py                 |  43 ++++++
 .../vsphere_topo/lib/utils.py                 | 129 ++++++++++++-----
 .../vsphere_topo/rulesets/packages.py         |   2 +-
 source/packages/vsphere_topo                  |   7 +-
 7 files changed, 187 insertions(+), 133 deletions(-)
 create mode 100644 mkp/vsphere_topo-0.0.5-20240717.mkp
 create mode 100644 source/cmk_addons_plugins/vsphere_topo/constants.py

diff --git a/README.md b/README.md
index 63d5cc5..76338db 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[PACKAGE]: ../../raw/master/mkp/vsphere_topo-0.0.5-20240714.mkp "vsphere_topo-0.0.5-20240714.mkp"
+[PACKAGE]: ../../raw/master/mkp/vsphere_topo-0.0.5-20240717.mkp "vsphere_topo-0.0.5-20240717.mkp"
 # vSphere Topology Visualization
 
 This plugin uses the data from the _VMware ESX via vSphere_ special agent to create a topology of the vSphere environment.\
diff --git a/mkp/vsphere_topo-0.0.5-20240717.mkp b/mkp/vsphere_topo-0.0.5-20240717.mkp
new file mode 100644
index 0000000000000000000000000000000000000000..736c43e65fad350c19a369c5a616d20d990612d9
GIT binary patch
literal 7740
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

literal 0
HcmV?d00001

diff --git a/source/cmk_addons_plugins/vsphere_topo/agent_based/packages.py b/source/cmk_addons_plugins/vsphere_topo/agent_based/packages.py
index dfb19c1..f03291e 100644
--- a/source/cmk_addons_plugins/vsphere_topo/agent_based/packages.py
+++ b/source/cmk_addons_plugins/vsphere_topo/agent_based/packages.py
@@ -16,10 +16,11 @@
 #             -> obsoletes 'Don\'t add vCenter as VM'
 # 2024-07-12: added support for Queried host is a host system
 # 2024-07-13: refactoring save topology/make default and add dummy's logic
+# 2024-07-16: moved add_host/add_service/add_connection to lib/utils/TopoObjects/TopoConnections
+#             moved ruleset name constants to utils/ruleset_names.py
 
 from collections.abc import Mapping, Sequence
 from time import time_ns
-from typing import Any
 
 from cmk.agent_based.v2 import (
     CheckPlugin,
@@ -32,13 +33,11 @@ from cmk.agent_based.v2 import (
     render,
 )
 from cmk.base.check_api import host_name
-from cmk_addons.plugins.vsphere_topo.lib.utils import (
-    BASE_TOPO_PATH,
+from cmk_addons.plugins.vsphere_topo.constants import (
     EMBLEM_CLUSTER,
     EMBLEM_DATA_CENTER,
     EMBLEM_DATA_STORE,
     ICON_VCENTER,
-    LiveStatusConnection,
     PARAM_CLUSTER,
     PARAM_DATA_CENTER,
     PARAM_DATA_STORE,
@@ -48,6 +47,13 @@ from cmk_addons.plugins.vsphere_topo.lib.utils import (
     PARAM_VCENTER,
     PARAM_VM_NAMES,
     RULE_SET_NAME_VSPHERE_TOPO,
+    TOPOLOGY_NAME,
+)
+from cmk_addons.plugins.vsphere_topo.lib.utils import (
+    BASE_TOPO_PATH,
+    LiveStatusConnection,
+    TopoConnections,
+    TopoObjects,
     add_dummy_topologies,
     adjust_name,
     get_emblem,
@@ -67,64 +73,14 @@ def discover_vsphere_topo(
 
 
 def check_vsphere_topo(
-        params: Mapping[str, Any],
+        params: Mapping[str, object],
         section_esx_vsphere_clusters: Section | None,
         section_esx_vsphere_virtual_machines: Sequence[Section] | None,
         section_esx_vsphere_datastores: Section | None,
 ) -> CheckResult:
-    def add_host(
-            host: str,
-            emblem: str | None = None,
-            icon: str | None = None,
-            link2core: bool = True,
-            obj_id_prefix: str = '',
-    ):
-        if objects.get(f'{obj_id_prefix}{host}') is not None:
-            return
-
-        metadata = {}
-        if emblem or icon:
-            metadata = {'images': {}}
-
-        if emblem is not None:
-            metadata['images'].update({'emblem': emblem})  # node image
-
-        if icon is not None:
-            metadata['images'].update({'icon': icon})  # node icon
-
-        objects[f'{obj_id_prefix}{host}'] = {
-            "name": host,
-            "link": {'core': host} if link2core else {},
-            'metadata': metadata,
-        }
-
-    def add_service(host: str, service: str, emblem: str | None = None, link2core: bool = True):
-        obj_id = f'{service}@{host}'
-        if objects.get(obj_id) is not None:
-            return
-
-        metadata = {}
-        if emblem is not None:
-            metadata = {
-                'images': {
-                    'emblem': emblem,  # node image
-                },
-            }
-
-        objects[obj_id] = {
-            "name": service,
-            "link": {'core': [host, service]} if link2core else {},
-            'metadata': metadata,
-        }
-
-    def add_connection(left: str, right: str):
-        connection = [left, right]
-        connection.sort()
-        connections.append([connection, {'line_config': {'css_styles': {'stroke-dasharray': 'unset'}}}])
-
     start_time = time_ns()
-    objects = {}
-    connections = []
+    objects = TopoObjects()
+    connections = TopoConnections()
 
     if section_esx_vsphere_clusters is not None:
         __clusters = {
@@ -136,7 +92,7 @@ def check_vsphere_topo(
         }
 
         raw_vsphere_host: str = host_name().strip()
-        add_host(
+        objects.add_host(
             host=raw_vsphere_host,
             obj_id_prefix='vc',
             icon=get_emblem(ICON_VCENTER, params.get(PARAM_VCENTER)),
@@ -145,32 +101,32 @@ def check_vsphere_topo(
 
         for cluster in section_esx_vsphere_clusters:
             data_center: str = section_esx_vsphere_clusters[cluster]['datacenter'].strip()
-            if objects.get(data_center) is None:
-                add_host(
+            if objects.topo_objects.get(data_center) is None:
+                objects.add_host(
                     host=data_center,
                     emblem=get_emblem(EMBLEM_DATA_CENTER, params.get(PARAM_DATA_CENTER)),
                     link2core=False,
                 )
-                add_connection(data_center, vsphere_host)
-            add_host(
+                connections.add_connection(data_center, vsphere_host)
+            objects.add_host(
                 host=cluster,
                 emblem=get_emblem(EMBLEM_CLUSTER, params.get(PARAM_CLUSTER)),
                 link2core=False
             )
-            add_connection(cluster, data_center)
+            connections.add_connection(cluster, data_center)
 
             host_systems: Sequence[str] = section_esx_vsphere_clusters[cluster].get('hostsystems', '').split(',')
             for host_system in host_systems:
                 esx_name: str = adjust_name(host_system, params.get(PARAM_HOST_SYSTEMS, {}))
-                add_host(host=esx_name)
-                add_connection(cluster, esx_name)
+                objects.add_host(host=esx_name)
+                connections.add_connection(cluster, esx_name)
         yield Result(
             state=State.OK,
             notice='VMware ESX via vSphere agent type of query: Queried host is the vCenter'
         )
     else:  # assuming the query type is ESXi host
         raw_vsphere_host: str = host_name().strip()
-        add_host(host=raw_vsphere_host)
+        objects.add_host(host=raw_vsphere_host)
         vsphere_host = raw_vsphere_host
         yield Result(
             state=State.OK,
@@ -207,8 +163,8 @@ def check_vsphere_topo(
         for vm in section_esx_vsphere_virtual_machines:
             vm_name: str = adjust_name(vm['vm_name'], params.get(PARAM_VM_NAMES, {}))
             host_system: str = adjust_name(vm['hostsystem'], params.get(PARAM_HOST_SYSTEMS, {}))
-            add_host(host=vm_name)
-            add_connection(vm_name, host_system)
+            objects.add_host(host=vm_name)
+            connections.add_connection(vm_name, host_system)
 
     service_prefix: str = ''
     ds_services = None
@@ -216,18 +172,18 @@ def check_vsphere_topo(
 
     if section_esx_vsphere_datastores is not None:
         data_stores_host: str = 'data_stores'  # Anchor for data stores
-        add_host(
+        objects.add_host(
             host=data_stores_host,
             link2core=False,
             emblem=get_emblem(EMBLEM_DATA_STORE, params.get(PARAM_DATA_STORE)),
         )
         for ds_name in section_esx_vsphere_datastores:
-            add_host(
+            objects.add_host(
                 host=ds_name,
                 link2core=False,
                 emblem=get_emblem(EMBLEM_DATA_STORE, params.get(PARAM_DATA_STORE)),
             )
-            add_connection(ds_name, data_stores_host)
+            connections.add_connection(ds_name, data_stores_host)
 
         if params.get(PARAM_DATA_STORE_AS_SERVICE) is True:
             query: str = (
@@ -242,12 +198,12 @@ def check_vsphere_topo(
                     service_name: str = ds_service[0]
                     service_id: str = f'{service_name}@{vsphere_host}'
                     ds_name: str = service_name.split('fs_')[-1].split('Filesystem ')[-1]
-                    add_service(
+                    objects.add_service(
                         host=vsphere_host,
                         service=service_name,
                     )
-                    add_connection(service_id, ds_name)
-                    service_prefix = service_name.split(ds_name)[0]
+                    connections.add_connection(service_id, ds_name)
+                    service_prefix: str = service_name.split(ds_name)[0]
 
     query: str = (
         'GET services\n'
@@ -258,46 +214,49 @@ def check_vsphere_topo(
     if (vm_to_data_stores := ls_connection.query(query=query)) is not None:
         for vm, data_stores in vm_to_data_stores:
             vm_name: str = adjust_name(vm, params.get(PARAM_VM_NAMES, {}))
-            if vm_name in objects:
+            if vm_name in objects.topo_objects:
                 data_stores = data_stores.split(',')
                 for data_store in data_stores:
                     ds_name = data_store.strip().split(' ')[2]
                     if ds_services is not None:
                         service_name = f'{service_prefix}{ds_name}'
                         service_id = f'{service_name}@{vsphere_host}'
-                        add_connection(service_id, vm_name)
+                        connections.add_connection(service_id, vm_name)
                     else:
-                        add_connection(ds_name, vm_name)
+                        connections.add_connection(ds_name, vm_name)
 
-    connections.sort()
-    data_set_vsphere = {
+    connections.topo_connections.sort()
+    data_set = {
         'version': 1,
-        'name': 'vSphere',
-        'objects': dict(sorted(objects.items())),
-        'connections': connections,
+        'name': TOPOLOGY_NAME,
+        'objects': dict(sorted(objects.topo_objects.items())),
+        'connections': connections.topo_connections,
     }
 
-    save_topology(data=data_set_vsphere, sub_directory=raw_vsphere_host)
+    save_topology(data=data_set, sub_directory=raw_vsphere_host)
     # workaround for backend is only picking up topologies from default folder
     add_dummy_topologies(sub_directory=raw_vsphere_host)
     # end workaround
     make_topo_default(sub_directory=raw_vsphere_host, make_default=params.get(PARAM_MAKE_DEFAULT, False))
 
-    yield Result(state=State.OK, summary=f'Objects: {len(objects)}')
-    yield Result(state=State.OK, summary=f'Connections: {len(connections)}')
-    yield Result(state=State.OK, notice=f'Written to: {BASE_TOPO_PATH}/{raw_vsphere_host}/data_vsphere.json')
+    yield Result(state=State.OK, summary=f'Objects: {len(objects.topo_objects)}')
+    yield Result(state=State.OK, summary=f'Connections: {len(connections.topo_connections)}')
+    yield Result(
+        state=State.OK,
+        notice=f'Written to: {BASE_TOPO_PATH}/{raw_vsphere_host}/data_{TOPOLOGY_NAME.lower()}.json',
+    )
     yield from check_levels(
         value=(time_ns() - start_time) / 1e9,
         label='Time taken',
-        metric_name='vsphere_topo_time_taken',
+        metric_name=f'{TOPOLOGY_NAME.lower()}_topo_time_taken',
         boundaries=(0, None),
         render_func=render.timespan,
     )
 
 
 check_plugin_vsphere_topo = CheckPlugin(
-    name="vsphere_topo",
-    service_name="vSphere Topology",
+    name='vsphere_topo',
+    service_name=f'{TOPOLOGY_NAME} Topology',
     sections=[
         'esx_vsphere_clusters',
         'esx_vsphere_virtual_machines',
diff --git a/source/cmk_addons_plugins/vsphere_topo/constants.py b/source/cmk_addons_plugins/vsphere_topo/constants.py
new file mode 100644
index 0000000..e58585f
--- /dev/null
+++ b/source/cmk_addons_plugins/vsphere_topo/constants.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# License: GNU General Public License v2
+#
+# Author: thl-cmk[at]outlook[dot]com
+# URL   : https://thl-cmk.hopto.org
+# Date  : 2024-07-16
+# File  : vsphere_topo/lib/ruelset_names.py
+
+from typing import Final
+
+EMBLEM_CLUSTER: Final[str] = 'icon_plugins_hw'
+EMBLEM_DATA_CENTER: Final[str] = 'icon_cloud'
+EMBLEM_DATA_STORE: Final[str] = 'icon_services_green'
+
+ICON_VCENTER: Final[str] = 'icon_topic_hosts'
+
+PARAM_ADD_DUMMY_TOPOLOGIES: Final[str] = 'add_dummy_topologies'
+PARAM_CHANGE_CASE: Final[str] = 'change_case'
+PARAM_CLUSTER: Final[str] = 'cluster'
+PARAM_CUSTOM_EMBLEM: Final[str] = 'custom_emblem'
+PARAM_DATA_CENTER: Final[str] = 'data_center'
+PARAM_DATA_STORE: Final[str] = 'data_store'
+PARAM_DATA_STORE_AS_SERVICE: Final[str] = 'data_sore_as_service'
+PARAM_DEFAULT_EMBLEM: Final[str] = 'default_emblem'
+PARAM_DONT_ADD_VC_AS_VM: Final[str] = 'dont_add_vc_as_vm'
+PARAM_HOST_SYSTEMS: Final[str] = 'host_systems'
+PARAM_KEEP_DOMAIN: Final[str] = 'keep_domain'
+PARAM_LOWER: Final[str] = 'lower'
+PARAM_MAKE_DEFAULT: Final[str] = 'make_default'
+PARAM_NO_CHANGE: Final[str] = 'no_change'
+PARAM_NO_EMBLEM: Final[str] = 'no_emblem'
+PARAM_PREFIX: Final[str] = 'prefix'
+PARAM_UPPER: Final[str] = 'upper'
+PARAM_VCENTER: Final[str] = 'vcenter_icon'
+PARAM_VM_NAMES: Final[str] = 'vm_names'
+
+PICTURE_TYPE_EMBLEM: Final[str] = 'emblem'
+PICTURE_TYPE_ICON: Final[str] = 'icon'
+
+RULE_SET_NAME_VSPHERE_TOPO: Final[str] = 'vsphere_topo'
+TOPOLOGY_NAME: Final[str] ='vSphere'
diff --git a/source/cmk_addons_plugins/vsphere_topo/lib/utils.py b/source/cmk_addons_plugins/vsphere_topo/lib/utils.py
index 606ffcd..e969d59 100644
--- a/source/cmk_addons_plugins/vsphere_topo/lib/utils.py
+++ b/source/cmk_addons_plugins/vsphere_topo/lib/utils.py
@@ -12,46 +12,26 @@ from collections.abc import Mapping, MutableMapping, MutableSequence, Sequence
 from json import dumps as json_dunps, loads as json_loads
 from os import environ
 from pathlib import Path
-from typing import Any, Final, Tuple
+from typing import Final, Tuple
 
 from livestatus import MultiSiteConnection, SiteConfigurations, SiteId
-
-EMBLEM_CLUSTER: Final[str] = 'icon_plugins_hw'
-EMBLEM_DATA_CENTER: Final[str] = 'icon_cloud'
-EMBLEM_DATA_STORE: Final[str] = 'icon_services_green'
-
-ICON_VCENTER: Final[str] = 'icon_topic_hosts'
-
-PARAM_ADD_DUMMY_TOPOLOGIES: Final[str] = 'add_dummy_topologies'
-PARAM_CHANGE_CASE: Final[str] = 'change_case'
-PARAM_CLUSTER: Final[str] = 'cluster'
-PARAM_CUSTOM_EMBLEM: Final[str] = 'custom_emblem'
-PARAM_DATA_CENTER: Final[str] = 'data_center'
-PARAM_DATA_STORE: Final[str] = 'data_store'
-PARAM_DATA_STORE_AS_SERVICE: Final[str] = 'data_sore_as_service'
-PARAM_DEFAULT_EMBLEM: Final[str] = 'default_emblem'
-PARAM_DONT_ADD_VC_AS_VM: Final[str] = 'dont_add_vc_as_vm'
-PARAM_HOST_SYSTEMS: Final[str] = 'host_systems'
-PARAM_KEEP_DOMAIN: Final[str] = 'keep_domain'
-PARAM_LOWER: Final[str] = 'lower'
-PARAM_MAKE_DEFAULT: Final[str] = 'make_default'
-PARAM_NO_CHANGE: Final[str] = 'no_change'
-PARAM_NO_EMBLEM: Final[str] = 'no_emblem'
-PARAM_PREFIX: Final[str] = 'prefix'
-PARAM_UPPER: Final[str] = 'upper'
-PARAM_VCENTER: Final[str] = 'vcenter_icon'
-PARAM_VM_NAMES: Final[str] = 'vm_names'
-
-PICTURE_TYPE_EMBLEM: Final[str] = 'emblem'
-PICTURE_TYPE_ICON: Final[str] = 'icon'
-
-RULE_SET_NAME_VSPHERE_TOPO: Final[str] = 'vsphere_topo'
-
-OMD_ROOT = environ["OMD_ROOT"]
+from cmk_addons.plugins.vsphere_topo.constants import (
+    PARAM_CHANGE_CASE,
+    PARAM_CUSTOM_EMBLEM,
+    PARAM_DEFAULT_EMBLEM,
+    PARAM_KEEP_DOMAIN,
+    PARAM_LOWER,
+    PARAM_NO_CHANGE,
+    PARAM_NO_EMBLEM,
+    PARAM_PREFIX,
+    PARAM_UPPER,
+    TOPOLOGY_NAME,
+)
+OMD_ROOT = environ['OMD_ROOT']
 BASE_TOPO_PATH: Final[str] = f'{OMD_ROOT}/var/check_mk/topology/data'
 
 
-def adjust_name(name: str, params: Mapping[str, Any]) -> str:
+def adjust_name(name: str, params: Mapping[str, object]) -> str:
     class Case:
         upper: str = PARAM_UPPER
         lower: str = PARAM_LOWER
@@ -149,8 +129,8 @@ def get_topologies() -> Sequence[str | None]:
 
 def add_dummy_topologies(sub_directory: str):
     path: str = f'{BASE_TOPO_PATH}/default'
-    if Path(path).exists() and not Path(f'{path}/data_vsphere.json').exists():  # don't overwrite existing topology
-        dummy_topology = {'version': 1, 'name': 'vSphere', 'objects': {}, 'connections': []}
+    if Path(path).exists() and not Path(f'{path}/data_{TOPOLOGY_NAME.lower()}.json').exists():  # don't overwrite existing topology
+        dummy_topology = {'version': 1, 'name': TOPOLOGY_NAME, 'objects': {}, 'connections': []}
         save_topology(
             data=dummy_topology,
             sub_directory='default',
@@ -175,14 +155,14 @@ class LiveStatusConnection(object):
         if self.sites_mk.exists():
             # make eval() "secure"
             # https://realpython.com/python-eval-function/#minimizing-the-security-issues-of-eval
-            _code = compile(self.sites_mk.read_text(), "<string>", "eval")
+            _code = compile(self.sites_mk.read_text(), '<string>', 'eval')
             allowed_names = ['sites', 'update']
             for name in _code.co_names:
                 if name not in allowed_names:
                     raise NameError(f'Use of {name} in {self.sites_mk.name} not allowed.')
 
             sites_raw: MutableMapping = {}
-            eval(self.sites_mk.read_text(), {"__builtins__": {}}, {"sites": sites_raw})
+            eval(self.sites_mk.read_text(), {'__builtins__': {}}, {'sites': sites_raw})
 
             for site, data in sites_raw.items():
                 self.sites.update({site: {
@@ -215,3 +195,74 @@ class LiveStatusConnection(object):
             return data
         else:
             return None
+
+
+class TopoObjects(object):
+    def __init__(self) -> None:
+        self.topo_objects: MutableMapping[str, object] = {}
+
+    def add_host(
+            self,
+            host: str,
+            emblem: str | None = None,
+            icon: str | None = None,
+            link2core: bool = True,
+            obj_id_prefix: str = '',
+    ):
+        if self.topo_objects.get(f'{obj_id_prefix}{host}') is not None:
+            return
+
+        metadata = {}
+        if emblem or icon:
+            metadata = {'images': {}}
+
+        if emblem is not None:
+            metadata['images'].update({'emblem': emblem})  # node image
+
+        if icon is not None:
+            metadata['images'].update({'icon': icon})  # node icon
+
+        self.topo_objects[f'{obj_id_prefix}{host}'] = {
+            'name': host,
+            'link': {'core': host} if link2core else {},
+            'metadata': metadata,
+        }
+
+    def add_service(
+            self,
+            host: str,
+            service: str,
+            emblem: str | None = None,
+            link2core: bool = True,
+    ):
+        obj_id = f'{service}@{host}'
+        if self.topo_objects.get(obj_id) is not None:
+            return
+
+        metadata = {}
+        if emblem is not None:
+            metadata = {
+                'images': {
+                    'emblem': emblem,  # node image
+                },
+            }
+
+        self.topo_objects[obj_id] = {
+            'name': service,
+            'link': {'core': [host, service]} if link2core else {},
+            'metadata': metadata,
+        }
+
+
+class TopoConnections(object):
+    def __init__(self) -> None:
+        self.topo_connections: MutableSequence = []
+
+    def add_connection(
+            self,
+            left: str,
+            right: str,
+    ):
+        connection = [left, right]
+        connection.sort()
+        self.topo_connections.append([connection, {'line_config': {'css_styles': {'stroke-dasharray': 'unset'}}}])
diff --git a/source/cmk_addons_plugins/vsphere_topo/rulesets/packages.py b/source/cmk_addons_plugins/vsphere_topo/rulesets/packages.py
index 974e6e0..e541adb 100644
--- a/source/cmk_addons_plugins/vsphere_topo/rulesets/packages.py
+++ b/source/cmk_addons_plugins/vsphere_topo/rulesets/packages.py
@@ -24,7 +24,7 @@ from cmk.rulesets.v1.form_specs import (
 )
 from cmk.rulesets.v1.form_specs.validators import LengthInRange
 from cmk.rulesets.v1.rule_specs import CheckParameters, HostCondition, Topic
-from cmk_addons.plugins.vsphere_topo.lib.utils import (
+from cmk_addons.plugins.vsphere_topo.constants import (
     EMBLEM_CLUSTER,
     EMBLEM_DATA_CENTER,
     EMBLEM_DATA_STORE,
diff --git a/source/packages/vsphere_topo b/source/packages/vsphere_topo
index 203c698..2215409 100644
--- a/source/packages/vsphere_topo
+++ b/source/packages/vsphere_topo
@@ -5,14 +5,15 @@
                 '\n'
                 'Topolgy is created by:  vCenter  -> data center(s) '
                 '->Cluster(s) -> ESX host(s) -> VM(s)\n',
- 'download_url': 'https://thl-cmk.hopto.org',
+ 'download_url': 'https://thl-cmk.hopto.org/gitlab/checkmk/various/vsphere_topo',
  'files': {'cmk_addons_plugins': ['vsphere_topo/agent_based/packages.py',
                                   'vsphere_topo/rulesets/packages.py',
                                   'vsphere_topo/lib/utils.py',
-                                  'vsphere_topo/graphing/packages.py']},
+                                  'vsphere_topo/graphing/packages.py',
+                                  'vsphere_topo/constants.py']},
  'name': 'vsphere_topo',
  'title': 'vSphere Topologie',
- 'version': '0.0.5-20240714',
+ 'version': '0.0.5-20240717',
  'version.min_required': '2.3.0b1',
  'version.packaged': 'cmk-mkp-tool 0.2.0',
  'version.usable_until': '2.4.0b1'}
-- 
GitLab