From c11f9f521d5833a8dd41bfdb32c1927daa42b00c Mon Sep 17 00:00:00 2001
From: Doug Szumski <doug@stackhpc.com>
Date: Wed, 28 Mar 2018 17:54:19 +0100
Subject: [PATCH] Support deploying the Monasca API

Deploys the Monasca API with mod_wsgi + Apache.

Co-Authored-By: Mark Goddard <mark@stackhpc.com>

Partially-Implements: blueprint monasca-roles
Change-Id: I3e03762217fbef1fb0cbff6239abb109cbec226b
---
 ansible/group_vars/all.yml                    |  7 +-
 ansible/inventory/all-in-one                  |  7 ++
 ansible/inventory/multinode                   |  7 ++
 ansible/roles/common/tasks/config.yml         |  1 +
 .../conf/filter/01-rewrite-0.12.conf.j2       |  2 +-
 .../conf/filter/01-rewrite-0.14.conf.j2       |  2 +-
 .../templates/conf/input/00-global.conf.j2    |  1 +
 .../templates/cron-logrotate-monasca.conf.j2  |  3 +
 ansible/roles/common/templates/cron.json.j2   |  1 +
 .../roles/haproxy/templates/haproxy.cfg.j2    | 19 +++++
 ansible/roles/monasca/defaults/main.yml       | 59 ++++++++++++++
 ansible/roles/monasca/handlers/main.yml       | 21 +++++
 ansible/roles/monasca/meta/main.yml           |  3 +
 ansible/roles/monasca/tasks/bootstrap.yml     | 51 ++++++++++++
 .../roles/monasca/tasks/bootstrap_service.yml | 24 ++++++
 ansible/roles/monasca/tasks/check.yml         |  1 +
 ansible/roles/monasca/tasks/config.yml        | 80 +++++++++++++++++++
 ansible/roles/monasca/tasks/deploy.yml        | 15 ++++
 ansible/roles/monasca/tasks/main.yml          |  2 +
 ansible/roles/monasca/tasks/precheck.yml      | 16 ++++
 ansible/roles/monasca/tasks/pull.yml          | 10 +++
 ansible/roles/monasca/tasks/reconfigure.yml   |  2 +
 ansible/roles/monasca/tasks/register.yml      | 44 ++++++++++
 ansible/roles/monasca/tasks/upgrade.yml       |  5 ++
 .../templates/monasca-api/api-config.conf.j2  | 59 ++++++++++++++
 .../templates/monasca-api/api-config.ini.j2   | 14 ++++
 .../templates/monasca-api/monasca-api.json.j2 | 32 ++++++++
 .../templates/monasca-api/wsgi-api.conf.j2    | 24 ++++++
 ansible/site.yml                              |  9 +++
 etc/kolla/globals.yml                         |  1 +
 etc/kolla/passwords.yml                       |  3 +
 .../add-monasca-api-eb536dd5a6d77563.yaml     |  5 ++
 32 files changed, 526 insertions(+), 4 deletions(-)
 create mode 100644 ansible/roles/common/templates/cron-logrotate-monasca.conf.j2
 create mode 100644 ansible/roles/monasca/defaults/main.yml
 create mode 100644 ansible/roles/monasca/handlers/main.yml
 create mode 100644 ansible/roles/monasca/meta/main.yml
 create mode 100644 ansible/roles/monasca/tasks/bootstrap.yml
 create mode 100644 ansible/roles/monasca/tasks/bootstrap_service.yml
 create mode 100644 ansible/roles/monasca/tasks/check.yml
 create mode 100644 ansible/roles/monasca/tasks/config.yml
 create mode 100644 ansible/roles/monasca/tasks/deploy.yml
 create mode 100644 ansible/roles/monasca/tasks/main.yml
 create mode 100644 ansible/roles/monasca/tasks/precheck.yml
 create mode 100644 ansible/roles/monasca/tasks/pull.yml
 create mode 100644 ansible/roles/monasca/tasks/reconfigure.yml
 create mode 100644 ansible/roles/monasca/tasks/register.yml
 create mode 100644 ansible/roles/monasca/tasks/upgrade.yml
 create mode 100644 ansible/roles/monasca/templates/monasca-api/api-config.conf.j2
 create mode 100644 ansible/roles/monasca/templates/monasca-api/api-config.ini.j2
 create mode 100644 ansible/roles/monasca/templates/monasca-api/monasca-api.json.j2
 create mode 100644 ansible/roles/monasca/templates/monasca-api/wsgi-api.conf.j2
 create mode 100644 releasenotes/notes/add-monasca-api-eb536dd5a6d77563.yaml

diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml
index 5ad218ab8..9be48d704 100644
--- a/ansible/group_vars/all.yml
+++ b/ansible/group_vars/all.yml
@@ -235,6 +235,8 @@ memcached_port: "11211"
 
 mistral_api_port: "8989"
 
+monasca_api_port: "8070"
+
 mongodb_port: "27017"
 mongodb_web_port: "28017"
 
@@ -438,12 +440,12 @@ enable_horizon_vitrage: "{{ enable_vitrage | bool }}"
 enable_horizon_watcher: "{{ enable_watcher | bool }}"
 enable_horizon_zun: "{{ enable_zun | bool }}"
 enable_hyperv: "no"
-enable_influxdb: "no"
+enable_influxdb: "{{ enable_monasca | bool }}"
 enable_ironic: "no"
 enable_ironic_pxe_uefi: "no"
 enable_iscsid: "{{ (enable_cinder | bool and enable_cinder_backend_iscsi | bool) or enable_ironic | bool }}"
 enable_karbor: "no"
-enable_kafka: "no"
+enable_kafka: "{{ enable_monasca | bool }}"
 enable_kuryr: "no"
 enable_magnum: "no"
 enable_manila: "no"
@@ -452,6 +454,7 @@ enable_manila_backend_hnas: "no"
 enable_manila_backend_cephfs_native: "no"
 enable_manila_backend_cephfs_nfs: "no"
 enable_mistral: "no"
+enable_monasca: "no"
 enable_mongodb: "no"
 enable_multipathd: "no"
 enable_murano: "no"
diff --git a/ansible/inventory/all-in-one b/ansible/inventory/all-in-one
index 89ef1c8a2..042f0caa7 100644
--- a/ansible/inventory/all-in-one
+++ b/ansible/inventory/all-in-one
@@ -100,6 +100,9 @@ control
 [qdrouterd:children]
 control
 
+[monasca:children]
+monitoring
+
 [mongodb:children]
 control
 
@@ -433,6 +436,10 @@ murano
 [murano-engine:children]
 murano
 
+# Monasca
+[monasca-api:children]
+monasca
+
 # Ironic
 [ironic-api:children]
 ironic
diff --git a/ansible/inventory/multinode b/ansible/inventory/multinode
index b19f84a02..58e02990d 100644
--- a/ansible/inventory/multinode
+++ b/ansible/inventory/multinode
@@ -122,6 +122,9 @@ control
 [qdrouterd:children]
 control
 
+[monasca:children]
+monitoring
+
 [mongodb:children]
 control
 
@@ -442,6 +445,10 @@ murano
 [murano-engine:children]
 murano
 
+# Monasca
+[monasca-api:children]
+monasca
+
 # Ironic
 [ironic-api:children]
 ironic
diff --git a/ansible/roles/common/tasks/config.yml b/ansible/roles/common/tasks/config.yml
index bc5ec7d9f..cd95eec6a 100644
--- a/ansible/roles/common/tasks/config.yml
+++ b/ansible/roles/common/tasks/config.yml
@@ -204,6 +204,7 @@
     - { name: "manila", enabled: "{{ enable_manila }}" }
     - { name: "mariadb", enabled: "{{ enable_mariadb }}" }
     - { name: "mistral", enabled: "{{ enable_mistral }}" }
+    - { name: "monasca", enabled: "{{ enable_monasca }}" }
     - { name: "mongodb", enabled: "{{ enable_mongodb }}" }
     - { name: "murano", enabled: "{{ enable_murano }}" }
     - { name: "neutron", enabled: "{{ enable_neutron }}" }
diff --git a/ansible/roles/common/templates/conf/filter/01-rewrite-0.12.conf.j2 b/ansible/roles/common/templates/conf/filter/01-rewrite-0.12.conf.j2
index 4179bd1d8..e5e5c76d7 100644
--- a/ansible/roles/common/templates/conf/filter/01-rewrite-0.12.conf.j2
+++ b/ansible/roles/common/templates/conf/filter/01-rewrite-0.12.conf.j2
@@ -1,7 +1,7 @@
 <match kolla.var.log.kolla.*.*.log>
     @type rewrite_tag_filter
     capitalize_regex_backreference yes
-    rewriterule1 programname ^(cinder-api-access|cloudkitty-api-access|gnocchi-api-access|horizon-access|keystone-apache-admin-access|keystone-apache-public-access|placement-api-access|panko-api-access)$ apache_access
+    rewriterule1 programname ^(cinder-api-access|cloudkitty-api-access|gnocchi-api-access|horizon-access|keystone-apache-admin-access|keystone-apache-public-access|monasca-api-access|placement-api-access|panko-api-access)$ apache_access
     rewriterule2 programname ^(aodh_wsgi_access|barbican-api|zun_api_wsgi_access|vitrage_wsgi_access)$ wsgi_access
     rewriterule3 programname ^(nova-api|nova-compute|nova-compute-ironic|nova-conductor|nova-consoleauth|nova-manage|nova-novncproxy|nova-scheduler|nova-placement-api|placement-api|privsep-helper)$ openstack_python
     rewriterule4 programname ^(sahara-api|sahara-engine)$ openstack_python
diff --git a/ansible/roles/common/templates/conf/filter/01-rewrite-0.14.conf.j2 b/ansible/roles/common/templates/conf/filter/01-rewrite-0.14.conf.j2
index 4316c2890..e1cf581c7 100644
--- a/ansible/roles/common/templates/conf/filter/01-rewrite-0.14.conf.j2
+++ b/ansible/roles/common/templates/conf/filter/01-rewrite-0.14.conf.j2
@@ -3,7 +3,7 @@
     capitalize_regex_backreference yes
   <rule>
     key     programname
-    pattern ^(cinder-api-access|cloudkitty-api-access|gnocchi-api-access|horizon-access|keystone-apache-admin-access|keystone-apache-public-access|placement-api-access|panko-api-access)$
+    pattern ^(cinder-api-access|cloudkitty-api-access|gnocchi-api-access|horizon-access|keystone-apache-admin-access|keystone-apache-public-access|monasca-api-access|placement-api-access|panko-api-access)$
     tag apache_access
   </rule>
   <rule>
diff --git a/ansible/roles/common/templates/conf/input/00-global.conf.j2 b/ansible/roles/common/templates/conf/input/00-global.conf.j2
index cd1e4ac17..448478bd5 100644
--- a/ansible/roles/common/templates/conf/input/00-global.conf.j2
+++ b/ansible/roles/common/templates/conf/input/00-global.conf.j2
@@ -21,6 +21,7 @@
     ( 'magnum', enable_magnum ),
     ( 'manila', enable_manila ),
     ( 'mistral', enable_mistral ),
+    ( 'monasca', enable_monasca ),
     ( 'mongodb', enable_mongodb ),
     ( 'murano', enable_murano ),
     ( 'neutron', enable_neutron ),
diff --git a/ansible/roles/common/templates/cron-logrotate-monasca.conf.j2 b/ansible/roles/common/templates/cron-logrotate-monasca.conf.j2
new file mode 100644
index 000000000..6f0f5ea57
--- /dev/null
+++ b/ansible/roles/common/templates/cron-logrotate-monasca.conf.j2
@@ -0,0 +1,3 @@
+"/var/log/kolla/monasca/*.log"
+{
+}
diff --git a/ansible/roles/common/templates/cron.json.j2 b/ansible/roles/common/templates/cron.json.j2
index 3ee2fb0c5..eca7cf76a 100644
--- a/ansible/roles/common/templates/cron.json.j2
+++ b/ansible/roles/common/templates/cron.json.j2
@@ -34,6 +34,7 @@
     ( 'manila', enable_manila ),
     ( 'mariadb', enable_mariadb ),
     ( 'mistral', enable_mistral ),
+    ( 'monasca', enable_monasca ),
     ( 'mongodb', enable_mongodb ),
     ( 'murano', enable_murano ),
     ( 'neutron', enable_neutron ),
diff --git a/ansible/roles/haproxy/templates/haproxy.cfg.j2 b/ansible/roles/haproxy/templates/haproxy.cfg.j2
index 1eefd9d0f..1da33c008 100644
--- a/ansible/roles/haproxy/templates/haproxy.cfg.j2
+++ b/ansible/roles/haproxy/templates/haproxy.cfg.j2
@@ -167,6 +167,25 @@ listen influxdb_http
 {% endfor %}
 {% endif %}
 
+{% if enable_monasca | bool %}
+listen monasca_api_internal
+  bind {{ kolla_internal_vip_address }}:{{ monasca_api_port }}
+  http-request del-header X-Forwarded-Proto if { ssl_fc }
+{% for host in groups['monasca-api'] %}
+  server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ monasca_api_port }} check inter 2000 rise 2 fall 5
+{% endfor %}
+
+{% if haproxy_enable_external_vip | bool %}
+listen monasca_api_external
+  bind {{ kolla_external_vip_address }}:{{ monasca_api_port }} {{ tls_bind_info }}
+  http-request del-header X-Forwarded-Proto if { ssl_fc }
+  http-request set-header X-Forwarded-Proto https if { ssl_fc }
+{% for host in groups['monasca-api'] %}
+  server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ monasca_api_port }} check inter 2000 rise 2 fall 5
+{% endfor %}
+{% endif %}
+{% endif %}
+
 {% if enable_nova | bool %}
 listen nova_api
   bind {{ kolla_internal_vip_address }}:{{ nova_api_port }}
diff --git a/ansible/roles/monasca/defaults/main.yml b/ansible/roles/monasca/defaults/main.yml
new file mode 100644
index 000000000..593aad256
--- /dev/null
+++ b/ansible/roles/monasca/defaults/main.yml
@@ -0,0 +1,59 @@
+---
+monasca_services:
+  monasca-api:
+    container_name: monasca_api
+    group: monasca-api
+    enabled: true
+    image: "{{ monasca_api_image_full }}"
+    volumes:
+      - "{{ node_config_directory }}/monasca-api/:{{ container_config_directory }}/:ro"
+      - "/etc/localtime:/etc/localtime:ro"
+      - "kolla_logs:/var/log/kolla"
+
+####################
+# Databases
+####################
+monasca_database_name: "monasca"
+monasca_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}monasca{% endif %}"
+monasca_database_address: "{{ database_address }}:{{ database_port }}"
+
+monasca_influxdb_name: "monasca"
+monasca_influxdb_address: "{{ kolla_internal_fqdn }}"
+monasca_influxdb_http_port: "{{ influxdb_http_port }}"
+
+####################
+# Monasca
+####################
+monasca_kafka: "{% for host in groups['kafka'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ kafka_port }}{% if not loop.last %},{% endif %}{% endfor %}"
+monasca_memcached_servers: "{% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}"
+
+####################
+# Docker
+####################
+monasca_install_type: "{{ kolla_install_type }}"
+monasca_tag: "{{ openstack_release }}"
+
+monasca_api_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ kolla_base_distro }}-{{ monasca_install_type }}-monasca-api"
+monasca_api_tag: "{{ monasca_tag }}"
+monasca_api_image_full: "{{ monasca_api_image }}:{{ monasca_api_tag }}"
+
+####################
+# OpenStack
+####################
+monasca_openstack_auth: "{{ openstack_auth }}"
+
+monasca_keystone_user: "monasca"
+monasca_default_authorized_roles:
+  - admin
+monasca_read_only_authorized_roles:
+  - monasca-read-only-user
+monasca_agent_authorized_roles:
+  - agent
+monasca_delegate_authorized_roles:
+  - admin
+
+monasca_api_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ monasca_api_port }}/v2.0"
+monasca_api_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ monasca_api_port }}/v2.0"
+monasca_api_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ monasca_api_port }}/v2.0"
+
+monasca_logging_debug: "{{ openstack_logging_debug }}"
diff --git a/ansible/roles/monasca/handlers/main.yml b/ansible/roles/monasca/handlers/main.yml
new file mode 100644
index 000000000..df23d6739
--- /dev/null
+++ b/ansible/roles/monasca/handlers/main.yml
@@ -0,0 +1,21 @@
+---
+- name: Restart monasca-api container
+  vars:
+    service_name: "monasca-api"
+    service: "{{ monasca_services[service_name] }}"
+    config_json: "{{ monasca_config_jsons.results|selectattr('item.key', 'equalto', service_name)|first }}"
+    monasca_api_container: "{{ check_monasca_containers.results|selectattr('item.key', 'equalto', service_name)|first }}"
+  kolla_docker:
+    action: "recreate_or_restart_container"
+    common_options: "{{ docker_common_options }}"
+    name: "{{ service.container_name }}"
+    image: "{{ service.image }}"
+    volumes: "{{ service.volumes }}"
+  when:
+    - kolla_action != "config"
+    - inventory_hostname in groups[service.group]
+    - service.enabled | bool
+    - config_json.changed | bool
+      or monasca_api_confs.changed | bool
+      or monasca_api_wsgi_confs.changed | bool
+      or monasca_api_container.changed | bool
diff --git a/ansible/roles/monasca/meta/main.yml b/ansible/roles/monasca/meta/main.yml
new file mode 100644
index 000000000..6b4fff8fe
--- /dev/null
+++ b/ansible/roles/monasca/meta/main.yml
@@ -0,0 +1,3 @@
+---
+dependencies:
+  - { role: common }
diff --git a/ansible/roles/monasca/tasks/bootstrap.yml b/ansible/roles/monasca/tasks/bootstrap.yml
new file mode 100644
index 000000000..b8733b5e0
--- /dev/null
+++ b/ansible/roles/monasca/tasks/bootstrap.yml
@@ -0,0 +1,51 @@
+---
+- name: Creating monasca database
+  kolla_toolbox:
+    module_name: mysql_db
+    module_args:
+      login_host: "{{ database_address }}"
+      login_port: "{{ database_port }}"
+      login_user: "{{ database_user }}"
+      login_password: "{{ database_password }}"
+      name: "{{ monasca_database_name }}"
+  register: database
+  run_once: True
+  delegate_to: "{{ groups['monasca-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
+
+- name: Creating monasca database user and setting permissions
+  kolla_toolbox:
+    module_name: mysql_user
+    module_args:
+      login_host: "{{ database_address }}"
+      login_port: "{{ database_port }}"
+      login_user: "{{ database_user }}"
+      login_password: "{{ database_password }}"
+      name: "{{ monasca_database_user }}"
+      password: "{{ monasca_database_password }}"
+      host: "%"
+      priv: "{{ monasca_database_name }}.*:ALL"
+      append_privs: "yes"
+  run_once: True
+  delegate_to: "{{ groups['monasca-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
+    - database.changed
+
+- include: bootstrap_service.yml
+  when: database.changed or use_preconfigured_databases | bool
+
+- name: Creating monasca influxdb database
+  kolla_toolbox:
+    module_name: influxdb_database
+    module_args:
+      hostname: "{{ monasca_influxdb_address }}"
+      port: "{{ monasca_influxdb_http_port }}"
+      database_name: "{{ monasca_influxdb_name }}"
+  run_once: True
+  delegate_to: "{{ groups['monasca-api'][0] }}"
+
+# NOTE(dszumski): The Monasca APIs write logs and messages to Kafka. Since
+# Kafka has automatic topic generation enabled by default we don't need to
+# create topics here.
diff --git a/ansible/roles/monasca/tasks/bootstrap_service.yml b/ansible/roles/monasca/tasks/bootstrap_service.yml
new file mode 100644
index 000000000..bed466e55
--- /dev/null
+++ b/ansible/roles/monasca/tasks/bootstrap_service.yml
@@ -0,0 +1,24 @@
+---
+- name: Running monasca bootstrap container
+  vars:
+    monasca_api: "{{ monasca_services['monasca-api'] }}"
+  kolla_docker:
+    action: "start_container"
+    common_options: "{{ docker_common_options }}"
+    detach: False
+    environment:
+      KOLLA_BOOTSTRAP:
+      KOLLA_CONFIG_STRATEGY: "{{ config_strategy }}"
+      MONASCA_DATABASE_ADDRESS: "{{ database_address }}"
+      MONASCA_DATABASE_PORT: "{{ database_port }}"
+      MONASCA_DATABASE_USER: "{{ monasca_database_user }}"
+      MONASCA_DATABASE_PASSWORD: "{{ monasca_database_password }}"
+      MONASCA_DATABASE_NAME: "{{ monasca_database_name }}"
+    image: "{{ monasca_api.image }}"
+    labels:
+      BOOTSTRAP:
+    name: "bootstrap_monasca"
+    restart_policy: "never"
+    volumes: "{{ monasca_api.volumes }}"
+  run_once: True
+  delegate_to: "{{ groups[monasca_api.group][0] }}"
diff --git a/ansible/roles/monasca/tasks/check.yml b/ansible/roles/monasca/tasks/check.yml
new file mode 100644
index 000000000..ed97d539c
--- /dev/null
+++ b/ansible/roles/monasca/tasks/check.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible/roles/monasca/tasks/config.yml b/ansible/roles/monasca/tasks/config.yml
new file mode 100644
index 000000000..70343d98c
--- /dev/null
+++ b/ansible/roles/monasca/tasks/config.yml
@@ -0,0 +1,80 @@
+---
+- name: Ensuring config directories exist
+  file:
+    path: "{{ node_config_directory }}/{{ item.key }}"
+    state: "directory"
+    owner: "{{ config_owner_user }}"
+    group: "{{ config_owner_group }}"
+    mode: "0770"
+    recurse: yes
+  become: true
+  when:
+    - inventory_hostname in groups[item.value.group]
+    - item.value.enabled | bool
+  with_dict: "{{ monasca_services }}"
+
+- name: Copying over config.json files for services
+  template:
+    src: "{{ item.key }}/{{ item.key }}.json.j2"
+    dest: "{{ node_config_directory }}/{{ item.key }}/config.json"
+    mode: "0660"
+  become: true
+  register: monasca_config_jsons
+  when:
+    - inventory_hostname in groups[item.value.group]
+    - item.value.enabled | bool
+  with_dict: "{{ monasca_services }}"
+  notify:
+    - Restart monasca-api container
+
+- name: Copying over monasca-api config
+  vars:
+    service: "{{ monasca_services['monasca-api'] }}"
+  merge_configs:
+    sources:
+      - "{{ role_path }}/templates/monasca-api/{{ item }}.j2"
+      - "{{ node_custom_config }}/monasca/{{ item }}"
+      - "{{ node_custom_config }}/monasca/{{ inventory_hostname }}/{{ item }}"
+    dest: "{{ node_config_directory }}/monasca-api/{{ item }}"
+    mode: "0660"
+  become: true
+  register: monasca_api_confs
+  with_items:
+    - api-config.conf
+    - api-config.ini
+  when:
+    - inventory_hostname in groups[service['group']]
+    - service.enabled | bool
+  notify:
+    - Restart monasca-api container
+
+- name: Copying over monasca-api wsgi config
+  vars:
+    service: "{{ monasca_services['monasca-api'] }}"
+  template:
+    src: "{{ role_path }}/templates/monasca-api/wsgi-api.conf.j2"
+    dest: "{{ node_config_directory }}/monasca-api/wsgi-api.conf"
+    mode: "0660"
+  become: true
+  register: monasca_api_wsgi_confs
+  when:
+    - inventory_hostname in groups[service['group']]
+    - service.enabled | bool
+  notify:
+    - Restart monasca-api container
+
+- name: Check monasca containers
+  kolla_docker:
+    action: "compare_container"
+    common_options: "{{ docker_common_options }}"
+    name: "{{ item.value.container_name }}"
+    image: "{{ item.value.image }}"
+    volumes: "{{ item.value.volumes }}"
+  register: check_monasca_containers
+  when:
+    - kolla_action != "config"
+    - inventory_hostname in groups[item.value.group]
+    - item.value.enabled | bool
+  with_dict: "{{ monasca_services }}"
+  notify:
+    - Restart monasca-api container
diff --git a/ansible/roles/monasca/tasks/deploy.yml b/ansible/roles/monasca/tasks/deploy.yml
new file mode 100644
index 000000000..9eb3e1c6b
--- /dev/null
+++ b/ansible/roles/monasca/tasks/deploy.yml
@@ -0,0 +1,15 @@
+---
+- include: register.yml
+  when: inventory_hostname in groups['monasca-api']
+
+- include: config.yml
+  when: inventory_hostname in groups['monasca-api']
+
+- include: bootstrap.yml
+  when: inventory_hostname in groups['monasca-api']
+
+- name: Flush handlers
+  meta: flush_handlers
+
+- include: check.yml
+  when: inventory_hostname in groups['monasca-api']
diff --git a/ansible/roles/monasca/tasks/main.yml b/ansible/roles/monasca/tasks/main.yml
new file mode 100644
index 000000000..49a33b849
--- /dev/null
+++ b/ansible/roles/monasca/tasks/main.yml
@@ -0,0 +1,2 @@
+---
+- include: "{{ kolla_action }}.yml"
diff --git a/ansible/roles/monasca/tasks/precheck.yml b/ansible/roles/monasca/tasks/precheck.yml
new file mode 100644
index 000000000..0b1849803
--- /dev/null
+++ b/ansible/roles/monasca/tasks/precheck.yml
@@ -0,0 +1,16 @@
+---
+- name: Get container facts
+  kolla_container_facts:
+    name: "{{ monasca_services.values()|map(attribute='container_name')|list }}"
+  register: container_facts
+
+- name: Checking free port for monasca-api
+  wait_for:
+    host: "{{ api_interface_address }}"
+    port: "{{ monasca_api_port }}"
+    connect_timeout: 1
+    timeout: 1
+    state: stopped
+  when:
+    - inventory_hostname in groups[monasca_services['monasca-api']['group']]
+    - container_facts['monasca_api'] is not defined
diff --git a/ansible/roles/monasca/tasks/pull.yml b/ansible/roles/monasca/tasks/pull.yml
new file mode 100644
index 000000000..dae0055cb
--- /dev/null
+++ b/ansible/roles/monasca/tasks/pull.yml
@@ -0,0 +1,10 @@
+---
+- name: Pulling monasca images
+  kolla_docker:
+    action: "pull_image"
+    common_options: "{{ docker_common_options }}"
+    image: "{{ item.value.image }}"
+  when:
+    - inventory_hostname in groups[item.value.group]
+    - item.value.enabled | bool
+  with_dict: "{{ monasca_services }}"
diff --git a/ansible/roles/monasca/tasks/reconfigure.yml b/ansible/roles/monasca/tasks/reconfigure.yml
new file mode 100644
index 000000000..e078ef131
--- /dev/null
+++ b/ansible/roles/monasca/tasks/reconfigure.yml
@@ -0,0 +1,2 @@
+---
+- include: deploy.yml
diff --git a/ansible/roles/monasca/tasks/register.yml b/ansible/roles/monasca/tasks/register.yml
new file mode 100644
index 000000000..d55b54ecd
--- /dev/null
+++ b/ansible/roles/monasca/tasks/register.yml
@@ -0,0 +1,44 @@
+---
+- name: Creating monasca-api service and endpoints
+  kolla_toolbox:
+    module_name: "kolla_keystone_service"
+    module_args:
+      service_name: "monasca-api"
+      service_type: "monitoring"
+      description: "Monasca monitoring as a service"
+      endpoint_region: "{{ openstack_region_name }}"
+      url: "{{ item.url }}"
+      interface: "{{ item.interface }}"
+      region_name: "{{ openstack_region_name }}"
+      auth: "{{ monasca_openstack_auth }}"
+  run_once: True
+  with_items:
+    - {'interface': 'admin', 'url': '{{ monasca_api_admin_endpoint }}'}
+    - {'interface': 'internal', 'url': '{{ monasca_api_internal_endpoint }}'}
+    - {'interface': 'public', 'url': '{{ monasca_api_public_endpoint }}'}
+
+- name: Creating the monasca keystone user
+  kolla_toolbox:
+    module_name: "kolla_keystone_user"
+    module_args:
+      project: "service"
+      user: "{{ monasca_keystone_user }}"
+      password: "{{ monasca_keystone_password }}"
+      role: "admin"
+      region_name: "{{ openstack_region_name }}"
+      auth: "{{ '{{ monasca_openstack_auth }}' }}"
+  run_once: True
+
+- name: Creating monasca roles
+  kolla_toolbox:
+    module_name: os_keystone_role
+    module_args:
+      name: "{{ item }}"
+      region_name: "{{ openstack_region_name }}"
+      auth: "{{ monasca_openstack_auth }}"
+  run_once: True
+  with_items:
+    - "{{ monasca_default_authorized_roles }}"
+    - "{{ monasca_agent_authorized_roles }}"
+    - "{{ monasca_read_only_authorized_roles }}"
+    - "{{ monasca_delegate_authorized_roles }}"
diff --git a/ansible/roles/monasca/tasks/upgrade.yml b/ansible/roles/monasca/tasks/upgrade.yml
new file mode 100644
index 000000000..dd26ecc34
--- /dev/null
+++ b/ansible/roles/monasca/tasks/upgrade.yml
@@ -0,0 +1,5 @@
+---
+- include: config.yml
+
+- name: Flush handlers
+  meta: flush_handlers
diff --git a/ansible/roles/monasca/templates/monasca-api/api-config.conf.j2 b/ansible/roles/monasca/templates/monasca-api/api-config.conf.j2
new file mode 100644
index 000000000..d05e38f96
--- /dev/null
+++ b/ansible/roles/monasca/templates/monasca-api/api-config.conf.j2
@@ -0,0 +1,59 @@
+[DEFAULT]
+log_file = monasca-api.log
+log_dir = /var/log/kolla/monasca
+debug = {{ monasca_logging_debug }}
+region = {{ openstack_region_name }}
+
+[database]
+database = {{ monasca_database_name }}
+connection = mysql+pymysql://{{ monasca_database_user }}:{{ monasca_database_password }}@{{ monasca_database_address }}/{{ monasca_database_name }}
+
+[influxdb]
+database_name = {{ monasca_influxdb_name }}
+ip_address = {{ monasca_influxdb_address }}
+port = {{ monasca_influxdb_http_port }}
+
+[kafka]
+partitions = 0
+uri = {{ monasca_kafka }}
+
+[messaging]
+driver = monasca_api.common.messaging.kafka_publisher:KafkaPublisher
+
+[security]
+default_authorized_roles = {{ monasca_default_authorized_roles|join(', ') }}
+agent_authorized_roles = {{ monasca_agent_authorized_roles|join(', ') }}
+read_only_authorized_roles = {{ monasca_read_only_authorized_roles|join(', ') }}
+delegate_authorized_roles = {{ monasca_delegate_authorized_roles|join(', ') }}
+
+[keystone_authtoken]
+auth_uri = {{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ keystone_public_port }}
+auth_url = {{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ keystone_admin_port }}
+auth_type = password
+project_domain_id = {{ default_project_domain_id }}
+user_domain_id = {{ default_user_domain_id }}
+project_name = service
+username = {{ monasca_keystone_user }}
+password = {{ monasca_keystone_password }}
+service_token_roles_required=True
+
+memcache_security_strategy = ENCRYPT
+memcache_secret_key = {{ memcache_secret_key }}
+memcached_servers = {{ monasca_memcached_servers }}
+
+[dispatcher]
+versions = monasca_api.v2.reference.versions:Versions
+version_2_0 = monasca_api.v2.reference.version_2_0:Version2
+metrics = monasca_api.v2.reference.metrics:Metrics
+metrics_measurements = monasca_api.v2.reference.metrics:MetricsMeasurements
+metrics_statistics = monasca_api.v2.reference.metrics:MetricsStatistics
+metrics_names = monasca_api.v2.reference.metrics:MetricsNames
+alarm_definitions = monasca_api.v2.reference.alarm_definitions:AlarmDefinitions
+alarms = monasca_api.v2.reference.alarms:Alarms
+alarms_count = monasca_api.v2.reference.alarms:AlarmsCount
+alarms_state_history = monasca_api.v2.reference.alarms:AlarmsStateHistory
+notification_methods = monasca_api.v2.reference.notifications:Notifications
+dimension_values = monasca_api.v2.reference.metrics:DimensionValues
+dimension_names = monasca_api.v2.reference.metrics:DimensionNames
+notification_method_types = monasca_api.v2.reference.notificationstype:NotificationsType
+healthchecks = monasca_api.healthchecks:HealthChecks
diff --git a/ansible/roles/monasca/templates/monasca-api/api-config.ini.j2 b/ansible/roles/monasca/templates/monasca-api/api-config.ini.j2
new file mode 100644
index 000000000..6ac702a4c
--- /dev/null
+++ b/ansible/roles/monasca/templates/monasca-api/api-config.ini.j2
@@ -0,0 +1,14 @@
+[DEFAULT]
+name = monasca_api
+
+[pipeline:main]
+pipeline = request_id auth api
+
+[app:api]
+paste.app_factory = monasca_api.api.server:launch
+
+[filter:auth]
+paste.filter_factory = monasca_api.healthcheck.keystone_protocol:filter_factory
+
+[filter:request_id]
+paste.filter_factory = oslo_middleware.request_id:RequestId.factory
diff --git a/ansible/roles/monasca/templates/monasca-api/monasca-api.json.j2 b/ansible/roles/monasca/templates/monasca-api/monasca-api.json.j2
new file mode 100644
index 000000000..66dcce588
--- /dev/null
+++ b/ansible/roles/monasca/templates/monasca-api/monasca-api.json.j2
@@ -0,0 +1,32 @@
+{% set monasca_cmd = 'apache2' if kolla_base_distro in ['ubuntu', 'debian'] else 'httpd' %}
+{% set wsgi_conf_dir = 'apache2/conf-enabled' if kolla_base_distro in ['ubuntu', 'debian'] else 'httpd/conf.d' %}
+{
+    "command": "/usr/sbin/{{ monasca_cmd }} -DFOREGROUND",
+    "config_files": [
+        {
+            "source": "{{ container_config_directory }}/api-config.conf",
+            "dest": "/etc/monasca/api-config.conf",
+            "owner": "monasca",
+            "perm": "0600"
+        },
+        {
+            "source": "{{ container_config_directory }}/api-config.ini",
+            "dest": "/etc/monasca/api-config.ini",
+            "owner": "monasca",
+            "perm": "0600"
+        },
+        {
+            "source": "{{ container_config_directory }}/wsgi-api.conf",
+            "dest": "/etc/{{ wsgi_conf_dir }}/wsgi-config.conf",
+            "owner": "monasca",
+            "perm": "0600"
+        }
+    ],
+    "permissions": [
+        {
+            "path": "/var/log/kolla/monasca",
+            "owner": "monasca:kolla",
+            "recurse": true
+        }
+    ]
+}
diff --git a/ansible/roles/monasca/templates/monasca-api/wsgi-api.conf.j2 b/ansible/roles/monasca/templates/monasca-api/wsgi-api.conf.j2
new file mode 100644
index 000000000..99b20a54d
--- /dev/null
+++ b/ansible/roles/monasca/templates/monasca-api/wsgi-api.conf.j2
@@ -0,0 +1,24 @@
+{% set python_path = '/usr/lib/python2.7/site-packages' if kolla_install_type == 'binary' else '/var/lib/kolla/venv/lib/python2.7/site-packages' %}
+{% set wsgi_path = '/usr/bin' if monasca_install_type == 'binary' else '/monasca-api/monasca_api/api' %}
+
+Listen {{ api_interface_address }}:{{ monasca_api_port }}
+
+TraceEnable off
+
+<VirtualHost *:{{ monasca_api_port }}>
+
+    ErrorLog "/var/log/kolla/monasca/monasca-api-error.log"
+    LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b %D \"%{Referer}i\" \"%{User-Agent}i\"" logformat
+    CustomLog "/var/log/kolla/monasca/monasca-api-access.log" logformat
+    WSGIApplicationGroup %{GLOBAL}
+    WSGIDaemonProcess monasca-api group=monasca processes={{ openstack_service_workers }} threads=1 user=monasca python-path={{ python_path }}
+    WSGIProcessGroup  monasca-api
+    WSGIScriptAlias / {{ wsgi_path }}/wsgi.py
+    WSGIPassAuthorization On
+    SetEnv no-gzip 1
+
+    <Directory "{{ wsgi_path }}">
+        Require all granted
+    </Directory>
+
+</VirtualHost>
diff --git a/ansible/site.yml b/ansible/site.yml
index 2ec6cec9d..842cd9b0b 100644
--- a/ansible/site.yml
+++ b/ansible/site.yml
@@ -546,6 +546,15 @@
         tags: ceilometer,
         when: enable_ceilometer | bool }
 
+- name: Apply role monasca
+  gather_facts: false
+  hosts: monasca
+  serial: '{{ kolla_serial|default("0") }}'
+  roles:
+    - { role: monasca,
+        tags: monasca,
+        when: enable_monasca | bool }
+
 - name: Apply role aodh
   gather_facts: false
   hosts:
diff --git a/etc/kolla/globals.yml b/etc/kolla/globals.yml
index d0a78c7a5..aec0a4fd5 100644
--- a/etc/kolla/globals.yml
+++ b/etc/kolla/globals.yml
@@ -200,6 +200,7 @@ kolla_internal_vip_address: "10.10.10.254"
 #enable_manila_backend_cephfs_native: "no"
 #enable_manila_backend_cephfs_nfs: "no"
 #enable_mistral: "no"
+#enable_monasca: "no"
 #enable_mongodb: "no"
 #enable_murano: "no"
 #enable_multipathd: "no"
diff --git a/etc/kolla/passwords.yml b/etc/kolla/passwords.yml
index 0094a156c..34867c9af 100644
--- a/etc/kolla/passwords.yml
+++ b/etc/kolla/passwords.yml
@@ -122,6 +122,9 @@ murano_database_password:
 murano_keystone_password:
 murano_agent_rabbitmq_password:
 
+monasca_database_password:
+monasca_keystone_password:
+
 ironic_database_password:
 ironic_keystone_password:
 
diff --git a/releasenotes/notes/add-monasca-api-eb536dd5a6d77563.yaml b/releasenotes/notes/add-monasca-api-eb536dd5a6d77563.yaml
new file mode 100644
index 000000000..1c0041749
--- /dev/null
+++ b/releasenotes/notes/add-monasca-api-eb536dd5a6d77563.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - Add a role for deploying the Monasca API which forms part of the
+    Monasca distributed monitoring and logging as a service platform.
+    See https://wiki.openstack.org/wiki/Monasca for more details.
-- 
GitLab