diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml
index 83d504a97420b9f562e9b10d27cf2126ced517aa..1b9dff1b8650ade8872f2de8c009aae3a2467637 100644
--- a/ansible/group_vars/all.yml
+++ b/ansible/group_vars/all.yml
@@ -193,6 +193,8 @@ watcher_api_port: "9322"
 influxdb_admin_port: "8083"
 influxdb_http_port: "8086"
 
+senlin_api_port: "8778"
+
 public_protocol: "{{ 'https' if kolla_enable_tls_external | bool else 'http' }}"
 internal_protocol: "http"
 admin_protocol: "http"
@@ -259,6 +261,7 @@ enable_neutron_qos: "no"
 enable_neutron_agent_ha: "no"
 enable_rally: "no"
 enable_sahara: "no"
+enable_senlin: "no"
 enable_swift: "no"
 enable_tempest: "no"
 enable_watcher: "no"
diff --git a/ansible/inventory/all-in-one b/ansible/inventory/all-in-one
index d03fae6b7b43205b6cc6ffbdcb2e0ff77380b651..a1dcf2f8bf87c997e3d6b2c1b8c4b752bca43c18 100644
--- a/ansible/inventory/all-in-one
+++ b/ansible/inventory/all-in-one
@@ -117,6 +117,9 @@ control
 [tempest:children]
 control
 
+[senlin:children]
+control
+
 [watcher:children]
 control
 
@@ -352,3 +355,10 @@ watcher
 
 [watcher-applier:children]
 watcher
+
+# Senlin
+[senlin-api:children]
+senlin
+
+[senlin-engine:children]
+senlin
diff --git a/ansible/inventory/multinode b/ansible/inventory/multinode
index b70502484962a83cfd3e25e3994faaa44ce92997..be0dc85dcae8003cc5b12b603fccd269bebcbe7f 100644
--- a/ansible/inventory/multinode
+++ b/ansible/inventory/multinode
@@ -135,6 +135,9 @@ control
 [tempest:children]
 control
 
+[senlin:children]
+control
+
 [watcher:children]
 control
 
@@ -370,3 +373,10 @@ watcher
 
 [watcher-applier:children]
 watcher
+
+# Senlin
+[senlin-api:children]
+senlin
+
+[senlin-engine:children]
+senlin
diff --git a/ansible/roles/common/tasks/config.yml b/ansible/roles/common/tasks/config.yml
index 359f44982800e962acb5aa8e01aba8b999629606..f28aced813be01abb99ff2aa12a84967b09ae53b 100644
--- a/ansible/roles/common/tasks/config.yml
+++ b/ansible/roles/common/tasks/config.yml
@@ -88,5 +88,6 @@
     - "neutron"
     - "nova"
     - "rabbitmq"
+    - "senlin"
     - "swift"
     - "watcher"
diff --git a/ansible/roles/common/templates/cron-logrotate-senlin.conf.j2 b/ansible/roles/common/templates/cron-logrotate-senlin.conf.j2
new file mode 100644
index 0000000000000000000000000000000000000000..b4a61be8c07d4ebb2522604a3aee2a8f6e550083
--- /dev/null
+++ b/ansible/roles/common/templates/cron-logrotate-senlin.conf.j2
@@ -0,0 +1,3 @@
+"/var/log/kolla/senlin/*.log"
+{
+}
diff --git a/ansible/roles/common/templates/cron.json.j2 b/ansible/roles/common/templates/cron.json.j2
index 4e4c5dd6c17498e685b7c17082df8c6df57baea6..26b8a47ebc3bddb729b2766fb0b12361840da9a8 100644
--- a/ansible/roles/common/templates/cron.json.j2
+++ b/ansible/roles/common/templates/cron.json.j2
@@ -19,6 +19,7 @@
     "neutron",
     "nova",
     "rabbitmq",
+    "senlin",
     "swift"
 ] %}
 {
diff --git a/ansible/roles/common/templates/heka-openstack.toml.j2 b/ansible/roles/common/templates/heka-openstack.toml.j2
index 65cfcf1124e208bafb24604b34514941fea8ca38..40f11dfc4472d62d115e38d98ad0dfe1a6946975 100644
--- a/ansible/roles/common/templates/heka-openstack.toml.j2
+++ b/ansible/roles/common/templates/heka-openstack.toml.j2
@@ -6,6 +6,6 @@ filename = "lua_decoders/os_openstack_log.lua"
 type = "LogstreamerInput"
 decoder = "openstack_log_decoder"
 log_directory = "/var/log/kolla"
-file_match = '(?P<Service>nova|glance|keystone|neutron|ceph|cinder|heat|murano|magnum|mistral|manila)/(?P<Program>.*)\.log\.?(?P<Seq>\d*)$'
+file_match = '(?P<Service>nova|glance|keystone|neutron|ceph|cinder|heat|murano|magnum|mistral|manila|senlin)/(?P<Program>.*)\.log\.?(?P<Seq>\d*)$'
 priority = ["^Seq"]
 differentiator = ["Service", "_", "Program"]
diff --git a/ansible/roles/haproxy/templates/haproxy.cfg.j2 b/ansible/roles/haproxy/templates/haproxy.cfg.j2
index 0b6c2675efd30e558b588a8ab5e293c85ed401f8..4d2bcb77168068d90ca3466aa57d592fb9164d70 100644
--- a/ansible/roles/haproxy/templates/haproxy.cfg.j2
+++ b/ansible/roles/haproxy/templates/haproxy.cfg.j2
@@ -305,6 +305,22 @@ listen ironic_api_external
 {% endif %}
 {% endif %}
 
+{% if enable_senlin | bool %}
+listen senlin_api
+  bind {{ kolla_internal_vip_address }}:{{ senlin_api_port }}
+{% for host in groups['senlin-api'] %}
+  server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ senlin_api_port }} check inter 2000 rise 2 fall 5
+{% endfor %}
+{% if haproxy_enable_external_vip | bool %}
+
+listen senlin_api_external
+  bind {{ kolla_external_vip_address }}:{{ senlin_api_port }} {{ tls_bind_info }}
+{% for host in groups['senlin-api'] %}
+  server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ senlin_api_port }} check inter 2000 rise 2 fall 5
+{% endfor %}
+{% endif %}
+{% endif %}
+
 {% if enable_swift | bool %}
 listen swift_api
   bind {{ kolla_internal_vip_address }}:{{ swift_proxy_server_port }}
diff --git a/ansible/roles/prechecks/tasks/port_checks.yml b/ansible/roles/prechecks/tasks/port_checks.yml
index f0a0dc265c40827cd61fd77c88a3e789207f0173..c424c85ea63155c73e5fb9aa966a2a881a77675b 100644
--- a/ansible/roles/prechecks/tasks/port_checks.yml
+++ b/ansible/roles/prechecks/tasks/port_checks.yml
@@ -489,6 +489,22 @@
     state: stopped
   when: inventory_hostname in groups['haproxy']
 
+- name: Checking free port for Senlin API
+  wait_for:
+    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    port: "{{ senlin_api_port }}"
+    connect_timeout: 1
+    state: stopped
+  when: inventory_hostname in groups['senlin-api']
+
+- name: Checking free port for Senlin API HAProxy
+  wait_for:
+    host: "{{ kolla_internal_vip_address }}"
+    port: "{{ senlin_api_port }}"
+    connect_timeout: 1
+    state: stopped
+  when: inventory_hostname in groups['haproxy']
+
 - name: Checking if kolla_internal_vip_address and kolla_external_vip_address are not pingable from any node
   command: ping -c 3 {{ item }}
   register: ping_output
diff --git a/ansible/roles/senlin/defaults/main.yml b/ansible/roles/senlin/defaults/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..811ad25c784c1ee1b97655ea462c8d8ce29a8348
--- /dev/null
+++ b/ansible/roles/senlin/defaults/main.yml
@@ -0,0 +1,35 @@
+---
+project_name: "senlin"
+
+####################
+# Database
+####################
+senlin_database_name: "senlin"
+senlin_database_user: "senlin"
+senlin_database_address: "{{ kolla_internal_fqdn }}:{{ database_port }}"
+
+
+####################
+# Docker
+####################
+senlin_engine_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ kolla_base_distro }}-{{ kolla_install_type }}-senlin-engine"
+senlin_engine_tag: "{{ openstack_release }}"
+senlin_engine_image_full: "{{ senlin_engine_image }}:{{ senlin_engine_tag }}"
+
+senlin_api_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ kolla_base_distro }}-{{ kolla_install_type }}-senlin-api"
+senlin_api_tag: "{{ openstack_release }}"
+senlin_api_image_full: "{{ senlin_api_image }}:{{ senlin_api_tag }}"
+
+
+####################
+# OpenStack
+####################
+senlin_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ senlin_api_port }}"
+senlin_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ senlin_api_port }}"
+senlin_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ senlin_api_port }}"
+
+senlin_logging_debug: "{{ openstack_logging_debug }}"
+
+senlin_keystone_user: "senlin"
+
+openstack_senlin_auth: "{'auth_url':'{{ openstack_auth.auth_url }}','username':'{{ openstack_auth.username }}','password':'{{ openstack_auth.password }}','project_name':'{{ openstack_auth.project_name }}'}"
diff --git a/ansible/roles/senlin/meta/main.yml b/ansible/roles/senlin/meta/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6b4fff8fef6f81d35b73a30c90de45639db41cc9
--- /dev/null
+++ b/ansible/roles/senlin/meta/main.yml
@@ -0,0 +1,3 @@
+---
+dependencies:
+  - { role: common }
diff --git a/ansible/roles/senlin/tasks/bootstrap.yml b/ansible/roles/senlin/tasks/bootstrap.yml
new file mode 100644
index 0000000000000000000000000000000000000000..95a599b1797e59273aac53058ac2d7d4c7b907f8
--- /dev/null
+++ b/ansible/roles/senlin/tasks/bootstrap.yml
@@ -0,0 +1,41 @@
+---
+- name: Creating Senlin 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='{{ senlin_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['senlin-api'][0] }}"
+
+- name: Reading json from variable
+  set_fact:
+    database_created: "{{ (database.stdout.split('localhost | SUCCESS => ')[1]|from_json).changed }}"
+
+- name: Creating Senlin 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='{{ senlin_database_name }}'
+        password='{{ senlin_database_password }}'
+        host='%'
+        priv='{{ senlin_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['senlin-api'][0] }}"
+
+- include: bootstrap_service.yml
+  when: database_created
diff --git a/ansible/roles/senlin/tasks/bootstrap_service.yml b/ansible/roles/senlin/tasks/bootstrap_service.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ff3504d9573df6da292e429951568e37d214906f
--- /dev/null
+++ b/ansible/roles/senlin/tasks/bootstrap_service.yml
@@ -0,0 +1,20 @@
+---
+- name: Running Senlin bootstrap container
+  kolla_docker:
+    action: "start_container"
+    common_options: "{{ docker_common_options }}"
+    detach: False
+    environment:
+      KOLLA_BOOTSTRAP:
+      KOLLA_CONFIG_STRATEGY: "{{ config_strategy }}"
+    image: "{{ senlin_api_image_full }}"
+    labels:
+      BOOTSTRAP:
+    name: "bootstrap_senlin"
+    restart_policy: "never"
+    volumes:
+      - "{{ node_config_directory }}/senlin-api/:{{ container_config_directory }}/:ro"
+      - "/etc/localtime:/etc/localtime:ro"
+      - "kolla_logs:/var/log/kolla/"
+  run_once: True
+  delegate_to: "{{ groups['senlin-api'][0] }}"
diff --git a/ansible/roles/senlin/tasks/config.yml b/ansible/roles/senlin/tasks/config.yml
new file mode 100644
index 0000000000000000000000000000000000000000..fa714f550808db17dcc457b61bc5cdaa4bbbe642
--- /dev/null
+++ b/ansible/roles/senlin/tasks/config.yml
@@ -0,0 +1,34 @@
+---
+- name: Ensuring config directories exist
+  file:
+    path: "{{ node_config_directory }}/{{ item }}"
+    state: "directory"
+    recurse: yes
+  with_items:
+    - "senlin-api"
+    - "senlin-engine"
+
+- name: Copying over config.json files for services
+  template:
+    src: "{{ item }}.json.j2"
+    dest: "{{ node_config_directory }}/{{ item }}/config.json"
+  with_items:
+    - "senlin-api"
+    - "senlin-engine"
+
+- name: Copying over senlin.conf
+  merge_configs:
+    vars:
+      service_name: "{{ item }}"
+    sources:
+      - "{{ role_path }}/templates/senlin.conf.j2"
+      - "{{ node_custom_config }}/global.conf"
+      - "{{ node_custom_config }}/database.conf"
+      - "{{ node_custom_config }}/messaging.conf"
+      - "{{ node_custom_config }}/senlin.conf"
+      - "{{ node_custom_config }}/senlin/{{ item }}.conf"
+      - "{{ node_custom_config }}/senlin/{{ inventory_hostname }}/senlin.conf"
+    dest: "{{ node_config_directory }}/{{ item }}/senlin.conf"
+  with_items:
+    - "senlin-api"
+    - "senlin-engine"
diff --git a/ansible/roles/senlin/tasks/deploy.yml b/ansible/roles/senlin/tasks/deploy.yml
new file mode 100644
index 0000000000000000000000000000000000000000..46aedd6e1f5ab2f687e789cb1dabd9058f89cd09
--- /dev/null
+++ b/ansible/roles/senlin/tasks/deploy.yml
@@ -0,0 +1,14 @@
+---
+- include: register.yml
+  when: inventory_hostname in groups['senlin-api']
+
+- include: config.yml
+  when: inventory_hostname in groups['senlin-api'] or
+        inventory_hostname in groups['senlin-engine']
+
+- include: bootstrap.yml
+  when: inventory_hostname in groups['senlin-api']
+
+- include: start.yml
+  when: inventory_hostname in groups['senlin-api'] or
+        inventory_hostname in groups['senlin-engine']
diff --git a/ansible/roles/senlin/tasks/do_reconfigure.yml b/ansible/roles/senlin/tasks/do_reconfigure.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ffd25dc39255f5c8e63c97b3a36645810cdda07c
--- /dev/null
+++ b/ansible/roles/senlin/tasks/do_reconfigure.yml
@@ -0,0 +1,66 @@
+---
+- 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: senlin_api, group: senlin-api }
+    - { name: senlin_engine, group: senlin-engine }
+
+- 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: senlin_api, group: senlin-api }
+    - { name: senlin_engine, group: senlin-engine }
+
+- 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: senlin_api, group: senlin-api }
+    - { name: senlin_engine, group: senlin-engine }
+
+- name: Remove the containers
+  kolla_docker:
+    name: "{{ item[0]['name'] }}"
+    action: "remove_container"
+  register: remove_containers
+  when:
+    - config_strategy == "COPY_ONCE" or item[1]['KOLLA_CONFIG_STRATEGY'] == 'COPY_ONCE'
+    - item[2]['rc'] == 1
+    - inventory_hostname in groups[item[0]['group']]
+  with_together:
+    - [{ name: senlin_api, group: senlin-api },
+       { name: senlin_engine, group: senlin-engine }]
+    - "{{ 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:
+    - config_strategy == 'COPY_ALWAYS'
+    - item[1]['KOLLA_CONFIG_STRATEGY'] != 'COPY_ONCE'
+    - item[2]['rc'] == 1
+    - inventory_hostname in groups[item[0]['group']]
+  with_together:
+    - [{ name: senlin_api, group: senlin-api },
+       { name: senlin_engine, group: senlin-engine }]
+    - "{{ container_envs.results }}"
+    - "{{ check_results.results }}"
diff --git a/ansible/roles/senlin/tasks/main.yml b/ansible/roles/senlin/tasks/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b017e8b4ad9edbc10e43b690b269d460e5b9b1f4
--- /dev/null
+++ b/ansible/roles/senlin/tasks/main.yml
@@ -0,0 +1,2 @@
+---
+- include: "{{ action }}.yml"
diff --git a/ansible/roles/senlin/tasks/pull.yml b/ansible/roles/senlin/tasks/pull.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3a21aee0521f0f6611d0082d7843000d599ddee9
--- /dev/null
+++ b/ansible/roles/senlin/tasks/pull.yml
@@ -0,0 +1,14 @@
+---
+- name: Pulling senlin-api image
+  kolla_docker:
+    action: "pull_image"
+    common_options: "{{ docker_common_options }}"
+    image: "{{ senlin_api_image_full }}"
+  when: inventory_hostname in groups['senlin-api']
+
+- name: Pulling senlin-engine image
+  kolla_docker:
+    action: "pull_image"
+    common_options: "{{ docker_common_options }}"
+    image: "{{ senlin_engine_image_full }}"
+  when: inventory_hostname in groups['senlin-engine']
diff --git a/ansible/roles/senlin/tasks/reconfigure.yml b/ansible/roles/senlin/tasks/reconfigure.yml
new file mode 100644
index 0000000000000000000000000000000000000000..60d4d0ce2089695fb47a25d560c4e03331c343a3
--- /dev/null
+++ b/ansible/roles/senlin/tasks/reconfigure.yml
@@ -0,0 +1,4 @@
+---
+- include: do_reconfigure.yml
+  when: inventory_hostname in groups['senlin-api']
+        or inventory_hostname in groups['senlin-engine']
diff --git a/ansible/roles/senlin/tasks/register.yml b/ansible/roles/senlin/tasks/register.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c9d0185f4fcf6bc7030ec8c3b2fec1589797d72a
--- /dev/null
+++ b/ansible/roles/senlin/tasks/register.yml
@@ -0,0 +1,40 @@
+---
+- name: Creating the Senlin service and endpoint
+  command: docker exec -t kolla_toolbox /usr/bin/ansible localhost
+    -m kolla_keystone_service
+    -a "service_name=senlin
+        service_type=clustering
+        description='Senlin Clustering Service'
+        endpoint_region={{ openstack_region_name }}
+        url='{{ item.url }}'
+        interface='{{ item.interface }}'
+        region_name={{ openstack_region_name }}
+        auth={{ '{{ openstack_senlin_auth }}' }}"
+    -e "{'openstack_senlin_auth':{{ openstack_senlin_auth }}}"
+  register: senlin_endpoint
+  changed_when: "{{ senlin_endpoint.stdout.find('localhost | SUCCESS => ') != -1 and (senlin_endpoint.stdout.split('localhost | SUCCESS => ')[1]|from_json).changed }}"
+  until: senlin_endpoint.stdout.split()[2] == 'SUCCESS'
+  retries: 10
+  delay: 5
+  run_once: True
+  with_items:
+    - {'interface': 'admin', 'url': '{{ senlin_admin_endpoint }}'}
+    - {'interface': 'internal', 'url': '{{ senlin_internal_endpoint }}'}
+    - {'interface': 'public', 'url': '{{ senlin_public_endpoint }}'}
+
+- name: Creating the Senlin project, user, and role
+  command: docker exec -t kolla_toolbox /usr/bin/ansible localhost
+    -m kolla_keystone_user
+    -a "project=service
+        user=senlin
+        password={{ senlin_keystone_password }}
+        role=admin
+        region_name={{ openstack_region_name }}
+        auth={{ '{{ openstack_senlin_auth }}' }}"
+    -e "{'openstack_senlin_auth':{{ openstack_senlin_auth }}}"
+  register: senlin_user
+  changed_when: "{{ senlin_user.stdout.find('localhost | SUCCESS => ') != -1 and (senlin_user.stdout.split('localhost | SUCCESS => ')[1]|from_json).changed }}"
+  until: senlin_user.stdout.split()[2] == 'SUCCESS'
+  retries: 10
+  delay: 5
+  run_once: True
diff --git a/ansible/roles/senlin/tasks/start.yml b/ansible/roles/senlin/tasks/start.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f539bb3cd5916ec36c25a060074ff8529e964ffc
--- /dev/null
+++ b/ansible/roles/senlin/tasks/start.yml
@@ -0,0 +1,24 @@
+---
+- name: Starting senlin-engine container
+  kolla_docker:
+    action: "start_container"
+    common_options: "{{ docker_common_options }}"
+    image: "{{ senlin_engine_image_full }}"
+    name: "senlin_engine"
+    volumes:
+      - "{{ node_config_directory }}/senlin-engine/:{{ container_config_directory }}/:ro"
+      - "/etc/localtime:/etc/localtime:ro"
+      - "kolla_logs:/var/log/kolla/"
+  when: inventory_hostname in groups['senlin-engine']
+
+- name: Starting senlin-api container
+  kolla_docker:
+    action: "start_container"
+    common_options: "{{ docker_common_options }}"
+    image: "{{ senlin_api_image_full }}"
+    name: "senlin_api"
+    volumes:
+      - "{{ node_config_directory }}/senlin-api/:{{ container_config_directory }}/:ro"
+      - "/etc/localtime:/etc/localtime:ro"
+      - "kolla_logs:/var/log/kolla/"
+  when: inventory_hostname in groups['senlin-api']
diff --git a/ansible/roles/senlin/tasks/upgrade.yml b/ansible/roles/senlin/tasks/upgrade.yml
new file mode 100644
index 0000000000000000000000000000000000000000..308053080c0ccc6af9fe340dbb86eceef5f49074
--- /dev/null
+++ b/ansible/roles/senlin/tasks/upgrade.yml
@@ -0,0 +1,6 @@
+---
+- include: config.yml
+
+- include: bootstrap_service.yml
+
+- include: start.yml
diff --git a/ansible/roles/senlin/templates/senlin-api.json.j2 b/ansible/roles/senlin/templates/senlin-api.json.j2
new file mode 100644
index 0000000000000000000000000000000000000000..7f77a9b9172e5f4236b7fcab5ada34cfd33811a0
--- /dev/null
+++ b/ansible/roles/senlin/templates/senlin-api.json.j2
@@ -0,0 +1,11 @@
+{
+    "command": "senlin-api --config-file /etc/senlin/senlin.conf",
+    "config_files": [
+        {
+            "source": "{{ container_config_directory }}/senlin.conf",
+            "dest": "/etc/senlin/senlin.conf",
+            "owner": "senlin",
+            "perm": "0600"
+        }
+    ]
+}
diff --git a/ansible/roles/senlin/templates/senlin-engine.json.j2 b/ansible/roles/senlin/templates/senlin-engine.json.j2
new file mode 100644
index 0000000000000000000000000000000000000000..de63916f681131b549d6c3ac5608f5fd51f8f47d
--- /dev/null
+++ b/ansible/roles/senlin/templates/senlin-engine.json.j2
@@ -0,0 +1,11 @@
+{
+    "command": "senlin-engine --config-file /etc/senlin/senlin.conf",
+    "config_files": [
+        {
+            "source": "{{ container_config_directory }}/senlin.conf",
+            "dest": "/etc/senlin/senlin.conf",
+            "owner": "senlin",
+            "perm": "0600"
+        }
+    ]
+}
diff --git a/ansible/roles/senlin/templates/senlin.conf.j2 b/ansible/roles/senlin/templates/senlin.conf.j2
new file mode 100644
index 0000000000000000000000000000000000000000..361efda7ccd76a4d44de0357254e47134bb57920
--- /dev/null
+++ b/ansible/roles/senlin/templates/senlin.conf.j2
@@ -0,0 +1,35 @@
+[DEFAULT]
+debug = {{ senlin_logging_debug }}
+
+log_dir = /var/log/kolla/senlin
+
+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 %}
+
+[authentication]
+auth_url = {{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ keystone_admin_port }}
+service_username = {{ senlin_keystone_user }}
+service_password = {{ senlin_keystone_password }}
+service_project_name = service
+service_user_domain = default
+service_project_domain = default
+
+[database]
+connection = mysql+pymysql://{{ senlin_database_user }}:{{ senlin_database_password }}@{{ senlin_database_address }}/{{ senlin_database_name }}
+max_retries = -1
+
+[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
+user_domain_id = default
+project_name = service
+username = {{ senlin_keystone_user }}
+password = {{ senlin_keystone_password }}
+
+memcache_security_strategy = ENCRYPT
+memcache_secret_key = {{ memcache_secret_key }}
+memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+
+[oslo_messaging_notifications]
+driver = noop
diff --git a/ansible/site.yml b/ansible/site.yml
index 6f87631de699e791e3fcb08419dc55b8c619f941..67f1e2a32e0e3ebd94c1a14fd4047dc6b47ad981 100644
--- a/ansible/site.yml
+++ b/ansible/site.yml
@@ -345,3 +345,12 @@
     - { role: cloudkitty,
         tags: cloudkitty,
         when: enable_cloudkitty | bool }
+
+- hosts:
+    - senlin-api
+    - senlin-engine
+  serial: '{{ "30%" if action == "upgrade" else "0" }}'
+  roles:
+    - { role: senlin,
+        tags: senlin,
+        when: enable_senlin | bool }
diff --git a/etc/kolla/globals.yml b/etc/kolla/globals.yml
index 5415874954dc52a748406722072b24b2222f7ac6..26ddbddf7009e2204278aa96dcca2afcdae87e20 100644
--- a/etc/kolla/globals.yml
+++ b/etc/kolla/globals.yml
@@ -135,6 +135,7 @@ kolla_internal_vip_address: "10.10.10.254"
 #enable_neutron_qos: "no"
 #enable_neutron_agent_ha: "no"
 #enable_rally: "no"
+#enable_senlin: "no"
 #enable_swift: "no"
 #enable_tempest: "no"
 #enable_watcher: "no"
diff --git a/etc/kolla/passwords.yml b/etc/kolla/passwords.yml
index e570a9d906f354a695ef7c1c6333568b2f038f37..c31a9805f450536cc1d65a4fadc89ad60f0b4b0f 100644
--- a/etc/kolla/passwords.yml
+++ b/etc/kolla/passwords.yml
@@ -84,6 +84,9 @@ congress_keystone_password:
 
 rally_database_password:
 
+senlin_database_password:
+senlin_keystone_password:
+
 horizon_secret_key:
 
 telemetry_secret_key:
diff --git a/releasenotes/notes/senlin-container-e1ae6aa932097e51.yaml b/releasenotes/notes/senlin-container-e1ae6aa932097e51.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..e2a1175baea8f61fb7878f4636529aaaf7053727
--- /dev/null
+++ b/releasenotes/notes/senlin-container-e1ae6aa932097e51.yaml
@@ -0,0 +1,3 @@
+---
+features:
+  - Implement Senlin Container