Configure cinder to use service user tokens

Long-running operations such as snapshot can sometimes overrun the
expiry of the user token. In such cases, post operations such as
cleaning up after a snapshot can fail when the cinder service needs
to cleanup resources.
This patch enables cinder to use service user tokens to supplement
the regular user token used to initiate the operation.
The identity service (keystone) will then authenticate a request
using the service user token if the user token has already expired.

Change-Id: I3c794ae27b543e1a5df60696bb4a351e9d11a387
Related-Prod: PROD-25985
diff --git a/README.rst b/README.rst
index a3a507c..6c97682 100644
--- a/README.rst
+++ b/README.rst
@@ -895,6 +895,30 @@
           max_retries: '-1'
           max_overflow: 40
 
+Configure cinder to use service user tokens:
+========
+Long-running operations such as snapshot can sometimes overrun the expiry of the user token.
+In such cases, post operations such as cleaning up after a snapshot can fail when the
+cinder service needs to cleanup resources.
+
+This pillar enables cinder to use service user tokens to supplement the regular user token
+used to initiate the operation. The identity service (keystone) will then authenticate
+a request using the service user token if the user token has already expired.
+
+.. code-block:: yaml
+
+   cinder:
+     controller:
+     enabled: True
+     ...
+       service_user:
+         enabled: True
+         auth_type: password
+         user_domain_id: default
+         project_domain_id: default
+         project_name: service
+         username: cinder
+         password: pswd
 
 Upgrades
 ========
diff --git a/cinder/files/queens/cinder.conf.controller.Debian b/cinder/files/queens/cinder.conf.controller.Debian
index d034ef5..9cb21bd 100644
--- a/cinder/files/queens/cinder.conf.controller.Debian
+++ b/cinder/files/queens/cinder.conf.controller.Debian
@@ -3247,6 +3247,15 @@
 # When True, if sending a user token to an REST API, also send a service token.
 #  (boolean value)
 #send_service_user_token = false
+{%- if controller.get('service_user', {}).get('enabled', True) %}
+send_service_user_token = True
+{%- set _data = {} %}
+{%- do _data.update(controller.get('identity', {})) %}
+{%- do _data.update(controller.get('service_user', {})) %}
+{%- if not _data.port == '5000' %}{% do _data.update({'port': '5000'}) %}{% endif %}
+{%- if 'cacert_file' not in _data.keys() %}{% do _data.update({'cacert_file': controller.cacert_file}) %}{% endif %}
+{%- include "oslo_templates/files/queens/keystoneauth/_type_"+ _data.get('auth_type','password') +".conf" %}
+{%- endif %}
 
 {%- if controller.get('barbican', {}).get('enabled', False) %}
 {%- set _data = controller.identity %}
diff --git a/cinder/files/queens/cinder.conf.volume.Debian b/cinder/files/queens/cinder.conf.volume.Debian
index 3c60abc..195ee91 100644
--- a/cinder/files/queens/cinder.conf.volume.Debian
+++ b/cinder/files/queens/cinder.conf.volume.Debian
@@ -3249,6 +3249,15 @@
 # When True, if sending a user token to an REST API, also send a service token.
 #  (boolean value)
 #send_service_user_token = false
+{%- if volume.get('service_user', {}).get('enabled', True) %}
+send_service_user_token = True
+{%- set _data = {} %}
+{%- do _data.update(volume.get('identity', {})) %}
+{%- do _data.update(volume.get('service_user', {})) %}
+{%- if not _data.port == '5000' %}{% do _data.update({'port': '5000'}) %}{% endif %}
+{%- if 'cacert_file' not in _data.keys() %}{% do _data.update({'cacert_file': volume.cacert_file}) %}{% endif %}
+{%- include "oslo_templates/files/queens/keystoneauth/_type_"+ _data.get('auth_type','password') +".conf" %}
+{%- endif %}
 
 {%- if volume.get('barbican', {}).get('enabled', False) %}
 {%- set _data = volume.identity %}
diff --git a/cinder/files/rocky/cinder.conf.controller.Debian b/cinder/files/rocky/cinder.conf.controller.Debian
index 7f349b3..e94b3fa 100644
--- a/cinder/files/rocky/cinder.conf.controller.Debian
+++ b/cinder/files/rocky/cinder.conf.controller.Debian
@@ -4016,6 +4016,14 @@
 # When True, if sending a user token to an REST API, also send a service token.
 #  (boolean value)
 #send_service_user_token = false
+{%- if controller.get('service_user', {}).get('enabled', True) %}
+send_service_user_token = True
+{%- set _data = {} %}
+{%- do _data.update(controller.get('identity', {})) %}
+{%- do _data.update(controller.get('service_user', {})) %}
+{%- if not _data.port == '5000' %}{% do _data.update({'port': '5000'}) %}{% endif %}
+{%- if 'cacert_file' not in _data.keys() %}{% do _data.update({'cacert_file': controller.cacert_file}) %}{% endif %}
+{%- include "oslo_templates/files/" ~ controller.version ~ "/keystoneauth/_type_"+ _data.get('auth_type','password') +".conf" %}
 
 # PEM encoded Certificate Authority to use when verifying HTTPs connections.
 # (string value)
@@ -4038,6 +4046,7 @@
 
 # Log requests to multiple loggers. (boolean value)
 #split_loggers = false
+{%- endif %}
 
 
 [ssl]
diff --git a/cinder/files/rocky/cinder.conf.volume.Debian b/cinder/files/rocky/cinder.conf.volume.Debian
index 2bbe2ba..11242fe 100644
--- a/cinder/files/rocky/cinder.conf.volume.Debian
+++ b/cinder/files/rocky/cinder.conf.volume.Debian
@@ -4019,6 +4019,14 @@
 # When True, if sending a user token to an REST API, also send a service token.
 #  (boolean value)
 #send_service_user_token = false
+{%- if volume.get('service_user', {}).get('enabled', True) %}
+send_service_user_token = True
+{%- set _data = {} %}
+{%- do _data.update(volume.get('identity', {})) %}
+{%- do _data.update(volume.get('service_user', {})) %}
+{%- if not _data.port == '5000' %}{% do _data.update({'port': '5000'}) %}{% endif %}
+{%- if 'cacert_file' not in _data.keys() %}{% do _data.update({'cacert_file': volume.cacert_file}) %}{% endif %}
+{%- include "oslo_templates/files/" ~ volume.version ~ "/keystoneauth/_type_"+ _data.get('auth_type','password') +".conf" %}
 
 # PEM encoded Certificate Authority to use when verifying HTTPs connections.
 # (string value)
@@ -4041,6 +4049,7 @@
 
 # Log requests to multiple loggers. (boolean value)
 #split_loggers = false
+{%- endif %}
 
 
 [ssl]
diff --git a/tests/pillar/control_cluster.sls b/tests/pillar/control_cluster.sls
index 05456c0..e1e6dc5 100644
--- a/tests/pillar/control_cluster.sls
+++ b/tests/pillar/control_cluster.sls
@@ -24,6 +24,14 @@
       user: cinder
       password: password
       endpoint_type: internalURL
+    service_user:
+      enabled: True
+      auth_type: password
+      user_domain_id: default
+      project_domain_id: default
+      project_name: service
+      username: cinder
+      password: password
     glance:
       host: 127.0.0.1
       port: 9292
diff --git a/tests/pillar/control_single.sls b/tests/pillar/control_single.sls
index cd1f7d9..9f6d66d 100644
--- a/tests/pillar/control_single.sls
+++ b/tests/pillar/control_single.sls
@@ -21,6 +21,14 @@
       user: cinder
       password: password
       endpoint_type: internalURL
+    service_user:
+      enabled: True
+      auth_type: password
+      user_domain_id: default
+      project_domain_id: default
+      project_name: service
+      username: cinder
+      password: password
     glance:
       host: 127.0.0.1
       port: 9292