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 `__
- `Octavia `__
- `Panko `__
+- `Qinling `__
- `Rally `__
- `Sahara `__
- `Searchlight `__
diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml
index 5d63e9d8be..47587084d7 100644
--- a/ansible/group_vars/all.yml
+++ b/ansible/group_vars/all.yml
@@ -375,6 +375,8 @@ prometheus_elasticsearch_exporter_port: "9108"
qdrouterd_port: "31459"
+qinling_api_port: "7070"
+
rabbitmq_port: "5672"
rabbitmq_management_port: "15672"
rabbitmq_cluster_port: "25672"
@@ -561,6 +563,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 }}"
@@ -615,6 +618,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"
@@ -894,6 +898,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
+
+ key programname
+ pattern ^(qinling-engine|qinling-api)$
+ tag openstack_python
+
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 240cfb9207..81c2739d47 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