feat: Add openstack db exporter (#1039)

diff --git a/roles/cinder/tasks/main.yml b/roles/cinder/tasks/main.yml
index 617c92b..7de3400 100644
--- a/roles/cinder/tasks/main.yml
+++ b/roles/cinder/tasks/main.yml
@@ -44,7 +44,7 @@
 - name: Append Helm values (Staffeln)
   when: atmosphere_staffeln_enabled is defined
   ansible.builtin.set_fact:
-    _neutron_helm_values: "{{ _cinder_helm_values | combine(__cinder_staffeln_helm_values, recursive=True) }}"
+    _cinder_helm_values: "{{ _cinder_helm_values | combine(__cinder_staffeln_helm_values, recursive=True) }}"
 
 - name: Deploy Helm chart
   run_once: true
diff --git a/roles/cinder/vars/main.yml b/roles/cinder/vars/main.yml
index 179f34e..eee67ed 100644
--- a/roles/cinder/vars/main.yml
+++ b/roles/cinder/vars/main.yml
@@ -21,7 +21,7 @@
       api: 3
       scheduler: 3
   conf:
-    policy: "{{ staffeln_backup_delete_policy if atmosphere_staffeln_enabled is defined else {} }}"
+    policy: {}
     cinder:
       DEFAULT:
         allowed_direct_url_schemes: cinder
diff --git a/roles/defaults/vars/main.yml b/roles/defaults/vars/main.yml
index ca82bc7..8964b7c 100644
--- a/roles/defaults/vars/main.yml
+++ b/roles/defaults/vars/main.yml
@@ -172,6 +172,7 @@
   prometheus_memcached_exporter: quay.io/prometheus/memcached-exporter:v0.10.0@sha256:fa5a2de1a4744da66fb369bee81232f5ea52208bc643e409a60f66d699ac27b2
   prometheus_mysqld_exporter: quay.io/prometheus/mysqld-exporter:v0.14.0@sha256:eb6fe170738bf9181c51f5bc89f93adb26672ec49ffdcb22f55c24834003b45d
   prometheus_node_exporter: quay.io/prometheus/node-exporter:v1.7.0@sha256:4cb2b9019f1757be8482419002cb7afe028fdba35d47958829e4cfeaf6246d80
+  prometheus_openstack_database_exporter: ghcr.io/vexxhost/openstack-database-exporter:v0.2.0@sha256:286573f63840f961a6861982f7b3e8007b9a93eed77ec4476810af3286cb7fd9
   prometheus_openstack_exporter: ghcr.io/openstack-exporter/openstack-exporter:1.7.0@sha256:e5146a7dd5153c035fd8060899e3504b25557756ef4a4d85860a409247404f97
   prometheus_operator_kube_webhook_certgen: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20221220-controller-v1.5.1-58-g787ea74b6@sha256:5e6fdb9b2c74ad2576dd835b389d00d18ccfee21b547d1a79efb881009664099
   prometheus_operator: quay.io/prometheus-operator/prometheus-operator:v0.71.2@sha256:bbf3c671e65b0c115d2196bbe7fed0bcdc59f44b7c93868cd40d1c90cbd3806e
diff --git a/roles/kube_prometheus_stack/files/jsonnet/openstack.libsonnet b/roles/kube_prometheus_stack/files/jsonnet/openstack.libsonnet
index 9d05f36..b8e6ac1 100644
--- a/roles/kube_prometheus_stack/files/jsonnet/openstack.libsonnet
+++ b/roles/kube_prometheus_stack/files/jsonnet/openstack.libsonnet
@@ -129,6 +129,18 @@
                 severity: 'warning',
               },
             },
+            {
+              alert: 'NeutronRouterMultipleActiveL3Agents',
+              annotations: {
+                summary: 'Neutron HA router has multiple active L3 agents',
+                description: 'The router with ID {{ $labels.router_id }} has {{ $value }} L3 agents in active state which can cause network resets and traffic drops.',
+              },
+              expr: 'sum by (router_id) (openstack_neutron_l3_agent_of_router{ha_state="active"}) > 1',
+              'for': '5m',
+              labels: {
+                severity: 'P3',
+              },
+            },
           ],
       },
       {
@@ -229,6 +241,24 @@
           },
         ],
       },
+      {
+        name: 'octavia',
+        rules:
+          [
+            {
+              alert: 'OctaviaLoadBalancerNotActive',
+              annotations: {
+                summary: 'Octavia load balancer not active',
+                description: 'Load balancer with ID {{ $labels.id }} stuck in non-active state for more then 15 minutes.',
+              },
+              expr: 'openstack_loadbalancer_loadbalancer_status{provisioning_status!="ACTIVE"} > 0',
+              'for': '15m',
+              labels: {
+                severity: 'P3',
+              },
+            },
+          ],
+      },
     ],
   },
 }
diff --git a/roles/kube_prometheus_stack/vars/main.yml b/roles/kube_prometheus_stack/vars/main.yml
index f062718..e638d22 100644
--- a/roles/kube_prometheus_stack/vars/main.yml
+++ b/roles/kube_prometheus_stack/vars/main.yml
@@ -439,6 +439,18 @@
           - interval: 60s
             port: metrics
             relabelings: *relabelings_instance_to_node_name
+      - name: openstack-database-exporter
+        jobLabel: job
+        namespaceSelector:
+          matchNames:
+            - openstack
+        selector:
+          matchLabels:
+            application: openstack-database-exporter
+        podMetricsEndpoints:
+          - interval: 60s
+            port: metrics
+            relabelings: *relabelings_instance_to_pod_name
       - name: percona-xtradb-pxc
         jobLabel: app.kubernetes.io/component
         namespaceSelector:
diff --git a/roles/openstack_exporter/tasks/main.yml b/roles/openstack_exporter/tasks/main.yml
index f477859..21e1915 100644
--- a/roles/openstack_exporter/tasks/main.yml
+++ b/roles/openstack_exporter/tasks/main.yml
@@ -71,6 +71,8 @@
                     - internal
                     - default
                     - --collect-metric-time
+                    - -d neutron-l3_agent_of_router
+                    - -d loadbalancer-loadbalancer_status
                   ports:
                     - name: metrics
                       containerPort: 9180
@@ -114,3 +116,88 @@
               targetPort: metrics
           selector:
             application: openstack-exporter
+
+- name: Fetch Octavia DB secret
+  run_once: true
+  no_log: true
+  kubernetes.core.k8s_info:
+    kind: Secret
+    namespace: openstack
+    name: octavia-db-user
+  register: _octavia_db_user
+
+- name: Fetch Neutron DB secret
+  run_once: true
+  no_log: true
+  kubernetes.core.k8s_info:
+    kind: Secret
+    namespace: openstack
+    name: neutron-db-user
+  register: _neutron_db_user
+
+- name: Create "openstack-database-exporter-dsn" secret
+  run_once: true
+  no_log: true
+  kubernetes.core.k8s:
+    state: present
+    definition:
+      apiVersion: v1
+      kind: Secret
+      type: Opaque
+      metadata:
+        name: openstack-database-exporter-dsn
+        namespace: openstack
+        labels:
+          application: openstack-database-exporter
+      stringData:
+        NEUTRON_DSN: "{{ _neutron_db_user.resources.0.data.DB_CONNECTION | b64decode |  regex_replace('^.*//', '') | regex_replace('@(.*)/', '@tcp(\\1)/') }}"
+        OCTAVIA_DSN: "{{ _octavia_db_user.resources.0.data.DB_CONNECTION | b64decode |  regex_replace('^.*//', '') | regex_replace('@(.*)/', '@tcp(\\1)/') }}"
+
+- name: Deploy service
+  run_once: true
+  kubernetes.core.k8s:
+    state: present
+    definition:
+      - apiVersion: apps/v1
+        kind: Deployment
+        metadata:
+          name: openstack-database-exporter
+          namespace: openstack
+          labels:
+            application: openstack-database-exporter
+        spec:
+          replicas: 1
+          selector:
+            matchLabels:
+              application: openstack-database-exporter
+          template:
+            metadata:
+              labels:
+                application: openstack-database-exporter
+            spec:
+              nodeSelector:
+                openstack-control-plane: enabled
+              containers:
+                - name: openstack-database-exporter
+                  image: "{{ atmosphere_images['prometheus_openstack_database_exporter'] | vexxhost.kubernetes.docker_image('ref') }}"
+                  envFrom:
+                    - secretRef:
+                        name: openstack-database-exporter-dsn
+                  ports:
+                    - name: metrics
+                      containerPort: 9180
+                  readinessProbe:
+                    failureThreshold: 3
+                    httpGet:
+                      path: /
+                      port: 9180
+                      scheme: HTTP
+                  livenessProbe:
+                    failureThreshold: 3
+                    httpGet:
+                      path: /
+                      port: 9180
+                      scheme: HTTP
+                    periodSeconds: 10
+                    successThreshold: 1
+                    timeoutSeconds: 1