Adding ability to cleanup database stale resources

Prod-Related: PROD-35183
Change-Id: I3e35c706653ff3d2773d29582be8db41e2d2139a
diff --git a/README.rst b/README.rst
index 9d440d0..9acf16f 100644
--- a/README.rst
+++ b/README.rst
@@ -1464,3 +1464,59 @@
     nova:
       upgrade:
         manage_service_maintenance: false
+
+Execute database maintenance tasks
+----------------------------------
+Cleanup stale records from nova database to make it smaller.
+This is helpful before any upgrade activity.
+It is safe to execute it generally without maintenance window same as online db_sync.
+
+Enable this pillar:
+
+.. code-block:: yaml
+
+   nova:
+     controller:
+       db_purge:
+         enabled: True
+
+Execute state nova.db.db_cleanup to purge stale records:
+
+.. code-block:: bash
+
+   salt -C 'I@nova:controller:role:primary' state.apply nova.db.db_cleanup -l debug
+
+Starting from rocky release it is possible to pass days parameter.
+If you skip setting it, all records would be archived/purged:
+
+.. code-block:: yaml
+
+   nova:
+     controller:
+       db_purge:
+         enabled: True
+         days: 45
+
+Control pre-rocky releases parameters:
+
+.. code-block:: yaml
+
+   nova:
+     controller:
+       db_purge:
+         enabled: True
+         max_rows: 1000
+         verbose: True
+
+Control rocky+ releases parameters:
+'days' and 'all' parameters are mutually exclusive.
+
+.. code-block:: yaml
+
+   nova:
+     controller:
+       db_purge:
+         enabled: True
+         all: True
+         verbose: True
+         all_cells: True
diff --git a/nova/db/db_cleanup.sls b/nova/db/db_cleanup.sls
new file mode 100644
index 0000000..348a42d
--- /dev/null
+++ b/nova/db/db_cleanup.sls
@@ -0,0 +1,48 @@
+{%- from "nova/map.jinja" import controller with context %}
+
+{%- if controller.get('db_purge', {}).get('enabled', False) %}
+
+{%- set cmd_args = [] %}
+{%- set api_cmd_args = [] %}
+{%- if controller.db_purge.max_rows is defined %}
+{%-   do cmd_args.append('--max_rows') %}
+{%-   do cmd_args.append(controller.db_purge.max_rows|string) %}
+{%- endif %}
+{%- if controller.db_purge.get('verbose', True) %}
+{%-   do cmd_args.append('--verbose') %}
+{%- endif %}
+{%- if controller.db_purge.get('all', True) %}
+{%-   do cmd_args.append('--until-complete') %}
+{%- endif %}
+nova_db_clean:
+  cmd.run:
+  - name: "nova-manage db archive_deleted_rows {{ cmd_args|join(' ') }}"
+  - runas: 'nova'
+  {%- if grains.get('noservices') or controller.get('role', 'primary') == 'secondary' %}
+  - onlyif: /bin/false
+  {%- endif %}
+
+{%- if controller.version is defined and controller.version[0] in ['r','s','t','u','v','w','x','y','z'] %}
+{%-   if controller.db_purge.days is defined %}
+{%-     do api_cmd_args.append('--before') %}
+{%      set prior_to = salt.cmd.run('date -d "' + controller.db_purge.days|string + ' days ago" +%d.%m.%Y') %}
+{%-     do api_cmd_args.append(prior_to) %}
+{%-   else %}
+{%-     do api_cmd_args.append('--all') %}
+{%-   endif %}
+{%-   if controller.db_purge.get('verbose', True) %}
+{%-     do api_cmd_args.append('--verbose') %}
+{%-   endif %}
+{%-   if controller.db_purge.get('all_cells', True) %}
+{%-     do cmd_args.append('--all-cells') %}
+{%-   endif %}
+nova_api_db_clean:
+  cmd.run:
+  - name: "nova-manage db purge {{ api_cmd_args|join(' ') }}"
+  - runas: 'nova'
+  {%- if grains.get('noservices') or controller.get('role', 'primary') == 'secondary' %}
+  - onlyif: /bin/false
+  {%- endif %}
+{%- endif %}
+
+{%- endif %}