Repository: pulp/pulpcore

Issue:
Gather postgresql version metric collection
As a pulp user have the postgresql server gathered consistent with [this proposal](https://hackmd.io/zJ1dJe8qQtmzr0JiM1jptw).


Discussion:

Resolving patch:
diff --git a/pulpcore/app/protobuf/analytics_pb2.py b/pulpcore/app/protobuf/analytics_pb2.py
new file mode 100644
--- /dev/null
+++ b/pulpcore/app/protobuf/analytics_pb2.py
@@ -0,0 +1,77 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: analytics.proto
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
+    b'\n\x0f\x61nalytics.proto"\xe7\x02\n\tAnalytics\x12\x11\n\tsystem_id\x18\x01 \x02(\t\x12\x39\n\x13online_content_apps\x18\x02 \x01(\x0b\x32\x1c.Analytics.OnlineContentApps\x12\x30\n\x0eonline_workers\x18\x03 \x01(\x0b\x32\x18.Analytics.OnlineWorkers\x12(\n\ncomponents\x18\x04 \x03(\x0b\x32\x14.Analytics.Component\x12\x1a\n\x12postgresql_version\x18\x05 \x01(\r\x1a\x35\n\x11OnlineContentApps\x12\x11\n\tprocesses\x18\x01 \x01(\r\x12\r\n\x05hosts\x18\x02 \x01(\r\x1a\x31\n\rOnlineWorkers\x12\x11\n\tprocesses\x18\x01 \x01(\r\x12\r\n\x05hosts\x18\x02 \x01(\r\x1a*\n\tComponent\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0f\n\x07version\x18\x02 \x02(\t'
+)
+
+
+_ANALYTICS = DESCRIPTOR.message_types_by_name["Analytics"]
+_ANALYTICS_ONLINECONTENTAPPS = _ANALYTICS.nested_types_by_name["OnlineContentApps"]
+_ANALYTICS_ONLINEWORKERS = _ANALYTICS.nested_types_by_name["OnlineWorkers"]
+_ANALYTICS_COMPONENT = _ANALYTICS.nested_types_by_name["Component"]
+Analytics = _reflection.GeneratedProtocolMessageType(
+    "Analytics",
+    (_message.Message,),
+    {
+        "OnlineContentApps": _reflection.GeneratedProtocolMessageType(
+            "OnlineContentApps",
+            (_message.Message,),
+            {
+                "DESCRIPTOR": _ANALYTICS_ONLINECONTENTAPPS,
+                "__module__": "analytics_pb2"
+                # @@protoc_insertion_point(class_scope:Analytics.OnlineContentApps)
+            },
+        ),
+        "OnlineWorkers": _reflection.GeneratedProtocolMessageType(
+            "OnlineWorkers",
+            (_message.Message,),
+            {
+                "DESCRIPTOR": _ANALYTICS_ONLINEWORKERS,
+                "__module__": "analytics_pb2"
+                # @@protoc_insertion_point(class_scope:Analytics.OnlineWorkers)
+            },
+        ),
+        "Component": _reflection.GeneratedProtocolMessageType(
+            "Component",
+            (_message.Message,),
+            {
+                "DESCRIPTOR": _ANALYTICS_COMPONENT,
+                "__module__": "analytics_pb2"
+                # @@protoc_insertion_point(class_scope:Analytics.Component)
+            },
+        ),
+        "DESCRIPTOR": _ANALYTICS,
+        "__module__": "analytics_pb2"
+        # @@protoc_insertion_point(class_scope:Analytics)
+    },
+)
+_sym_db.RegisterMessage(Analytics)
+_sym_db.RegisterMessage(Analytics.OnlineContentApps)
+_sym_db.RegisterMessage(Analytics.OnlineWorkers)
+_sym_db.RegisterMessage(Analytics.Component)
+
+if _descriptor._USE_C_DESCRIPTORS == False:
+
+    DESCRIPTOR._options = None
+    _ANALYTICS._serialized_start = 20
+    _ANALYTICS._serialized_end = 379
+    _ANALYTICS_ONLINECONTENTAPPS._serialized_start = 231
+    _ANALYTICS_ONLINECONTENTAPPS._serialized_end = 284
+    _ANALYTICS_ONLINEWORKERS._serialized_start = 286
+    _ANALYTICS_ONLINEWORKERS._serialized_end = 335
+    _ANALYTICS_COMPONENT._serialized_start = 337
+    _ANALYTICS_COMPONENT._serialized_end = 379
+# @@protoc_insertion_point(module_scope)
diff --git a/pulpcore/app/protobuf/telemetry_pb2.py b/pulpcore/app/protobuf/telemetry_pb2.py
deleted file mode 100644
--- a/pulpcore/app/protobuf/telemetry_pb2.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by the protocol buffer compiler.  DO NOT EDIT!
-# source: telemetry.proto
-"""Generated protocol buffer code."""
-from google.protobuf import descriptor as _descriptor
-from google.protobuf import descriptor_pool as _descriptor_pool
-from google.protobuf import message as _message
-from google.protobuf import reflection as _reflection
-from google.protobuf import symbol_database as _symbol_database
-# @@protoc_insertion_point(imports)
-
-_sym_db = _symbol_database.Default()
-
-
-
-
-DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0ftelemetry.proto\"\xcb\x02\n\tTelemetry\x12\x11\n\tsystem_id\x18\x01 \x02(\t\x12\x39\n\x13online_content_apps\x18\x02 \x01(\x0b\x32\x1c.Telemetry.OnlineContentApps\x12\x30\n\x0eonline_workers\x18\x03 \x01(\x0b\x32\x18.Telemetry.OnlineWorkers\x12(\n\ncomponents\x18\x04 \x03(\x0b\x32\x14.Telemetry.Component\x1a\x35\n\x11OnlineContentApps\x12\x11\n\tprocesses\x18\x01 \x01(\r\x12\r\n\x05hosts\x18\x02 \x01(\r\x1a\x31\n\rOnlineWorkers\x12\x11\n\tprocesses\x18\x01 \x01(\r\x12\r\n\x05hosts\x18\x02 \x01(\r\x1a*\n\tComponent\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0f\n\x07version\x18\x02 \x02(\t')
-
-
-
-_TELEMETRY = DESCRIPTOR.message_types_by_name['Telemetry']
-_TELEMETRY_ONLINECONTENTAPPS = _TELEMETRY.nested_types_by_name['OnlineContentApps']
-_TELEMETRY_ONLINEWORKERS = _TELEMETRY.nested_types_by_name['OnlineWorkers']
-_TELEMETRY_COMPONENT = _TELEMETRY.nested_types_by_name['Component']
-Telemetry = _reflection.GeneratedProtocolMessageType('Telemetry', (_message.Message,), {
-
-  'OnlineContentApps' : _reflection.GeneratedProtocolMessageType('OnlineContentApps', (_message.Message,), {
-    'DESCRIPTOR' : _TELEMETRY_ONLINECONTENTAPPS,
-    '__module__' : 'telemetry_pb2'
-    # @@protoc_insertion_point(class_scope:Telemetry.OnlineContentApps)
-    })
-  ,
-
-  'OnlineWorkers' : _reflection.GeneratedProtocolMessageType('OnlineWorkers', (_message.Message,), {
-    'DESCRIPTOR' : _TELEMETRY_ONLINEWORKERS,
-    '__module__' : 'telemetry_pb2'
-    # @@protoc_insertion_point(class_scope:Telemetry.OnlineWorkers)
-    })
-  ,
-
-  'Component' : _reflection.GeneratedProtocolMessageType('Component', (_message.Message,), {
-    'DESCRIPTOR' : _TELEMETRY_COMPONENT,
-    '__module__' : 'telemetry_pb2'
-    # @@protoc_insertion_point(class_scope:Telemetry.Component)
-    })
-  ,
-  'DESCRIPTOR' : _TELEMETRY,
-  '__module__' : 'telemetry_pb2'
-  # @@protoc_insertion_point(class_scope:Telemetry)
-  })
-_sym_db.RegisterMessage(Telemetry)
-_sym_db.RegisterMessage(Telemetry.OnlineContentApps)
-_sym_db.RegisterMessage(Telemetry.OnlineWorkers)
-_sym_db.RegisterMessage(Telemetry.Component)
-
-if _descriptor._USE_C_DESCRIPTORS == False:
-
-  DESCRIPTOR._options = None
-  _TELEMETRY._serialized_start=20
-  _TELEMETRY._serialized_end=351
-  _TELEMETRY_ONLINECONTENTAPPS._serialized_start=203
-  _TELEMETRY_ONLINECONTENTAPPS._serialized_end=256
-  _TELEMETRY_ONLINEWORKERS._serialized_start=258
-  _TELEMETRY_ONLINEWORKERS._serialized_end=307
-  _TELEMETRY_COMPONENT._serialized_start=309
-  _TELEMETRY_COMPONENT._serialized_end=351
-# @@protoc_insertion_point(module_scope)
diff --git a/pulpcore/app/tasks/telemetry.py b/pulpcore/app/tasks/telemetry.py
--- a/pulpcore/app/tasks/telemetry.py
+++ b/pulpcore/app/tasks/telemetry.py
@@ -6,13 +6,14 @@
 import async_timeout
 
 from asgiref.sync import sync_to_async
+from django.db import connection
 from google.protobuf.json_format import MessageToJson
 
 from pulpcore.app.apps import pulp_plugin_configs
 from pulpcore.app.models import SystemID
 from pulpcore.app.models.status import ContentAppStatus
 from pulpcore.app.models.task import Worker
-from pulpcore.app.protobuf.telemetry_pb2 import Telemetry
+from pulpcore.app.protobuf.analytics_pb2 import Analytics
 
 
 logger = logging.getLogger(__name__)
@@ -22,15 +23,15 @@
 DEV_URL = "https://dev.analytics.pulpproject.org/"
 
 
-def get_telemetry_posting_url():
+def get_analytics_posting_url():
     """
-    Return either the dev or production telemetry FQDN url.
+    Return either the dev or production analytics FQDN url.
 
     Production version string examples:  ["3.21.1", "1.11.0"]
     Developer version string example: ["3.20.3.dev", "2.0.0a6"]
 
     Returns:
-        The FQDN string of either the dev or production telemetry site.
+        The FQDN string of either the dev or production analytics site.
     """
     for app in pulp_plugin_configs():
         if not app.version.count(".") == 2:  # Only two periods allowed in prod version strings
@@ -44,6 +45,14 @@ def get_telemetry_posting_url():
     return PRODUCTION_URL
 
 
+def _get_postgresql_version_string():
+    return connection.cursor().connection.server_version
+
+
+async def _postgresql_version(analytics):
+    analytics.postgresql_version = await sync_to_async(_get_postgresql_version_string)()
+
+
 async def _num_hosts(qs):
     hosts = set()
     items = await sync_to_async(list)(qs.all())
@@ -52,40 +61,41 @@ async def _num_hosts(qs):
     return len(hosts)
 
 
-async def _versions_data(telemetry):
+async def _versions_data(analytics):
     for app in pulp_plugin_configs():
-        new_component = telemetry.components.add()
+        new_component = analytics.components.add()
         new_component.name = app.label
         new_component.version = app.version
 
 
-async def _online_content_apps_data(telemetry):
+async def _online_content_apps_data(analytics):
     online_content_apps_qs = ContentAppStatus.objects.online()
-    telemetry.online_content_apps.processes = await sync_to_async(online_content_apps_qs.count)()
-    telemetry.online_content_apps.hosts = await _num_hosts(online_content_apps_qs)
+    analytics.online_content_apps.processes = await sync_to_async(online_content_apps_qs.count)()
+    analytics.online_content_apps.hosts = await _num_hosts(online_content_apps_qs)
 
 
-async def _online_workers_data(telemetry):
+async def _online_workers_data(analytics):
     online_workers_qs = Worker.objects.online_workers()
-    telemetry.online_workers.processes = await sync_to_async(online_workers_qs.count)()
-    telemetry.online_workers.hosts = await _num_hosts(online_workers_qs)
+    analytics.online_workers.processes = await sync_to_async(online_workers_qs.count)()
+    analytics.online_workers.hosts = await _num_hosts(online_workers_qs)
 
 
-async def _system_id(telemetry):
+async def _system_id(analytics):
     system_id_obj = await sync_to_async(SystemID.objects.get)()
-    telemetry.system_id = str(system_id_obj.pk)
+    analytics.system_id = str(system_id_obj.pk)
 
 
 async def post_telemetry():
-    url = get_telemetry_posting_url()
+    url = get_analytics_posting_url()
 
-    telemetry = Telemetry()
+    analytics = Analytics()
 
     awaitables = (
-        _system_id(telemetry),
-        _versions_data(telemetry),
-        _online_content_apps_data(telemetry),
-        _online_workers_data(telemetry),
+        _system_id(analytics),
+        _versions_data(analytics),
+        _online_content_apps_data(analytics),
+        _online_workers_data(analytics),
+        _postgresql_version(analytics),
     )
 
     await asyncio.gather(*awaitables)
@@ -93,20 +103,20 @@ async def post_telemetry():
     try:
         async with aiohttp.ClientSession() as session:
             async with async_timeout.timeout(300):
-                async with session.post(url, data=telemetry.SerializeToString()) as resp:
+                async with session.post(url, data=analytics.SerializeToString()) as resp:
                     if resp.status == 200:
                         logger.info(
-                            ("Submitted telemetry to %s. " "Information submitted includes %s"),
+                            ("Submitted analytics to %s. " "Information submitted includes %s"),
                             url,
-                            json.loads(MessageToJson(telemetry)),
+                            json.loads(MessageToJson(analytics)),
                         )
                     else:
                         logger.warning(
-                            "Sending telemetry failed with statuscode %s from %s",
+                            "Sending analytics failed with statuscode %s from %s",
                             resp.status,
                             url,
                         )
     except asyncio.TimeoutError:
-        logger.error("Timed out while sending telemetry to %s", url)
+        logger.error("Timed out while sending analytics to %s", url)
     except aiohttp.ClientError as err:
-        logger.error("Error sending telemetry to %s: %r", url, err)
+        logger.error("Error sending analytics to %s: %r", url, err)


Test patch: