From 195a32b1e913c93b081e9bc05a6d5d107817f327 Mon Sep 17 00:00:00 2001
From: zhubingbing <1392607554@qq.com>
Date: Fri, 12 Aug 2016 18:43:02 +0000
Subject: [PATCH] Add trove role

Add trove enable options and port configuration
Add trove groups to ansible inventory
Add defaults/main.yml
Add bootstrap Ansible task
Add config Ansible task
Add deploy Ansible task
Add pull Ansible task
Add register Ansible task
Add start Ansible task
Add upgrade Ansible task
Add reconfigure Ansible task
Add templatized json and .conf files for trove
Integrate trove with haproxy
Reference installation configuration link:http://docs.openstack.org/mitaka/install-guide-rdo/trove-install.html

Change-Id: I0bc6edbf2e495ce377f994f793bde979d5c55dbf
Implements: blueprint ansible-trove
---
 ansible/group_vars/all.yml                    |  3 +
 ansible/inventory/all-in-one                  | 13 ++++
 ansible/inventory/multinode                   | 13 ++++
 .../roles/haproxy/templates/haproxy.cfg.j2    | 16 ++++
 ansible/roles/trove/defaults/main.yml         | 39 ++++++++++
 ansible/roles/trove/meta/main.yml             |  3 +
 ansible/roles/trove/tasks/bootstrap.yml       | 41 ++++++++++
 .../roles/trove/tasks/bootstrap_service.yml   | 21 ++++++
 ansible/roles/trove/tasks/config.yml          | 45 +++++++++++
 ansible/roles/trove/tasks/deploy.yml          | 16 ++++
 ansible/roles/trove/tasks/main.yml            |  2 +
 ansible/roles/trove/tasks/pull.yml            | 21 ++++++
 ansible/roles/trove/tasks/reconfigure.yml     | 74 +++++++++++++++++++
 ansible/roles/trove/tasks/register.yml        | 40 ++++++++++
 ansible/roles/trove/tasks/start.yml           | 38 ++++++++++
 .../roles/trove/templates/trove-api.json.j2   | 18 +++++
 .../trove/templates/trove-conductor.conf.j2   | 13 ++++
 .../trove/templates/trove-conductor.json.j2   | 23 ++++++
 .../trove/templates/trove-taskmanager.conf.j2 | 26 +++++++
 .../trove/templates/trove-taskmanager.json.j2 | 23 ++++++
 ansible/roles/trove/templates/trove.conf.j2   | 34 +++++++++
 ansible/site.yml                              | 10 +++
 etc/kolla/passwords.yml                       |  3 +
 .../notes/add-trove-d531db316ded568a.yaml     |  3 +
 24 files changed, 538 insertions(+)
 create mode 100644 ansible/roles/trove/defaults/main.yml
 create mode 100644 ansible/roles/trove/meta/main.yml
 create mode 100644 ansible/roles/trove/tasks/bootstrap.yml
 create mode 100644 ansible/roles/trove/tasks/bootstrap_service.yml
 create mode 100644 ansible/roles/trove/tasks/config.yml
 create mode 100644 ansible/roles/trove/tasks/deploy.yml
 create mode 100644 ansible/roles/trove/tasks/main.yml
 create mode 100644 ansible/roles/trove/tasks/pull.yml
 create mode 100644 ansible/roles/trove/tasks/reconfigure.yml
 create mode 100644 ansible/roles/trove/tasks/register.yml
 create mode 100644 ansible/roles/trove/tasks/start.yml
 create mode 100644 ansible/roles/trove/templates/trove-api.json.j2
 create mode 100644 ansible/roles/trove/templates/trove-conductor.conf.j2
 create mode 100644 ansible/roles/trove/templates/trove-conductor.json.j2
 create mode 100644 ansible/roles/trove/templates/trove-taskmanager.conf.j2
 create mode 100644 ansible/roles/trove/templates/trove-taskmanager.json.j2
 create mode 100644 ansible/roles/trove/templates/trove.conf.j2
 create mode 100644 releasenotes/notes/add-trove-d531db316ded568a.yaml

diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml
index ca2c0c5bed..e0660b8027 100644
--- a/ansible/group_vars/all.yml
+++ b/ansible/group_vars/all.yml
@@ -195,6 +195,8 @@ influxdb_http_port: "8086"
 
 senlin_api_port: "8778"
 
+trove_api_port: "8779"
+
 etcd_client_port: "2379"
 etcd_peer_port: "2380"
 
@@ -286,6 +288,7 @@ enable_senlin: "no"
 enable_swift: "no"
 enable_telegraf: "no"
 enable_tempest: "no"
+enable_trove: "no"
 enable_vmtp: "no"
 enable_watcher: "no"
 
diff --git a/ansible/inventory/all-in-one b/ansible/inventory/all-in-one
index 17cb24d38b..70258ad100 100644
--- a/ansible/inventory/all-in-one
+++ b/ansible/inventory/all-in-one
@@ -133,6 +133,9 @@ control
 [vmtp:children]
 control
 
+[trove:children]
+control
+
 [watcher:children]
 control
 
@@ -272,6 +275,16 @@ barbican
 [barbican-worker:children]
 barbican
 
+# Trove
+[trove-api:children]
+trove
+
+[trove-conductor:children]
+trove
+
+[trove-taskmanager:children]
+trove
+
 # Heat
 [heat-api:children]
 heat
diff --git a/ansible/inventory/multinode b/ansible/inventory/multinode
index fa92b90441..e24e03440b 100644
--- a/ansible/inventory/multinode
+++ b/ansible/inventory/multinode
@@ -138,6 +138,9 @@ control
 [gnocchi:children]
 control
 
+[trove:children]
+control
+
 # Tempest
 [tempest:children]
 control
@@ -390,6 +393,16 @@ gnocchi
 [gnocchi-metricd:children]
 gnocchi
 
+# Trove
+[trove-api:children]
+trove
+
+[trove-conductor:children]
+trove
+
+[trove-taskmanager:children]
+trove
+
 # Multipathd
 [multipathd:children]
 compute
diff --git a/ansible/roles/haproxy/templates/haproxy.cfg.j2 b/ansible/roles/haproxy/templates/haproxy.cfg.j2
index 89b7997c64..e2cd06cc6b 100644
--- a/ansible/roles/haproxy/templates/haproxy.cfg.j2
+++ b/ansible/roles/haproxy/templates/haproxy.cfg.j2
@@ -568,6 +568,22 @@ listen aodh_api_external
 {% endif %}
 {% endif %}
 
+{% if enable_trove | bool %}
+listen trove_api
+  bind {{ kolla_internal_vip_address }}:{{ trove_api_port }}
+{% for host in groups['trove-api'] %}
+  server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ trove_api_port }} check inter 2000 rise 2 fall 5
+{% endfor %}
+{% if haproxy_enable_external_vip | bool %}
+
+listen trove_api_external
+  bind {{ kolla_external_vip_address }}:{{ trove_api_port }} {{ tls_bind_info }}
+{% for host in groups['trove-api'] %}
+  server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ trove_api_port }} check inter 2000 rise 2 fall 5
+{% endfor %}
+{% endif %}
+{% endif %}
+
 {% if enable_congress | bool %}
 listen congress_api
   bind {{ kolla_internal_vip_address }}:{{ congress_api_port }}
diff --git a/ansible/roles/trove/defaults/main.yml b/ansible/roles/trove/defaults/main.yml
new file mode 100644
index 0000000000..79ed9b3299
--- /dev/null
+++ b/ansible/roles/trove/defaults/main.yml
@@ -0,0 +1,39 @@
+---
+project_name: "trove"
+
+####################
+# Database
+####################
+trove_database_name: "trove"
+trove_database_user: "trove"
+trove_database_address: "{{ kolla_internal_fqdn }}:{{ database_port }}"
+
+
+####################
+# Docker
+####################
+trove_conductor_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ kolla_base_distro }}-{{ kolla_install_type }}-trove-conductor"
+trove_conductor_tag: "{{ openstack_release }}"
+trove_conductor_image_full: "{{ trove_conductor_image }}:{{ trove_conductor_tag }}"
+
+trove_api_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ kolla_base_distro }}-{{ kolla_install_type }}-trove-api"
+trove_api_tag: "{{ openstack_release }}"
+trove_api_image_full: "{{ trove_api_image }}:{{ trove_api_tag }}"
+
+trove_taskmanager_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ kolla_base_distro }}-{{ kolla_install_type }}-trove-taskmanager"
+trove_taskmanager_tag: "{{ openstack_release }}"
+trove_taskmanager_image_full: "{{ trove_taskmanager_image }}:{{ trove_taskmanager_tag }}"
+
+
+####################
+# OpenStack
+####################
+trove_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ trove_api_port }}/v1.0/%(tenant_id)s"
+trove_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ trove_api_port }}/v1.0/%(tenant_id)s"
+trove_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ trove_api_port }}/v1.0/%(tenant_id)s"
+
+trove_logging_debug: "{{ openstack_logging_debug }}"
+
+trove_keystone_user: "trove"
+
+openstack_trove_auth: "{'auth_url':'{{ openstack_auth.auth_url }}','username':'{{ openstack_auth.username }}','password':'{{ openstack_auth.password }}','project_name':'{{ openstack_auth.project_name }}','domain_name':'default'}"
diff --git a/ansible/roles/trove/meta/main.yml b/ansible/roles/trove/meta/main.yml
new file mode 100644
index 0000000000..6b4fff8fef
--- /dev/null
+++ b/ansible/roles/trove/meta/main.yml
@@ -0,0 +1,3 @@
+---
+dependencies:
+  - { role: common }
diff --git a/ansible/roles/trove/tasks/bootstrap.yml b/ansible/roles/trove/tasks/bootstrap.yml
new file mode 100644
index 0000000000..56f6bf12c1
--- /dev/null
+++ b/ansible/roles/trove/tasks/bootstrap.yml
@@ -0,0 +1,41 @@
+---
+- name: Creating trove database
+  command: docker exec -t kolla_toolbox /usr/bin/ansible localhost
+    -m mysql_db
+    -a "login_host='{{ database_address }}'
+        login_port='{{ database_port }}'
+        login_user='{{ database_user }}'
+        login_password='{{ database_password }}'
+        name='{{ trove_database_name }}'"
+  register: database
+  changed_when: "{{ database.stdout.find('localhost | SUCCESS => ') != -1 and
+                    (database.stdout.split('localhost | SUCCESS => ')[1]|from_json).changed }}"
+  failed_when: database.stdout.split()[2] != 'SUCCESS'
+  run_once: True
+  delegate_to: "{{ groups['trove-api'][0] }}"
+
+- name: Reading json from variable
+  set_fact:
+    database_created: "{{ (database.stdout.split('localhost | SUCCESS => ')[1]|from_json).changed }}"
+
+- name: Creating trove database user and setting permissions
+  command: docker exec -t kolla_toolbox /usr/bin/ansible localhost
+    -m mysql_user
+    -a "login_host='{{ database_address }}'
+        login_port='{{ database_port }}'
+        login_user='{{ database_user }}'
+        login_password='{{ database_password }}'
+        name='{{ trove_database_name }}'
+        password='{{ trove_database_password }}'
+        host='%'
+        priv='{{ trove_database_name }}.*:ALL'
+        append_privs='yes'"
+  register: database_user_create
+  changed_when: "{{ database_user_create.stdout.find('localhost | SUCCESS => ') != -1 and
+                    (database_user_create.stdout.split('localhost | SUCCESS => ')[1]|from_json).changed }}"
+  failed_when: database_user_create.stdout.split()[2] != 'SUCCESS'
+  run_once: True
+  delegate_to: "{{ groups['trove-api'][0] }}"
+
+- include: bootstrap_service.yml
+  when: database_created
diff --git a/ansible/roles/trove/tasks/bootstrap_service.yml b/ansible/roles/trove/tasks/bootstrap_service.yml
new file mode 100644
index 0000000000..9136554fc2
--- /dev/null
+++ b/ansible/roles/trove/tasks/bootstrap_service.yml
@@ -0,0 +1,21 @@
+---
+- name: Running trove bootstrap container
+  kolla_docker:
+    action: "start_container"
+    common_options: "{{ docker_common_options }}"
+    detach: False
+    environment:
+      KOLLA_BOOTSTRAP:
+      KOLLA_CONFIG_STRATEGY: "{{ config_strategy }}"
+    image: "{{ trove_api_image_full }}"
+    labels:
+      BOOTSTRAP:
+    name: "bootstrap_trove"
+    restart_policy: "never"
+    volumes:
+      - "{{ node_config_directory }}/trove-api/:{{ container_config_directory }}/:ro"
+      - "/etc/localtime:/etc/localtime:ro"
+      - "kolla_logs:/var/log/kolla/"
+      - "trove:/var/lib/trove/"
+  run_once: True
+  delegate_to: "{{ groups['trove-api'][0] }}"
diff --git a/ansible/roles/trove/tasks/config.yml b/ansible/roles/trove/tasks/config.yml
new file mode 100644
index 0000000000..b2481a0fdc
--- /dev/null
+++ b/ansible/roles/trove/tasks/config.yml
@@ -0,0 +1,45 @@
+---
+- name: Ensuring config directories exist
+  file:
+    path: "{{ node_config_directory }}/{{ item }}"
+    state: "directory"
+    recurse: yes
+  with_items:
+    - "trove-conductor"
+    - "trove-api"
+    - "trove-taskmanager"
+
+- name: Copying over config.json files for services
+  template:
+    src: "{{ item }}.json.j2"
+    dest: "{{ node_config_directory }}/{{ item }}/config.json"
+  with_items:
+    - "trove-conductor"
+    - "trove-api"
+    - "trove-taskmanager"
+
+- name: Copying over trove conf files
+  template:
+    src: "{{ item }}.conf.j2"
+    dest: "{{ node_config_directory }}/{{ item }}/{{ item }}.conf"
+  with_items:
+    - "trove-conductor"
+    - "trove-taskmanager"
+
+- name: Copying over trove.conf
+  merge_configs:
+    vars:
+      service_name: "{{ item }}"
+    sources:
+      - "{{ role_path }}/templates/trove.conf.j2"
+      - "{{ node_custom_config }}/global.conf"
+      - "{{ node_custom_config }}/database.conf"
+      - "{{ node_custom_config }}/messaging.conf"
+      - "{{ node_custom_config }}/trove.conf"
+      - "{{ node_custom_config }}/trove/{{ item }}.conf"
+      - "{{ node_custom_config }}/trove/{{ inventory_hostname }}/trove.conf"
+    dest: "{{ node_config_directory }}/{{ item }}/trove.conf"
+  with_items:
+    - "trove-conductor"
+    - "trove-api"
+    - "trove-taskmanager"
diff --git a/ansible/roles/trove/tasks/deploy.yml b/ansible/roles/trove/tasks/deploy.yml
new file mode 100644
index 0000000000..833d0a36ba
--- /dev/null
+++ b/ansible/roles/trove/tasks/deploy.yml
@@ -0,0 +1,16 @@
+---
+- include: register.yml
+  when: inventory_hostname in groups['trove-api']
+
+- include: config.yml
+  when: inventory_hostname in groups['trove-api'] or
+        inventory_hostname in groups['trove-conductor'] or
+        inventory_hostname in groups['trove-taskmanager']
+
+- include: bootstrap.yml
+  when: inventory_hostname in groups['trove-api']
+
+- include: start.yml
+  when: inventory_hostname in groups['trove-api'] or
+        inventory_hostname in groups['trove-conductor'] or
+        inventory_hostname in groups['trove-taskmanager']
diff --git a/ansible/roles/trove/tasks/main.yml b/ansible/roles/trove/tasks/main.yml
new file mode 100644
index 0000000000..b017e8b4ad
--- /dev/null
+++ b/ansible/roles/trove/tasks/main.yml
@@ -0,0 +1,2 @@
+---
+- include: "{{ action }}.yml"
diff --git a/ansible/roles/trove/tasks/pull.yml b/ansible/roles/trove/tasks/pull.yml
new file mode 100644
index 0000000000..9aaf50b0a3
--- /dev/null
+++ b/ansible/roles/trove/tasks/pull.yml
@@ -0,0 +1,21 @@
+---
+- name: Pulling trove-api image
+  kolla_docker:
+    action: "pull_image"
+    common_options: "{{ docker_common_options }}"
+    image: "{{ trove_api_image_full }}"
+  when: inventory_hostname in groups['trove-api']
+
+- name: Pulling trove-conductor image
+  kolla_docker:
+    action: "pull_image"
+    common_options: "{{ docker_common_options }}"
+    image: "{{ trove_conductor_image_full }}"
+  when: inventory_hostname in groups['trove-conductor']
+
+- name: Pulling trove-taskmanager image
+  kolla_docker:
+    action: "pull_image"
+    common_options: "{{ docker_common_options }}"
+    image: "{{ trove_taskmanager_image_full }}"
+  when: inventory_hostname in groups['trove-taskmanager']
diff --git a/ansible/roles/trove/tasks/reconfigure.yml b/ansible/roles/trove/tasks/reconfigure.yml
new file mode 100644
index 0000000000..227aff576e
--- /dev/null
+++ b/ansible/roles/trove/tasks/reconfigure.yml
@@ -0,0 +1,74 @@
+---
+- name: Ensuring the containers up
+  kolla_docker:
+    name: "{{ item.name }}"
+    action: "get_container_state"
+  register: container_state
+  failed_when: container_state.Running == false
+  when: inventory_hostname in groups[item.group]
+  with_items:
+    - { name: trove_api, group: trove-api }
+    - { name: trove_conductor, group: trove-conductor }
+    - { name: trove_taskmanager, group: trove-taskmanager }
+
+- include: config.yml
+
+- name: Check the configs
+  command: docker exec {{ item.name }} /usr/local/bin/kolla_set_configs --check
+  changed_when: false
+  failed_when: false
+  register: check_results
+  when: inventory_hostname in groups[item.group]
+  with_items:
+    - { name: trove_api, group: trove-api }
+    - { name: trove_conductor, group: trove-conductor }
+    - { name: trove_taskmanager, group: trove-taskmanager }
+
+# NOTE(jeffrey4l): when config_strategy == 'COPY_ALWAYS'
+# and container env['KOLLA_CONFIG_STRATEGY'] == 'COPY_ONCE',
+# just remove the container and start again
+- name: Containers config strategy
+  kolla_docker:
+    name: "{{ item.name }}"
+    action: "get_container_env"
+  register: container_envs
+  when: inventory_hostname in groups[item.group]
+  with_items:
+    - { name: trove_api, group: trove-api }
+    - { name: trove_conductor, group: trove-conductor }
+    - { name: trove_taskmanager, group: trove-taskmanager }
+
+- name: Remove the containers
+  kolla_docker:
+    name: "{{ item[0]['name'] }}"
+    action: "remove_container"
+  register: remove_containers
+  when:
+    - inventory_hostname in groups[item[0]['group']]
+    - config_strategy == "COPY_ONCE" or item[1]['KOLLA_CONFIG_STRATEGY'] == 'COPY_ONCE'
+    - item[2]['rc'] == 1
+  with_together:
+    - [{ name: trove_api, group: trove-api },
+       { name: trove_conductor, group: trove-conductor },
+       { name: trove_taskmanager, group: trove-taskmanager }]
+    - "{{ container_envs.results }}"
+    - "{{ check_results.results }}"
+
+- include: start.yml
+  when: remove_containers.changed
+
+- name: Restart containers
+  kolla_docker:
+    name: "{{ item[0]['name'] }}"
+    action: "restart_container"
+  when:
+    - inventory_hostname in groups[item[0]['group']]
+    - config_strategy == 'COPY_ALWAYS'
+    - item[1]['KOLLA_CONFIG_STRATEGY'] != 'COPY_ONCE'
+    - item[2]['rc'] == 1
+  with_together:
+    - [{ name: trove_api, group: trove-api },
+       { name: trove_conductor, group: trove-conductor },
+       { name: trove_taskmanager, group: trove-taskmanager }]
+    - "{{ container_envs.results }}"
+    - "{{ check_results.results }}"
diff --git a/ansible/roles/trove/tasks/register.yml b/ansible/roles/trove/tasks/register.yml
new file mode 100644
index 0000000000..f2d5928867
--- /dev/null
+++ b/ansible/roles/trove/tasks/register.yml
@@ -0,0 +1,40 @@
+---
+- name: Creating the Trove service and endpoint
+  command: docker exec -t kolla_toolbox /usr/bin/ansible localhost
+    -m kolla_keystone_service
+    -a "service_name=trove
+        service_type=database
+        description='Trove Database Service'
+        endpoint_region={{ openstack_region_name }}
+        url='{{ item.url }}'
+        interface='{{ item.interface }}'
+        region_name={{ openstack_region_name }}
+        auth={{ '{{ openstack_trove_auth }}' }}"
+    -e "{'openstack_trove_auth':{{ openstack_trove_auth }}}"
+  register: trove_endpoint
+  changed_when: "{{ trove_endpoint.stdout.find('localhost | SUCCESS => ') != -1 and (trove_endpoint.stdout.split('localhost | SUCCESS => ')[1]|from_json).changed }}"
+  until: trove_endpoint.stdout.split()[2] == 'SUCCESS'
+  retries: 10
+  delay: 5
+  run_once: True
+  with_items:
+    - {'interface': 'admin', 'url': '{{ trove_admin_endpoint }}'}
+    - {'interface': 'internal', 'url': '{{ trove_internal_endpoint }}'}
+    - {'interface': 'public', 'url': '{{ trove_public_endpoint }}'}
+
+- name: Creating the Trove project, user, and role
+  command: docker exec -t kolla_toolbox /usr/bin/ansible localhost
+    -m kolla_keystone_user
+    -a "project=service
+        user=trove
+        password={{ trove_keystone_password }}
+        role=admin
+        region_name={{ openstack_region_name }}
+        auth={{ '{{ openstack_trove_auth }}' }}"
+    -e "{'openstack_trove_auth':{{ openstack_trove_auth }}}"
+  register: trove_user
+  changed_when: "{{ trove_user.stdout.find('localhost | SUCCESS => ') != -1 and (trove_user.stdout.split('localhost | SUCCESS => ')[1]|from_json).changed }}"
+  until: trove_user.stdout.split()[2] == 'SUCCESS'
+  retries: 10
+  delay: 5
+  run_once: True
diff --git a/ansible/roles/trove/tasks/start.yml b/ansible/roles/trove/tasks/start.yml
new file mode 100644
index 0000000000..34a767a40d
--- /dev/null
+++ b/ansible/roles/trove/tasks/start.yml
@@ -0,0 +1,38 @@
+---
+- name: Starting trove-api container
+  kolla_docker:
+    action: "start_container"
+    common_options: "{{ docker_common_options }}"
+    image: "{{ trove_api_image_full }}"
+    name: "trove_api"
+    volumes:
+      - "{{ node_config_directory }}/trove-api/:{{ container_config_directory }}/:ro"
+      - "/etc/localtime:/etc/localtime:ro"
+      - "kolla_logs:/var/log/kolla/"
+  when: inventory_hostname in groups['trove-api']
+
+- name: Starting trove-conductor container
+  kolla_docker:
+    action: "start_container"
+    common_options: "{{ docker_common_options }}"
+    image: "{{ trove_conductor_image_full }}"
+    name: "trove_conductor"
+    volumes:
+      - "{{ node_config_directory }}/trove-conductor/:{{ container_config_directory }}/:ro"
+      - "/etc/localtime:/etc/localtime:ro"
+      - "kolla_logs:/var/log/kolla/"
+      - "trove:/var/lib/trove/"
+  when: inventory_hostname in groups['trove-conductor']
+
+- name: Starting trove-taskmanager container
+  kolla_docker:
+    action: "start_container"
+    common_options: "{{ docker_common_options }}"
+    image: "{{ trove_taskmanager_image_full }}"
+    name: "trove_taskmanager"
+    volumes:
+      - "{{ node_config_directory }}/trove-taskmanager/:{{ container_config_directory }}/:ro"
+      - "/etc/localtime:/etc/localtime:ro"
+      - "kolla_logs:/var/log/kolla/"
+      - "trove:/var/lib/trove/"
+  when: inventory_hostname in groups['trove-taskmanager']
diff --git a/ansible/roles/trove/templates/trove-api.json.j2 b/ansible/roles/trove/templates/trove-api.json.j2
new file mode 100644
index 0000000000..01831afbcc
--- /dev/null
+++ b/ansible/roles/trove/templates/trove-api.json.j2
@@ -0,0 +1,18 @@
+{
+    "command": "trove-api --config-file=/etc/trove/trove.conf",
+    "config_files": [
+        {
+            "source": "{{ container_config_directory }}/trove.conf",
+            "dest": "/etc/trove/trove.conf",
+            "owner": "trove",
+            "perm": "0600"
+        }
+    ],
+    "permissions": [
+        {
+            "path": "/var/log/kolla/trove",
+            "owner": "trove:trove",
+            "recurse": true
+        }
+    ]
+}
diff --git a/ansible/roles/trove/templates/trove-conductor.conf.j2 b/ansible/roles/trove/templates/trove-conductor.conf.j2
new file mode 100644
index 0000000000..1f418e0574
--- /dev/null
+++ b/ansible/roles/trove/templates/trove-conductor.conf.j2
@@ -0,0 +1,13 @@
+[DEFAULT]
+debug = {{ trove_logging_debug }}
+
+log_dir = /var/log/kolla/trove
+trove_auth_url = {{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ keystone_public_port }}/v3
+conductor_manager = trove.conductor.manager.Manager
+control_exchange = trove
+
+transport_url = rabbit://{% for host in groups['rabbitmq'] %}{{ rabbitmq_user }}:{{ rabbitmq_password }}@{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ rabbitmq_port }}{% if not loop.last %},{% endif %}{% endfor %}
+
+[database]
+connection = mysql+pymysql://{{ trove_database_user }}:{{ trove_database_password }}@{{ trove_database_address }}/{{ trove_database_name }}
+max_retries = -1
diff --git a/ansible/roles/trove/templates/trove-conductor.json.j2 b/ansible/roles/trove/templates/trove-conductor.json.j2
new file mode 100644
index 0000000000..1d5b0601b5
--- /dev/null
+++ b/ansible/roles/trove/templates/trove-conductor.json.j2
@@ -0,0 +1,23 @@
+{
+    "command": "trove-conductor --config-file=/etc/trove/trove-conductor.conf",
+    "config_files": [
+        {
+            "source": "{{ container_config_directory }}/trove-conductor.conf",
+            "dest": "/etc/trove/trove-conductor.conf",
+            "owner": "trove",
+            "perm": "0600"
+        }
+    ],
+    "permissions": [
+        {
+            "path": "/var/log/kolla/trove",
+            "owner": "trove:trove",
+            "recurse": true
+        },
+        {
+            "path": "/var/lib/trove",
+            "owner": "trove:trove",
+            "recurse": true
+        }
+    ]
+}
diff --git a/ansible/roles/trove/templates/trove-taskmanager.conf.j2 b/ansible/roles/trove/templates/trove-taskmanager.conf.j2
new file mode 100644
index 0000000000..90fce84a1b
--- /dev/null
+++ b/ansible/roles/trove/templates/trove-taskmanager.conf.j2
@@ -0,0 +1,26 @@
+[DEFAULT]
+debug = {{ trove_logging_debug }}
+
+log_dir = /var/log/kolla/trove
+
+nova_proxy_admin_pass = {{ trove_keystone_password }}
+nova_proxy_admin_tenant_name = services
+nova_proxy_admin_user = trove
+taskmanager_manager = trove.taskmanager.manager.Manager
+
+transport_url = rabbit://{% for host in groups['rabbitmq'] %}{{ rabbitmq_user }}:{{ rabbitmq_password }}@{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ rabbitmq_port }}{% if not loop.last %},{% endif %}{% endfor %}
+
+trove_auth_url = {{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ keystone_public_port }}/v3
+{% if enable_nova | bool %}
+nova_compute_url = {{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ nova_api_port }}/v2
+{% endif %}
+{% if enable_cinder | bool %}
+cinder_url = {{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ cinder_api_port }}/v1
+{% endif %}
+{% if enable_swift | bool %}
+swift_url = {{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ swift_api_port }}/AUTH_
+{% endif %}
+
+[database]
+connection = mysql+pymysql://{{ trove_database_user }}:{{ trove_database_password }}@{{ trove_database_address}}/{{ trove_database_name }}
+max_retries = -1
diff --git a/ansible/roles/trove/templates/trove-taskmanager.json.j2 b/ansible/roles/trove/templates/trove-taskmanager.json.j2
new file mode 100644
index 0000000000..41c4321e3e
--- /dev/null
+++ b/ansible/roles/trove/templates/trove-taskmanager.json.j2
@@ -0,0 +1,23 @@
+{
+    "command": "trove-taskmanager --config-file=/etc/trove/trove-taskmanager.conf",
+    "config_files": [
+        {
+            "source": "{{ container_config_directory }}/trove-taskmanager.conf",
+            "dest": "/etc/trove/trove-taskmanager.conf",
+            "owner": "trove",
+            "perm": "0600"
+        }
+    ],
+    "permissions": [
+        {
+            "path": "/var/log/kolla/trove",
+            "owner": "trove:trove",
+            "recurse": true
+        },
+        {
+            "path": "/var/lib/trove",
+            "owner": "trove:trove",
+            "recurse": true
+        }
+    ]
+}
diff --git a/ansible/roles/trove/templates/trove.conf.j2 b/ansible/roles/trove/templates/trove.conf.j2
new file mode 100644
index 0000000000..f978825d7c
--- /dev/null
+++ b/ansible/roles/trove/templates/trove.conf.j2
@@ -0,0 +1,34 @@
+[DEFAULT]
+debug = {{ trove_logging_debug }}
+
+log_dir = /var/log/kolla/trove
+port = {{ trove_api_port }}
+host = {{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}
+trove_api_workers = {{ openstack_service_workers }}
+auth_strategy = keystone
+
+transport_url = rabbit://{% for host in groups['rabbitmq'] %}{{ rabbitmq_user }}:{{ rabbitmq_password }}@{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ rabbitmq_port }}{% if not loop.last %},{% endif %}{% endfor %}
+
+trove_auth_url = {{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ keystone_public_port }}/v3
+{% if enable_nova | bool %}
+nova_compute_url = {{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ nova_api_port }}/v2
+{% endif %}
+{% if enable_cinder | bool %}
+cinder_url = {{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ cinder_api_port }}/v1
+{% endif %}
+{% if enable_swift | bool %}
+swift_url = {{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ swift_api_port }}/AUTH_
+{% endif %}
+
+[database]
+connection = mysql+pymysql://{{ trove_database_user }}:{{ trove_database_password }}@{{ trove_database_address }}/{{ trove_database_name }}
+
+[keystone_authtoken]
+auth_uri = {{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ keystone_public_port }}
+project_domain_name = Default
+project_name = service
+user_domain_name = Default
+username = {{ trove_keystone_user }}
+password = {{ trove_keystone_password }}
+auth_url = {{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ keystone_admin_port }}
+auth_type = password
diff --git a/ansible/site.yml b/ansible/site.yml
index 6878a1f77d..cc6361284c 100644
--- a/ansible/site.yml
+++ b/ansible/site.yml
@@ -368,6 +368,16 @@
         tags: vmtp,
         when: enable_vmtp | bool }
 
+- hosts:
+    - trove-api
+    - trove-conductor
+    - trove-taskmanager
+  serial: '{{ serial|default("0") }}'
+  roles:
+    - { role: trove,
+        tags: trove,
+        when: enable_trove | bool }
+
 - hosts:
     - watcher-api
     - watcher-engine
diff --git a/etc/kolla/passwords.yml b/etc/kolla/passwords.yml
index b0995abc07..a6e98b3b28 100644
--- a/etc/kolla/passwords.yml
+++ b/etc/kolla/passwords.yml
@@ -78,6 +78,9 @@ magnum_keystone_password:
 mistral_database_password:
 mistral_keystone_password:
 
+trove_database_password:
+trove_keystone_password:
+
 ceilometer_database_password:
 ceilometer_keystone_password:
 
diff --git a/releasenotes/notes/add-trove-d531db316ded568a.yaml b/releasenotes/notes/add-trove-d531db316ded568a.yaml
new file mode 100644
index 0000000000..bdbc99a1a8
--- /dev/null
+++ b/releasenotes/notes/add-trove-d531db316ded568a.yaml
@@ -0,0 +1,3 @@
+---
+features:
+  - Implement Trove ansible role