diff --git a/README.rst b/README.rst
index f9a42f78cd..42ca35f9d7 100644
--- a/README.rst
+++ b/README.rst
@@ -69,6 +69,7 @@ Kolla-Ansible deploys containers for the following OpenStack projects:
 - `Nova <https://docs.openstack.org/nova/latest/>`__
 - `Octavia <https://docs.openstack.org/octavia/latest/>`__
 - `Panko <https://docs.openstack.org/panko/latest/>`__
+- `Qinling <https://docs.openstack.org/qinling/latest/>`__
 - `Rally <https://docs.openstack.org/rally/latest/>`__
 - `Sahara <https://docs.openstack.org/sahara/latest/>`__
 - `Searchlight <https://docs.openstack.org/searchlight/latest/>`__
diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml
index ca1d2971e2..4d4dc75574 100644
--- a/ansible/group_vars/all.yml
+++ b/ansible/group_vars/all.yml
@@ -368,6 +368,8 @@ prometheus_elasticsearch_exporter_port: "9108"
 
 qdrouterd_port: "31459"
 
+qinling_api_port: "7070"
+
 rabbitmq_port: "5672"
 rabbitmq_management_port: "15672"
 rabbitmq_cluster_port: "25672"
@@ -554,6 +556,7 @@ enable_horizon_murano: "{{ enable_murano | bool }}"
 enable_horizon_neutron_lbaas: "{{ enable_neutron_lbaas | bool }}"
 enable_horizon_neutron_vpnaas: "{{ enable_neutron_vpnaas | bool }}"
 enable_horizon_octavia: "{{ enable_octavia | bool }}"
+enable_horizon_qinling: "{{ enable_qinling | bool }}"
 enable_horizon_sahara: "{{ enable_sahara | bool }}"
 enable_horizon_searchlight: "{{ enable_searchlight | bool }}"
 enable_horizon_senlin: "{{ enable_senlin | bool }}"
@@ -608,6 +611,7 @@ enable_osprofiler: "no"
 enable_panko: "no"
 enable_placement: "{{ enable_nova }}"
 enable_qdrouterd: "{{ 'yes' if om_rpc_transport == 'amqp' else 'no' }}"
+enable_qinling: "no"
 enable_rally: "no"
 enable_redis: "no"
 enable_sahara: "no"
@@ -887,6 +891,12 @@ octavia_amp_boot_network_list:
 octavia_amp_secgroup_list:
 octavia_amp_flavor_id:
 
+#################
+# Qinling options
+#################
+# Configure qinling-engine certificates to authenticate with Kubernetes cluster.
+qinling_kubernetes_certificates: "no"
+
 ###################
 # Ceph options
 ###################
diff --git a/ansible/inventory/all-in-one b/ansible/inventory/all-in-one
index 0772ba7948..badb4db5b6 100644
--- a/ansible/inventory/all-in-one
+++ b/ansible/inventory/all-in-one
@@ -163,6 +163,9 @@ monitoring
 [magnum:children]
 control
 
+[qinling:children]
+control
+
 [sahara:children]
 control
 
@@ -508,6 +511,13 @@ magnum
 [magnum-conductor:children]
 magnum
 
+# Qinling
+[qinling-api:children]
+qinling
+
+[qinling-engine:children]
+qinling
+
 # Solum
 [solum-api:children]
 solum
diff --git a/ansible/inventory/multinode b/ansible/inventory/multinode
index 9d07a7d401..4db1174050 100644
--- a/ansible/inventory/multinode
+++ b/ansible/inventory/multinode
@@ -185,6 +185,9 @@ control
 [magnum:children]
 control
 
+[qinling:children]
+control
+
 [sahara:children]
 control
 
@@ -517,6 +520,13 @@ magnum
 [magnum-conductor:children]
 magnum
 
+# Qinling
+[qinling-api:children]
+qinling
+
+[qinling-engine:children]
+qinling
+
 # Sahara
 [sahara-api:children]
 sahara
diff --git a/ansible/roles/common/tasks/config.yml b/ansible/roles/common/tasks/config.yml
index 08b97d36c9..0f6914265f 100644
--- a/ansible/roles/common/tasks/config.yml
+++ b/ansible/roles/common/tasks/config.yml
@@ -286,6 +286,7 @@
     - { name: "opendaylight", enabled: "{{ enable_opendaylight }}" }
     - { name: "outward-rabbitmq", enabled: "{{ enable_outward_rabbitmq }}" }
     - { name: "panko", enabled: "{{ enable_panko }}" }
+    - { name: "qinling", enabled: "{{ enable_qinling }}" }
     - { name: "rabbitmq", enabled: "{{ enable_rabbitmq }}" }
     - { name: "rally", enabled: "{{ enable_rally }}" }
     - { name: "sahara", enabled: "{{ enable_sahara }}" }
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 7f0a77ccde..0e03844ff8 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
@@ -166,4 +166,9 @@
     pattern ^(blazar-api|blazar-manager)$
     tag openstack_python
   </rule>
+  <rule>
+    key     programname
+    pattern ^(qinling-engine|qinling-api)$
+    tag openstack_python
+  </rule>
 </match>
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 16b70fdb83..639d533cef 100644
--- a/ansible/roles/common/templates/conf/input/00-global.conf.j2
+++ b/ansible/roles/common/templates/conf/input/00-global.conf.j2
@@ -29,6 +29,7 @@
     ( 'nova', enable_nova ),
     ( 'octavia', enable_octavia ),
     ( 'panko', enable_panko ),
+    ( 'qinling', enable_qinling ),
     ( 'rally', enable_rally ),
     ( 'sahara', enable_sahara ),
     ( 'searchlight', enable_searchlight ),
diff --git a/ansible/roles/common/templates/cron-logrotate-qinling.conf.j2 b/ansible/roles/common/templates/cron-logrotate-qinling.conf.j2
new file mode 100644
index 0000000000..570dfc2760
--- /dev/null
+++ b/ansible/roles/common/templates/cron-logrotate-qinling.conf.j2
@@ -0,0 +1,3 @@
+"/var/log/kolla/qinling/*.log"
+{
+}
diff --git a/ansible/roles/common/templates/cron.json.j2 b/ansible/roles/common/templates/cron.json.j2
index 91134cd5a9..b8517b2452 100644
--- a/ansible/roles/common/templates/cron.json.j2
+++ b/ansible/roles/common/templates/cron.json.j2
@@ -44,6 +44,7 @@
     ( 'opendaylight', enable_opendaylight ),
     ( 'outward-rabbitmq', enable_outward_rabbitmq ),
     ( 'panko', enable_panko ),
+    ( 'qinling', enable_qinling ),
     ( 'rabbitmq', enable_rabbitmq ),
     ( 'rally', enable_rally ),
     ( 'sahara', enable_sahara ),
diff --git a/ansible/roles/haproxy/tasks/precheck.yml b/ansible/roles/haproxy/tasks/precheck.yml
index 41f9c0b063..20b1e1565a 100644
--- a/ansible/roles/haproxy/tasks/precheck.yml
+++ b/ansible/roles/haproxy/tasks/precheck.yml
@@ -756,6 +756,19 @@
     - haproxy_stat.find('panko_api') == -1
     - "host_running_haproxy == 'None'"
 
+- name: Checking free port for Qinling API HAProxy
+  wait_for:
+    host: "{{ kolla_internal_vip_address }}"
+    port: "{{ qinling_api_port }}"
+    connect_timeout: 1
+    timeout: 1
+    state: stopped
+  when:
+    - enable_qinling | bool
+    - inventory_hostname in groups['haproxy']
+    - haproxy_stat.find('qinling_api') == -1
+    - "host_running_haproxy == 'None'"
+
 - name: Checking free port for RabbitMQ Management HAProxy
   wait_for:
     host: "{{ kolla_internal_vip_address }}"
diff --git a/ansible/roles/horizon/defaults/main.yml b/ansible/roles/horizon/defaults/main.yml
index babe266dfe..6d064764e8 100644
--- a/ansible/roles/horizon/defaults/main.yml
+++ b/ansible/roles/horizon/defaults/main.yml
@@ -24,6 +24,7 @@ horizon_services:
       ENABLE_NEUTRON_LBAAS: "{{ 'yes' if enable_horizon_neutron_lbaas | bool else 'no' }}"
       ENABLE_NEUTRON_VPNAAS: "{{ 'yes' if enable_horizon_neutron_vpnaas | bool else 'no' }}"
       ENABLE_OCTAVIA: "{{ 'yes' if enable_horizon_octavia | bool else 'no' }}"
+      ENABLE_QINLING: "{{ 'yes' if enable_horizon_qinling | bool else 'no' }}"
       ENABLE_SAHARA: "{{ 'yes' if enable_horizon_sahara | bool else 'no' }}"
       ENABLE_SEARCHLIGHT: "{{ 'yes' if enable_horizon_searchlight | bool else 'no' }}"
       ENABLE_SENLIN: "{{ 'yes' if enable_horizon_senlin | bool else 'no' }}"
diff --git a/ansible/roles/horizon/tasks/config.yml b/ansible/roles/horizon/tasks/config.yml
index 3545b69498..0ea4b3bfc0 100644
--- a/ansible/roles/horizon/tasks/config.yml
+++ b/ansible/roles/horizon/tasks/config.yml
@@ -37,6 +37,7 @@
     - { name: "murano", enabled: "{{ enable_horizon_murano }}" }
     - { name: "neutron", enabled: "{{ enable_neutron_horizon_policy_file }}" }
     - { name: "nova", enabled: "{{ enable_nova_horizon_policy_file }}" }
+    - { name: "qinling", enabled: "{{ enable_horizon_qinling }}" }
     - { name: "sahara", enabled: "{{ enable_horizon_sahara }}" }
     - { name: "searchlight", enabled: "{{ enable_horizon_searchlight }}" }
     - { name: "senlin", enabled: "{{ enable_horizon_senlin }}" }
diff --git a/ansible/roles/horizon/templates/horizon.json.j2 b/ansible/roles/horizon/templates/horizon.json.j2
index 90775ace05..1b302cd815 100644
--- a/ansible/roles/horizon/templates/horizon.json.j2
+++ b/ansible/roles/horizon/templates/horizon.json.j2
@@ -18,6 +18,7 @@
     ( 'murano', enable_horizon_murano ),
     ( 'neutron', enable_neutron ),
     ( 'nova', enable_nova ),
+    ( 'qinling', enable_horizon_qinling ),
     ( 'sahara', enable_horizon_sahara ),
     ( 'searchlight', enable_horizon_searchlight ),
     ( 'senlin', enable_horizon_senlin ),
diff --git a/ansible/roles/qinling/defaults/main.yml b/ansible/roles/qinling/defaults/main.yml
new file mode 100644
index 0000000000..13db9a3bfb
--- /dev/null
+++ b/ansible/roles/qinling/defaults/main.yml
@@ -0,0 +1,88 @@
+---
+project_name: "qinling"
+
+qinling_services:
+  qinling-api:
+    container_name: qinling_api
+    group: qinling-api
+    enabled: true
+    image: "{{ qinling_api_image_full }}"
+    volumes:
+      - "{{ node_config_directory }}/qinling-api/:{{ container_config_directory }}/:ro"
+      - "/etc/localtime:/etc/localtime:ro"
+      - "{{ kolla_dev_repos_directory ~ '/qinling/qinling:/var/lib/kolla/venv/lib/python2.7/site-packages/qinling' if qinling_dev_mode | bool else '' }}"
+      - "kolla_logs:/var/log/kolla/"
+    dimensions: "{{ qinling_api_dimensions }}"
+    haproxy:
+      qinling_api:
+        enabled: "{{ enable_qinling }}"
+        mode: "http"
+        external: false
+        port: "{{ qinling_api_port }}"
+      qinling_api_external:
+        enabled: "{{ enable_qinling }}"
+        mode: "http"
+        external: true
+        port: "{{ qinling_api_port }}"
+  qinling-engine:
+    container_name: qinling_engine
+    group: qinling-engine
+    enabled: true
+    image: "{{ qinling_engine_image_full }}"
+    environment: "{{ container_proxy }}"
+    volumes:
+      - "{{ node_config_directory }}/qinling-engine/:{{ container_config_directory }}/:ro"
+      - "/etc/localtime:/etc/localtime:ro"
+      - "qinling:/var/lib/qinling/"
+      - "{{ kolla_dev_repos_directory ~ '/qinling/qinling:/var/lib/kolla/venv/lib/python2.7/site-packages/qinling' if qinling_dev_mode | bool else '' }}"
+      - "kolla_logs:/var/log/kolla/"
+    dimensions: "{{ qinling_engine_dimensions }}"
+
+
+####################
+# Database
+####################
+qinling_database_name: "qinling"
+qinling_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}qinling{% endif %}"
+qinling_database_address: "{{ database_address }}:{{ database_port }}"
+
+
+####################
+# Docker
+####################
+qinling_install_type: "{{ kolla_install_type }}"
+qinling_tag: "{{ openstack_release }}"
+
+qinling_api_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ kolla_base_distro }}-{{ qinling_install_type }}-qinling-api"
+qinling_api_tag: "{{ qinling_tag }}"
+qinling_api_image_full: "{{ qinling_api_image }}:{{ qinling_api_tag }}"
+
+qinling_engine_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ kolla_base_distro }}-{{ qinling_install_type }}-qinling-engine"
+qinling_engine_tag: "{{ qinling_tag }}"
+qinling_engine_image_full: "{{ qinling_engine_image }}:{{ qinling_engine_tag }}"
+
+qinling_api_dimensions: "{{ default_container_dimensions }}"
+qinling_engine_dimensions: "{{ default_container_dimensions }}"
+
+
+####################
+# OpenStack
+####################
+qinling_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ qinling_api_port }}"
+qinling_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ qinling_api_port }}"
+qinling_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ qinling_api_port }}"
+
+qinling_logging_debug: "{{ openstack_logging_debug }}"
+
+qinling_keystone_user: "qinling"
+
+openstack_qinling_auth: "{{ openstack_auth }}"
+
+
+####################
+# Kolla
+####################
+qinling_git_repository: "{{ kolla_dev_repos_git }}/{{ project_name }}"
+qinling_dev_repos_pull: "{{ kolla_dev_repos_pull }}"
+qinling_dev_mode: "{{ kolla_dev_mode }}"
+qinling_source_version: "{{ kolla_source_version }}"
diff --git a/ansible/roles/qinling/handlers/main.yml b/ansible/roles/qinling/handlers/main.yml
new file mode 100644
index 0000000000..3963f068d5
--- /dev/null
+++ b/ansible/roles/qinling/handlers/main.yml
@@ -0,0 +1,52 @@
+---
+- name: Restart qinling-api container
+  vars:
+    service_name: "qinling-api"
+    service: "{{ qinling_services[service_name] }}"
+    config_json: "{{ qinling_config_jsons.results|selectattr('item.key', 'equalto', service_name)|first }}"
+    qinling_conf: "{{ qinling_confs.results|selectattr('item.key', 'equalto', service_name)|first }}"
+    policy_overwriting: "{{ qinling_policy_overwriting.results|selectattr('item.key', 'equalto', service_name)|first }}"
+    qinling_api_container: "{{ check_qinling_containers.results|selectattr('item.key', 'equalto', service_name)|first }}"
+  become: true
+  kolla_docker:
+    action: "recreate_or_restart_container"
+    common_options: "{{ docker_common_options }}"
+    name: "{{ service.container_name }}"
+    image: "{{ service.image }}"
+    volumes: "{{ service.volumes|reject('equalto', '')|list }}"
+    environment: "{{ service.environment }}"
+    dimensions: "{{ service.dimensions }}"
+  when:
+    - kolla_action != "config"
+    - inventory_hostname in groups[service.group]
+    - service.enabled | bool
+    - config_json.changed | bool
+      or qinling_conf.changed | bool
+      or policy_overwriting.changed | bool
+      or qinling_api_container.changed | bool
+
+- name: Restart qinling-engine container
+  vars:
+    service_name: "qinling-engine"
+    service: "{{ qinling_services[service_name] }}"
+    config_json: "{{ qinling_config_jsons.results|selectattr('item.key', 'equalto', service_name)|first }}"
+    qinling_conf: "{{ qinling_confs.results|selectattr('item.key', 'equalto', service_name)|first }}"
+    policy_overwriting: "{{ qinling_policy_overwriting.results|selectattr('item.key', 'equalto', service_name)|first }}"
+    qinling_engine_container: "{{ check_qinling_containers.results|selectattr('item.key', 'equalto', service_name)|first }}"
+  become: true
+  kolla_docker:
+    action: "recreate_or_restart_container"
+    common_options: "{{ docker_common_options }}"
+    name: "{{ service.container_name }}"
+    image: "{{ service.image }}"
+    volumes: "{{ service.volumes|reject('equalto', '')|list }}"
+    environment: "{{ service.environment }}"
+    dimensions: "{{ service.dimensions }}"
+  when:
+    - kolla_action != "config"
+    - inventory_hostname in groups[service.group]
+    - service.enabled | bool
+    - config_json.changed | bool
+      or qinling_conf.changed | bool
+      or policy_overwriting.changed | bool
+      or qinling_engine_container.changed | bool
diff --git a/ansible/roles/qinling/meta/main.yml b/ansible/roles/qinling/meta/main.yml
new file mode 100644
index 0000000000..6b4fff8fef
--- /dev/null
+++ b/ansible/roles/qinling/meta/main.yml
@@ -0,0 +1,3 @@
+---
+dependencies:
+  - { role: common }
diff --git a/ansible/roles/qinling/tasks/bootstrap.yml b/ansible/roles/qinling/tasks/bootstrap.yml
new file mode 100644
index 0000000000..838d8adbd6
--- /dev/null
+++ b/ansible/roles/qinling/tasks/bootstrap.yml
@@ -0,0 +1,38 @@
+---
+- name: Creating Qinling database
+  become: true
+  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: "{{ qinling_database_name }}"
+  register: database
+  run_once: True
+  delegate_to: "{{ groups['qinling-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
+
+- name: Creating Qinling database user and setting permissions
+  become: true
+  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: "{{ qinling_database_user }}"
+      password: "{{ qinling_database_password }}"
+      host: "%"
+      priv: "{{ qinling_database_name }}.*:ALL"
+      append_privs: "yes"
+  run_once: True
+  delegate_to: "{{ groups['qinling-api'][0] }}"
+  when:
+    - not use_preconfigured_databases | bool
+
+- include_tasks: bootstrap_service.yml
+  when: database.changed or use_preconfigured_databases | bool
diff --git a/ansible/roles/qinling/tasks/bootstrap_service.yml b/ansible/roles/qinling/tasks/bootstrap_service.yml
new file mode 100644
index 0000000000..7428401cd5
--- /dev/null
+++ b/ansible/roles/qinling/tasks/bootstrap_service.yml
@@ -0,0 +1,20 @@
+---
+- name: Running Qinling bootstrap container
+  vars:
+    qinling_api: "{{ qinling_services['qinling-api'] }}"
+  become: true
+  kolla_docker:
+    action: "start_container"
+    common_options: "{{ docker_common_options }}"
+    detach: False
+    environment:
+      KOLLA_BOOTSTRAP:
+      KOLLA_CONFIG_STRATEGY: "{{ config_strategy }}"
+    image: "{{ qinling_api.image }}"
+    labels:
+      BOOTSTRAP:
+    name: "bootstrap_qinling"
+    restart_policy: "never"
+    volumes: "{{ qinling_api.volumes|reject('equalto', '')|list }}"
+  run_once: True
+  delegate_to: "{{ groups[qinling_api.group][0] }}"
diff --git a/ansible/roles/qinling/tasks/check.yml b/ansible/roles/qinling/tasks/check.yml
new file mode 100644
index 0000000000..ed97d539c0
--- /dev/null
+++ b/ansible/roles/qinling/tasks/check.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible/roles/qinling/tasks/clone.yml b/ansible/roles/qinling/tasks/clone.yml
new file mode 100644
index 0000000000..dc608348b5
--- /dev/null
+++ b/ansible/roles/qinling/tasks/clone.yml
@@ -0,0 +1,7 @@
+---
+- name: Cloning qinling source repository for development
+  git:
+    repo: "{{ qinling_git_repository }}"
+    dest: "{{ kolla_dev_repos_directory }}/{{ project_name }}"
+    update: "{{ qinling_dev_repos_pull }}"
+    version: "{{ qinling_source_version }}"
diff --git a/ansible/roles/qinling/tasks/config.yml b/ansible/roles/qinling/tasks/config.yml
new file mode 100644
index 0000000000..67c87cde01
--- /dev/null
+++ b/ansible/roles/qinling/tasks/config.yml
@@ -0,0 +1,104 @@
+---
+- name: Ensuring config directories exist
+  file:
+    path: "{{ node_config_directory }}/{{ item.key }}"
+    state: "directory"
+    owner: "{{ config_owner_user }}"
+    group: "{{ config_owner_group }}"
+    mode: "0770"
+  become: true
+  when:
+    - inventory_hostname in groups[item.value.group]
+    - item.value.enabled | bool
+  with_dict: "{{ qinling_services }}"
+
+- include_tasks: external_kubernetes.yml
+  when:
+    - (enable_qinling | bool) and (qinling_kubernetes_certificates | bool)
+    - inventory_hostname in groups['qinling-engine']
+
+- name: Check if policies shall be overwritten
+  local_action: stat path="{{ item }}"
+  run_once: True
+  register: qinling_policy
+  with_first_found:
+    - files: "{{ supported_policy_format_list }}"
+      paths:
+        - "{{ node_custom_config }}/qinling/"
+      skip: true
+
+- name: Set qinling policy file
+  set_fact:
+    qinling_policy_file: "{{ qinling_policy.results.0.stat.path | basename }}"
+    qinling_policy_file_path: "{{ qinling_policy.results.0.stat.path }}"
+  when:
+    - qinling_policy.results
+
+- name: Copying over config.json files for services
+  template:
+    src: "{{ item.key }}.json.j2"
+    dest: "{{ node_config_directory }}/{{ item.key }}/config.json"
+    mode: "0660"
+  become: true
+  register: qinling_config_jsons
+  when:
+    - inventory_hostname in groups[item.value.group]
+    - item.value.enabled | bool
+  with_dict: "{{ qinling_services }}"
+  notify:
+    - Restart {{ item.key }} container
+
+- name: Copying over qinling.conf
+  vars:
+    service_name: "{{ item.key }}"
+  merge_configs:
+    sources:
+      - "{{ role_path }}/templates/qinling.conf.j2"
+      - "{{ node_custom_config }}/global.conf"
+      - "{{ node_custom_config }}/qinling.conf"
+      - "{{ node_custom_config }}/qinling/{{ item.key }}.conf"
+      - "{{ node_custom_config }}/qinling/{{ inventory_hostname }}/qinling.conf"
+    dest: "{{ node_config_directory }}/{{ item.key }}/qinling.conf"
+    mode: "0660"
+  become: true
+  register: qinling_confs
+  when:
+    - inventory_hostname in groups[item.value.group]
+    - item.value.enabled | bool
+  with_dict: "{{ qinling_services }}"
+  notify:
+    - Restart {{ item.key }} container
+
+- name: Copying over existing policy file
+  template:
+    src: "{{ qinling_policy_file_path }}"
+    dest: "{{ node_config_directory }}/{{ item.key }}/{{ qinling_policy_file }}"
+    mode: "0660"
+  become: true
+  register: qinling_policy_overwriting
+  when:
+    - qinling_policy_file is defined
+    - inventory_hostname in groups[item.value.group]
+    - item.value.enabled | bool
+  with_dict: "{{ qinling_services }}"
+  notify:
+    - Restart {{ item.key }} container
+
+- name: Check qinling containers
+  become: true
+  kolla_docker:
+    action: "compare_container"
+    common_options: "{{ docker_common_options }}"
+    name: "{{ item.value.container_name }}"
+    image: "{{ item.value.image }}"
+    volumes: "{{ item.value.volumes|reject('equalto', '')|list }}"
+    dimensions: "{{ item.value.dimensions }}"
+    environment: "{{ item.value.environment }}"
+  register: check_qinling_containers
+  when:
+    - kolla_action != "config"
+    - inventory_hostname in groups[item.value.group]
+    - item.value.enabled | bool
+  with_dict: "{{ qinling_services }}"
+  notify:
+    - Restart {{ item.key }} container
diff --git a/ansible/roles/qinling/tasks/deploy.yml b/ansible/roles/qinling/tasks/deploy.yml
new file mode 100644
index 0000000000..471893e058
--- /dev/null
+++ b/ansible/roles/qinling/tasks/deploy.yml
@@ -0,0 +1,16 @@
+---
+- include_tasks: register.yml
+  when: inventory_hostname in groups['qinling-api']
+
+- include_tasks: config.yml
+  when: inventory_hostname in groups['qinling-api'] or
+        inventory_hostname in groups['qinling-engine']
+
+- include_tasks: clone.yml
+  when: qinling_dev_mode | bool
+
+- include_tasks: bootstrap.yml
+  when: inventory_hostname in groups['qinling-api']
+
+- name: Flush handlers
+  meta: flush_handlers
diff --git a/ansible/roles/qinling/tasks/external_kubernetes.yml b/ansible/roles/qinling/tasks/external_kubernetes.yml
new file mode 100644
index 0000000000..0dee19b692
--- /dev/null
+++ b/ansible/roles/qinling/tasks/external_kubernetes.yml
@@ -0,0 +1,19 @@
+---
+- name: Copy over Kubernetes certificates files for qinling-engine
+  copy:
+    src: "{{ item }}"
+    dest: "{{ node_config_directory }}/qinling-engine/"
+    mode: "0660"
+    owner: "{{ config_owner_user }}"
+    group: "{{ config_owner_group }}"
+  become: true
+  register: qinling_engine_kubernetes_files
+  with_items:
+    - "{{ node_custom_config }}/qinling/qinling-engine/kubernetes_ca.crt"
+    - "{{ node_custom_config }}/qinling/qinling-engine/kubernetes.crt"
+    - "{{ node_custom_config }}/qinling/qinling-engine/kubernetes.key"
+  when:
+    - qinling_kubernetes_certificates | bool
+    - inventory_hostname in groups['qinling-engine']
+  notify:
+    - Restart qinling-engine container
diff --git a/ansible/roles/qinling/tasks/loadbalancer.yml b/ansible/roles/qinling/tasks/loadbalancer.yml
new file mode 100644
index 0000000000..4b0b6ccbf9
--- /dev/null
+++ b/ansible/roles/qinling/tasks/loadbalancer.yml
@@ -0,0 +1,7 @@
+---
+- name: "Configure haproxy for {{ project_name }}"
+  import_role:
+    role: haproxy-config
+  vars:
+    project_services: "{{ qinling_services }}"
+  tags: always
diff --git a/ansible/roles/qinling/tasks/main.yml b/ansible/roles/qinling/tasks/main.yml
new file mode 100644
index 0000000000..bc5d1e6257
--- /dev/null
+++ b/ansible/roles/qinling/tasks/main.yml
@@ -0,0 +1,2 @@
+---
+- include_tasks: "{{ kolla_action }}.yml"
diff --git a/ansible/roles/qinling/tasks/precheck.yml b/ansible/roles/qinling/tasks/precheck.yml
new file mode 100644
index 0000000000..724fd02977
--- /dev/null
+++ b/ansible/roles/qinling/tasks/precheck.yml
@@ -0,0 +1,17 @@
+---
+- name: Get container facts
+  kolla_container_facts:
+    name:
+      - qinling_api
+  register: container_facts
+
+- name: Checking free port for Qinling API
+  wait_for:
+    host: "{{ api_interface_address }}"
+    port: "{{ qinling_api_port }}"
+    connect_timeout: 1
+    timeout: 1
+    state: stopped
+  when:
+    - container_facts['qinling_api'] is not defined
+    - inventory_hostname in groups['qinling-api']
diff --git a/ansible/roles/qinling/tasks/pull.yml b/ansible/roles/qinling/tasks/pull.yml
new file mode 100644
index 0000000000..8cd89824e5
--- /dev/null
+++ b/ansible/roles/qinling/tasks/pull.yml
@@ -0,0 +1,11 @@
+---
+- name: Pulling qinling images
+  become: true
+  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: "{{ qinling_services }}"
diff --git a/ansible/roles/qinling/tasks/reconfigure.yml b/ansible/roles/qinling/tasks/reconfigure.yml
new file mode 100644
index 0000000000..f670a5b78d
--- /dev/null
+++ b/ansible/roles/qinling/tasks/reconfigure.yml
@@ -0,0 +1,2 @@
+---
+- include_tasks: deploy.yml
diff --git a/ansible/roles/qinling/tasks/register.yml b/ansible/roles/qinling/tasks/register.yml
new file mode 100644
index 0000000000..f1a8914ab2
--- /dev/null
+++ b/ansible/roles/qinling/tasks/register.yml
@@ -0,0 +1,34 @@
+---
+- name: Creating the Qinling service and endpoint
+  become: true
+  kolla_toolbox:
+    module_name: "kolla_keystone_service"
+    module_args:
+      service_name: "qinling"
+      service_type: "function-engine"
+      description: "Function Service"
+      endpoint_region: "{{ openstack_region_name }}"
+      url: "{{ item.url }}"
+      interface: "{{ item.interface }}"
+      region_name: "{{ openstack_region_name }}"
+      auth: "{{ openstack_qinling_auth }}"
+      endpoint_type: "{{ openstack_interface }}"
+  run_once: True
+  with_items:
+    - {'interface': 'admin', 'url': '{{ qinling_admin_endpoint }}'}
+    - {'interface': 'internal', 'url': '{{ qinling_internal_endpoint }}'}
+    - {'interface': 'public', 'url': '{{ qinling_public_endpoint }}'}
+
+- name: Creating the Qinling project, user, and role
+  become: true
+  kolla_toolbox:
+    module_name: "kolla_keystone_user"
+    module_args:
+      project: "service"
+      user: "{{ qinling_keystone_user }}"
+      password: "{{ qinling_keystone_password }}"
+      role: "admin"
+      region_name: "{{ openstack_region_name }}"
+      auth: "{{ openstack_qinling_auth }}"
+      endpoint_type: "{{ openstack_interface }}"
+  run_once: True
diff --git a/ansible/roles/qinling/tasks/stop.yml b/ansible/roles/qinling/tasks/stop.yml
new file mode 100644
index 0000000000..c3a4142ace
--- /dev/null
+++ b/ansible/roles/qinling/tasks/stop.yml
@@ -0,0 +1,6 @@
+---
+- import_role:
+    role: service-stop
+  vars:
+    project_services: "{{ qinling_services }}"
+    service_name: "{{ project_name }}"
diff --git a/ansible/roles/qinling/tasks/upgrade.yml b/ansible/roles/qinling/tasks/upgrade.yml
new file mode 100644
index 0000000000..20ccddc8f1
--- /dev/null
+++ b/ansible/roles/qinling/tasks/upgrade.yml
@@ -0,0 +1,7 @@
+---
+- include_tasks: config.yml
+
+- include_tasks: bootstrap_service.yml
+
+- name: Flush handlers
+  meta: flush_handlers
diff --git a/ansible/roles/qinling/templates/qinling-api.json.j2 b/ansible/roles/qinling/templates/qinling-api.json.j2
new file mode 100644
index 0000000000..230219f057
--- /dev/null
+++ b/ansible/roles/qinling/templates/qinling-api.json.j2
@@ -0,0 +1,24 @@
+{
+    "command": "qinling-api --config-file /etc/qinling/qinling.conf",
+    "config_files": [
+        {
+            "source": "{{ container_config_directory }}/qinling.conf",
+            "dest": "/etc/qinling/qinling.conf",
+            "owner": "qinling",
+            "perm": "0600"
+        }{% if qinling_policy_file is defined %},
+        {
+            "source": "{{ container_config_directory }}/{{ qinling_policy_file }}",
+            "dest": "/etc/qinling/{{ qinling_policy_file }}",
+            "owner": "qinling",
+            "perm": "0600"
+        }{% endif %}
+    ],
+    "permissions": [
+        {
+            "path": "/var/log/kolla/qinling",
+            "owner": "qinling:qinling",
+            "recurse": true
+        }
+    ]
+}
diff --git a/ansible/roles/qinling/templates/qinling-engine.json.j2 b/ansible/roles/qinling/templates/qinling-engine.json.j2
new file mode 100644
index 0000000000..b1f8bd4c41
--- /dev/null
+++ b/ansible/roles/qinling/templates/qinling-engine.json.j2
@@ -0,0 +1,47 @@
+{
+    "command": "qinling-engine --config-file /etc/qinling/qinling.conf",
+    "config_files": [
+        {
+            "source": "{{ container_config_directory }}/qinling.conf",
+            "dest": "/etc/qinling/qinling.conf",
+            "owner": "qinling",
+            "perm": "0600"
+        }{% if qinling_policy_file is defined %},
+        {
+            "source": "{{ container_config_directory }}/{{ qinling_policy_file }}",
+            "dest": "/etc/qinling/{{ qinling_policy_file }}",
+            "owner": "qinling",
+            "perm": "0600"
+        }{% endif %}{% if qinling_kubernetes_certificates is defined and qinling_kubernetes_certificates | bool %},
+        {
+            "source": "{{ container_config_directory }}/kubernetes_ca.crt",
+            "dest": "/etc/qinling/pki/kubernetes/ca.crt",
+            "owner": "qinling",
+            "perm": "0600"
+        },
+        {
+            "source": "{{ container_config_directory }}/kubernetes.crt",
+            "dest": "/etc/qinling/pki/kubernetes/qinling.crt",
+            "owner": "qinling",
+            "perm": "0600"
+        },
+        {
+            "source": "{{ container_config_directory }}/kubernetes.key",
+            "dest": "/etc/qinling/pki/kubernetes/qinling.key",
+            "owner": "qinling",
+            "perm": "0600"
+        }{% endif %}
+    ],
+    "permissions": [
+        {
+            "path": "/var/lib/qinling",
+            "owner": "qinling:qinling",
+            "recurse": true
+        },
+        {
+            "path": "/var/log/kolla/qinling",
+            "owner": "qinling:qinling",
+            "recurse": true
+        }
+    ]
+}
diff --git a/ansible/roles/qinling/templates/qinling.conf.j2 b/ansible/roles/qinling/templates/qinling.conf.j2
new file mode 100644
index 0000000000..b7b20c8258
--- /dev/null
+++ b/ansible/roles/qinling/templates/qinling.conf.j2
@@ -0,0 +1,62 @@
+[DEFAULT]
+debug = {{ qinling_logging_debug }}
+log_dir = /var/log/kolla/qinling
+transport_url = {{ rpc_transport_url }}
+
+{% if service_name == 'qinling-api' %}
+[api]
+port = {{ qinling_api_port }}
+host = {{ api_interface_address }}
+api_workers = {{ openstack_service_workers }}
+{% endif %}
+
+{% if service_name == 'qinling-engine' %}
+[engine]
+host = {{ api_interface_address }}
+{% endif %}
+
+[database]
+connection = mysql+pymysql://{{ qinling_database_user }}:{{ qinling_database_password }}@{{ qinling_database_address }}/{{ qinling_database_name }}
+max_retries = -1
+
+[keystone_authtoken]
+www_authenticate_uri = {{ keystone_internal_url }}/v3
+auth_url = {{ keystone_admin_url }}
+auth_type = password
+project_domain_name = {{ default_project_domain_name }}
+user_domain_name = {{ default_user_domain_name }}
+project_name = service
+username = {{ qinling_keystone_user }}
+password = {{ qinling_keystone_password }}
+region_name = {{ openstack_region_name }}
+
+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 %}
+
+[storage]
+file_system_dir = /var/lib/qinling/package
+
+[etcd]
+{% if enable_etcd | bool %}
+host = {{ api_interface_address }}
+port = {{ etcd_client_port }}
+protocol = {{ internal_protocol }}
+{% endif %}
+
+[oslo_messaging_notifications]
+transport_url = {{ notify_transport_url }}
+{% if enable_ceilometer | bool %}
+driver = messagingv2
+topics = notifications
+{% else %}
+driver = noop
+{% endif %}
+
+{% if qinling_policy_file is defined %}
+[oslo_policy]
+policy_file = {{ qinling_policy_file }}
+{% endif %}
+
+[oslo_middleware]
+enable_proxy_headers_parsing = True
diff --git a/ansible/site.yml b/ansible/site.yml
index 595e243837..2f27d6e4e1 100644
--- a/ansible/site.yml
+++ b/ansible/site.yml
@@ -69,6 +69,7 @@
         - enable_placement_{{ enable_placement | bool }}
         - enable_prometheus_{{ enable_prometheus | bool }}
         - enable_qdrouterd_{{ enable_qdrouterd | bool }}
+        - enable_qinling_{{ enable_qinling | bool }}
         - enable_rabbitmq_{{ enable_rabbitmq | bool }}
         - enable_rally_{{ enable_rally | bool }}
         - enable_redis_{{ enable_redis | bool }}
@@ -302,6 +303,11 @@
             tasks_from: loadbalancer
           tags: prometheus
           when: enable_prometheus | bool
+        - include_role:
+            role: qinling
+            tasks_from: loadbalancer
+          tags: qinling
+          when: enable_qinling | bool
         - include_role:
             role: rabbitmq
             tasks_from: loadbalancer
@@ -863,6 +869,18 @@
         tags: mistral,
         when: enable_mistral | bool }
 
+- name: Apply role qinling
+  gather_facts: false
+  hosts:
+    - qinling-api
+    - qinling-engine
+    - '&enable_qinling_True'
+  serial: '{{ kolla_serial|default("0") }}'
+  roles:
+    - { role: qinling,
+        tags: qinling,
+        when: enable_qinling | bool }
+
 - name: Apply role sahara
   gather_facts: false
   hosts:
diff --git a/doc/source/reference/compute/index.rst b/doc/source/reference/compute/index.rst
index ba17f64e0e..aa0e86a2bc 100644
--- a/doc/source/reference/compute/index.rst
+++ b/doc/source/reference/compute/index.rst
@@ -10,6 +10,7 @@ compute services like HyperV, XenServer and so on.
 
    hyperv-guide
    nova-fake-driver
+   qinling-guide
    vmware-guide
    xenserver-guide
    zun-guide
diff --git a/doc/source/reference/compute/qinling-guide.rst b/doc/source/reference/compute/qinling-guide.rst
new file mode 100644
index 0000000000..4aaa32e2bd
--- /dev/null
+++ b/doc/source/reference/compute/qinling-guide.rst
@@ -0,0 +1,96 @@
+.. _qinling-guide:
+
+=========================
+Qinling - Function Engine
+=========================
+
+Overview
+~~~~~~~~
+
+Qinling aims to provide a platform to support serverless functions
+(like AWS Lambda). Qinling supports different container orchestration
+platforms (Kubernetes/Swarm, etc...) and different function package storage
+backends (local/Swift/S3) by nature using plugin mechanism.
+
+Kolla deploys Qinling API and Qinling Engine containers which are the main
+Qinling components but it needs to be connected to an existing container
+orchestration platforms.
+
+Apply custom policies to Qinling API
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Custom policies could be apply by creating ``policy.json`` file under
+``/etc/kolla/config/qinling`` directory.
+
+
+Connect to an existing Kubernetes cluster
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Certificates
+------------
+
+``qinling-engine`` authenticates to Kubernetes by using certificates.
+
+.. note::
+   If the cluster has not been created with OpenStack Magnum then
+   certificates need to be gathered using different methods that will not
+   be mentioned here.
+
+If the Kubernetes cluster has been deployed with OpenStack Magnum then the
+OpenStack client should be used to retrieve the certificates.
+
+.. code-block:: console
+
+   openstack coe cluster config --dir . 687f7476-5604-4b44-8b09-b7a4f3fdbd64 --output-certs
+
+Where ``687f7476-5604-4b44-8b09-b7a4f3fdbd64`` is the Kubernetes cluster ID
+created with Magnum.
+
+Four files should have been generated:
+
+  - ``ca.pem``
+  - ``cert.pem``
+  - ``key.pem``
+  - ``config``
+
+Only ``ca.pem``, ``cert.pem`` and ``key.pem`` will be used, these files have
+to be stored in ``/etc/kolla/config/qinling/qinling-engine`` directory under
+these file name:
+
+  - ``ca.pem``: ``/etc/kolla/config/qinling/qinling-engine/kubernetes_ca.crt``
+  - ``cert.pem``: ``/etc/kolla/config/qinling/qinling-engine/kubernetes.crt``
+  - ``key.pem``: ``/etc/kolla/config/qinling/qinling-engine/kubernetes.key``
+
+
+Declare ``qinling_kubernetes_certificates`` variable in
+``/etc/kolla/globals.yml``:
+
+.. code-block:: yaml
+
+   qinling_kubernetes_certificates: "yes"
+
+Kubernetes cluster
+------------------
+
+``qinling-engine`` needs to know where to connect, the information is
+provided by options under ``[kubernetes]`` section inside ``qinling.conf``
+configuration file.
+
+As mentioned above, these settings are only required by ``qinling-engine``,
+put the content in ``/etc/kolla/config/qinling/qinling-engine.conf``.
+
+.. code-block:: ini
+
+   [kubernetes]
+   kube_host = https://192.168.1.168:6443
+   use_api_certificate = True
+   ssl_ca_cert = /etc/qinling/pki/kubernetes/ca.crt
+   cert_file = /etc/qinling/pki/kubernetes/qinling.crt
+   key_file = /etc/qinling/pki/kubernetes/qinling.key
+   trusted_cidrs = 192.168.1.0/24,10.0.0.53/32
+
+``kube_host`` is the the Kubernetes cluster API address, ``https`` protocol
+have to be defined.
+
+``trusted_cidrs`` is a list of CIDR trusted by the Kubernetes cluster defined
+by a network policy under the ``qinling`` namespace.
diff --git a/etc/kolla/globals.yml b/etc/kolla/globals.yml
index f091b4ca3f..83ae8167ec 100644
--- a/etc/kolla/globals.yml
+++ b/etc/kolla/globals.yml
@@ -236,6 +236,7 @@ kolla_internal_vip_address: "10.10.10.254"
 #enable_horizon_neutron_lbaas: "{{ enable_neutron_lbaas | bool }}"
 #enable_horizon_neutron_vpnaas: "{{ enable_neutron_vpnaas | bool }}"
 #enable_horizon_octavia: "{{ enable_octavia | bool }}"
+#enable_horizon_qinling: "{{ enable_qinling | bool }}"
 #enable_horizon_sahara: "{{ enable_sahara | bool }}"
 #enable_horizon_searchlight: "{{ enable_searchlight | bool }}"
 #enable_horizon_senlin: "{{ enable_senlin | bool }}"
@@ -288,6 +289,7 @@ kolla_internal_vip_address: "10.10.10.254"
 #enable_placement: "{{ enable_nova }}"
 #enable_prometheus: "no"
 #enable_qdrouterd: "no"
+#enable_qinling: "no"
 #enable_rally: "no"
 #enable_redis: "no"
 #enable_sahara: "no"
diff --git a/etc/kolla/passwords.yml b/etc/kolla/passwords.yml
index 43f919021a..87204dac10 100644
--- a/etc/kolla/passwords.yml
+++ b/etc/kolla/passwords.yml
@@ -196,6 +196,9 @@ vitrage_keystone_password:
 
 memcache_secret_key:
 
+qinling_database_password:
+qinling_keystone_password:
+
 # HMAC secret key
 osprofiler_secret:
 
diff --git a/releasenotes/notes/add-qinling-role-99d96102837b21f4.yaml b/releasenotes/notes/add-qinling-role-99d96102837b21f4.yaml
new file mode 100644
index 0000000000..b2fcdb981c
--- /dev/null
+++ b/releasenotes/notes/add-qinling-role-99d96102837b21f4.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - Adds Qinling Ansible role. Qinling is an OpenStack project
+    to provide "Function as a Service". This project aims to
+    provide a platform to support serverless functions.
diff --git a/tests/templates/inventory.j2 b/tests/templates/inventory.j2
index e3137964a3..2697b405d0 100644
--- a/tests/templates/inventory.j2
+++ b/tests/templates/inventory.j2
@@ -159,6 +159,9 @@ monitoring
 [magnum:children]
 control
 
+[qinling:children]
+control
+
 [sahara:children]
 control
 
@@ -457,6 +460,13 @@ magnum
 [magnum-conductor:children]
 magnum
 
+# Qinling
+[qinling-api:children]
+qinling
+
+[qinling-engine:children]
+qinling
+
 # Solum
 [solum-api:children]
 solum