From 8581497acb8726b14c669bcbb426fdf144e9e646 Mon Sep 17 00:00:00 2001
From: Pierre Riteau <pierre@stackhpc.com>
Date: Thu, 16 May 2024 09:07:59 +0200
Subject: [PATCH] Add support for OpenSearch 2.x as CloudKitty storage backend

Depends-On: https://review.opendev.org/c/openstack/cloudkitty/+/880739
Change-Id: Ib8d7182cc4b8a0c7d320ba2c51b2157782030317
---
 ansible/group_vars/all.yml                    |  2 +-
 ansible/roles/cloudkitty/defaults/main.yml    | 14 ++++++--
 ansible/roles/cloudkitty/tasks/bootstrap.yml  | 36 +++++++++++++++++--
 .../cloudkitty/templates/cloudkitty.conf.j2   | 12 ++++++-
 .../reference/rating/cloudkitty-guide.rst     | 16 ++++++---
 etc/kolla/globals.yml                         |  2 +-
 ...loudkitty-opensearch-2ea5cb46eeb31847.yaml | 16 +++++++++
 7 files changed, 87 insertions(+), 11 deletions(-)
 create mode 100644 releasenotes/notes/cloudkitty-opensearch-2ea5cb46eeb31847.yaml

diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml
index e8cdca9f83..57d195a68a 100644
--- a/ansible/group_vars/all.yml
+++ b/ansible/group_vars/all.yml
@@ -879,7 +879,7 @@ skip_stop_containers: []
 # services with ElasticSearch endpoints should be configured to log
 # to the external cluster by default. This is for backwards compatibility.
 opensearch_address: "{{ elasticsearch_address if elasticsearch_address is defined else kolla_internal_fqdn }}"
-enable_opensearch: "{{ enable_central_logging | bool or enable_osprofiler | bool or (enable_cloudkitty | bool and cloudkitty_storage_backend == 'elasticsearch') }}"
+enable_opensearch: "{{ enable_central_logging | bool or enable_osprofiler | bool or (enable_cloudkitty | bool and cloudkitty_storage_backend == 'opensearch') }}"
 enable_opensearch_dashboards: "{{ enable_opensearch | bool }}"
 enable_opensearch_dashboards_external: "{{ enable_opensearch_dashboards | bool }}"
 
diff --git a/ansible/roles/cloudkitty/defaults/main.yml b/ansible/roles/cloudkitty/defaults/main.yml
index 0735b4a67e..be387f7940 100644
--- a/ansible/roles/cloudkitty/defaults/main.yml
+++ b/ansible/roles/cloudkitty/defaults/main.yml
@@ -165,11 +165,13 @@ cloudkitty_influxdb_cafile: "{{ openstack_cacert }}"
 
 cloudkitty_influxdb_name: "cloudkitty"
 
-# Set the elasticsearch index name.
+# Set the elasticsearch/opensearch index name.
 cloudkitty_elasticsearch_index_name: "cloudkitty"
+cloudkitty_opensearch_index_name: "{{ cloudkitty_elasticsearch_index_name }}"
 
-# Set the elasticsearch host URL.
+# Set the elasticsearch/opensearch host URL.
 cloudkitty_elasticsearch_url: "{{ internal_protocol }}://{{ opensearch_address }}:{{ opensearch_port }}"
+cloudkitty_opensearch_url: "{{ cloudkitty_elasticsearch_url }}"
 
 # Path of the CA certificate to trust for HTTPS connections.
 cloudkitty_elasticsearch_cafile: "{{ openstack_cacert }}"
@@ -178,6 +180,14 @@ cloudkitty_elasticsearch_cafile: "{{ openstack_cacert }}"
 # This means, HTTPS connections without validating the certificate used by elasticsearch
 cloudkitty_elasticsearch_insecure_connections: false
 
+# Path of the CA certificate to trust for HTTPS connections.
+cloudkitty_opensearch_cafile: "{{ openstack_cacert }}"
+
+# Set to true to authorize insecure HTTPS connections to OpenSearch.
+# This means, HTTPS connections without validating the certificate used by
+# OpenSearch.
+cloudkitty_opensearch_insecure_connections: false
+
 ####################
 # Collector
 ####################
diff --git a/ansible/roles/cloudkitty/tasks/bootstrap.yml b/ansible/roles/cloudkitty/tasks/bootstrap.yml
index 8c432f3ffc..9e31dd5a47 100644
--- a/ansible/roles/cloudkitty/tasks/bootstrap.yml
+++ b/ansible/roles/cloudkitty/tasks/bootstrap.yml
@@ -62,9 +62,22 @@
       status_code: 200, 404
   run_once: true
   delegate_to: "{{ groups['cloudkitty-api'][0] }}"
-  register: cloudkitty_index
+  register: cloudkitty_index_elasticsearch
   when: cloudkitty_storage_backend == 'elasticsearch'
 
+- name: Checking if Cloudkitty opensearch index exists
+  become: true
+  kolla_toolbox:
+    container_engine: "{{ kolla_container_engine }}"
+    module_name: uri
+    module_args:
+      url: "{{ cloudkitty_opensearch_url }}/{{ cloudkitty_opensearch_index_name }}"
+      status_code: 200, 404
+  run_once: true
+  delegate_to: "{{ groups['cloudkitty-api'][0] }}"
+  register: cloudkitty_index_opensearch
+  when: cloudkitty_storage_backend == 'opensearch'
+
 - name: Creating Cloudkitty elasticsearch index
   become: true
   kolla_toolbox:
@@ -82,6 +95,25 @@
   delegate_to: "{{ groups['cloudkitty-api'][0] }}"
   when:
     - cloudkitty_storage_backend == 'elasticsearch'
-    - cloudkitty_index.get('status') != 200
+    - cloudkitty_index_elasticsearch.get('status') != 200
+
+- name: Creating Cloudkitty opensearch index
+  become: true
+  kolla_toolbox:
+    container_engine: "{{ kolla_container_engine }}"
+    module_name: uri
+    module_args:
+      url: "{{ cloudkitty_opensearch_url }}/{{ cloudkitty_opensearch_index_name }}"
+      method: PUT
+      status_code: 200
+      return_content: yes
+      body: |
+        {}
+      body_format: json
+  run_once: True
+  delegate_to: "{{ groups['cloudkitty-api'][0] }}"
+  when:
+    - cloudkitty_storage_backend == 'opensearch'
+    - cloudkitty_index_opensearch.get('status') != 200
 
 - import_tasks: bootstrap_service.yml
diff --git a/ansible/roles/cloudkitty/templates/cloudkitty.conf.j2 b/ansible/roles/cloudkitty/templates/cloudkitty.conf.j2
index b2f209c2ba..1a0aa808ea 100644
--- a/ansible/roles/cloudkitty/templates/cloudkitty.conf.j2
+++ b/ansible/roles/cloudkitty/templates/cloudkitty.conf.j2
@@ -154,8 +154,18 @@ host = {{ cloudkitty_elasticsearch_url }}
 index_name = {{ cloudkitty_elasticsearch_index_name }}
 insecure = {{ cloudkitty_elasticsearch_insecure_connections }}
 
-{% if cloudkitty_elasticsearch_cafile is defined %}
+{% if cloudkitty_elasticsearch_cafile | length > 0 %}
 cafile = {{ cloudkitty_elasticsearch_cafile }}
 {% endif %}
+{% endif %}
+
+{% if cloudkitty_storage_backend == 'opensearch' %}
+[storage_opensearch]
+host = {{ cloudkitty_opensearch_url }}
+index_name = {{ cloudkitty_opensearch_index_name }}
+insecure = {{ cloudkitty_opensearch_insecure_connections }}
 
+{% if cloudkitty_opensearch_cafile | length > 0 %}
+cafile = {{ cloudkitty_opensearch_cafile }}
+{% endif %}
 {% endif %}
diff --git a/doc/source/reference/rating/cloudkitty-guide.rst b/doc/source/reference/rating/cloudkitty-guide.rst
index 0af3aca3ba..061e096754 100644
--- a/doc/source/reference/rating/cloudkitty-guide.rst
+++ b/doc/source/reference/rating/cloudkitty-guide.rst
@@ -62,14 +62,22 @@ Cloudkitty Storage Backend
 As for collectors, CloudKitty supports multiple backend to store ratings.
 By default, Kolla Ansible uses the InfluxDB based backend.
 
-Another famous alternative is Elasticsearch and can be activated in Kolla
-Ansible using the ``cloudkitty_storage_backend``  configuration option in
+Another famous alternative is OpenSearch and can be activated in Kolla
+Ansible using the ``cloudkitty_storage_backend`` configuration option in
 your ``globals.yml`` configuration file:
 
+.. code-block:: yaml
+
+   cloudkitty_storage_backend: opensearch
+
+Using an external Elasticsearch backend is still possible with the following
+configuration:
+
 .. code-block:: yaml
 
    cloudkitty_storage_backend: elasticsearch
+   cloudkitty_elasticsearch_url: http://HOST:PORT
 
-You can only use one backend type at a time, selecting elasticsearch
-will automatically enable Elasticsearch deployment and creation of the
+You can only use one backend type at a time, selecting ``opensearch``
+will automatically enable OpenSearch deployment and creation of the
 required CloudKitty index.
diff --git a/etc/kolla/globals.yml b/etc/kolla/globals.yml
index a7ba2ebb23..065d9c9dbf 100644
--- a/etc/kolla/globals.yml
+++ b/etc/kolla/globals.yml
@@ -409,7 +409,7 @@ workaround_ansible_issue_8743: yes
 #enable_octavia: "no"
 #enable_octavia_driver_agent: "{{ enable_octavia | bool and neutron_plugin_agent == 'ovn' }}"
 #enable_octavia_jobboard: "{{ enable_octavia | bool and 'amphora' in octavia_provider_drivers }}"
-#enable_opensearch: "{{ enable_central_logging | bool or enable_osprofiler | bool or (enable_cloudkitty | bool and cloudkitty_storage_backend == 'elasticsearch') }}"
+#enable_opensearch: "{{ enable_central_logging | bool or enable_osprofiler | bool or (enable_cloudkitty | bool and cloudkitty_storage_backend == 'opensearch') }}"
 #enable_opensearch_dashboards: "{{ enable_opensearch | bool }}"
 #enable_opensearch_dashboards_external: "{{ enable_opensearch_dashboards | bool }}"
 #enable_openvswitch: "{{ enable_neutron | bool and neutron_plugin_agent != 'linuxbridge' }}"
diff --git a/releasenotes/notes/cloudkitty-opensearch-2ea5cb46eeb31847.yaml b/releasenotes/notes/cloudkitty-opensearch-2ea5cb46eeb31847.yaml
new file mode 100644
index 0000000000..dd91fce270
--- /dev/null
+++ b/releasenotes/notes/cloudkitty-opensearch-2ea5cb46eeb31847.yaml
@@ -0,0 +1,16 @@
+---
+features:
+  - |
+    Adds support for configuring CloudKitty to use OpenSearch as storage
+    backend.
+upgrade:
+  - |
+    To use OpenSearch for CloudKitty storage, set
+    ``cloudkitty_storage_backend`` to ``opensearch``. The following variables
+    have been added and may need to be updated unless the default configuration
+    is used:
+
+    * ``cloudkitty_opensearch_index_name``
+    * ``cloudkitty_opensearch_url``
+    * ``cloudkitty_opensearch_cafile``
+    * ``cloudkitty_opensearch_insecure_connections``
-- 
GitLab