Merge "RabbitMQ TLS support"
diff --git a/README.rst b/README.rst
index ab61b71..dd23ebf 100644
--- a/README.rst
+++ b/README.rst
@@ -146,6 +146,61 @@
           # Add key without value to remove line from policy.json
           'compute:create:attach_network':
 
+
+Client-side RabbitMQ TLS configuration:
+---------------------------------------
+
+To enable TLS for oslo.messaging you need to provide the CA certificate.
+
+By default system-wide CA certs is used. Nothing should be specified except `ssl.enabled`.
+
+.. code-block:: yaml
+
+  nova:
+    controller:
+      ....
+      message_queue:
+        ssl:
+          enabled: True
+
+
+
+Use `cacert_file` option to specify the CA-cert file path explicitly:
+
+.. code-block:: yaml
+
+  nova:
+    controller:
+      ....
+      message_queue:
+        ssl:
+          enabled: True
+          cacert_file: /etc/ssl/rabbitmq-ca.pem
+
+To manage content of the `cacert_file` use the `cacert` option:
+
+.. code-block:: yaml
+
+  nova:
+    controller:
+      ....
+      message_queue:
+        ssl:
+          enabled: True
+          cacert: |
+
+          -----BEGIN CERTIFICATE-----
+                    ...
+          -----END CERTIFICATE-------
+
+          cacert_file: /etc/openstack/rabbitmq-ca.pem
+
+
+Notice:
+ * The `message_queue.port` is set to **5671** (AMQPS) by default if `ssl.enabled=True`.
+ * Use `message_queue.ssl.version` if you need to specify protocol version. By default is TLSv1 for python < 2.7.9 and TLSv1_2 for version above.
+
+
 Compute nodes
 -------------
 
@@ -258,7 +313,7 @@
 .. code-block:: yaml
 
    nova:
-     controller:
+     compute:
        ....
        message_queue:
          engine: rabbitmq
@@ -271,7 +326,6 @@
          virtual_host: '/openstack'
       ....
 
-
 Nova with ephemeral configured with Ceph
 
 .. code-block:: yaml
diff --git a/nova/compute.sls b/nova/compute.sls
index 128c570..641305e 100644
--- a/nova/compute.sls
+++ b/nova/compute.sls
@@ -1,4 +1,4 @@
-{%- from "nova/map.jinja" import compute with context %}
+{%- from "nova/map.jinja" import compute, system_cacerts_file with context %}
 
 {%- if compute.get('enabled') %}
 
@@ -75,6 +75,21 @@
   - template: jinja
   - require:
     - pkg: nova_compute_packages
+
+{%- if compute.message_queue.get('ssl',{}).get('enabled',False)  %}
+rabbitmq_ca:
+{%- if compute.message_queue.ssl.cacert is defined %}
+  file.managed:
+    - name: {{ compute.message_queue.ssl.cacert_file }}
+    - contents_pillar: nova:compute:message_queue:ssl:cacert
+    - mode: 0444
+    - makedirs: true
+{%- else %}
+  file.exists:
+   - name: {{ compute.message_queue.ssl.get('cacert_file', system_cacerts_file) }}
+{%- endif %}
+{%- endif %}
+
 {%- endif %}
 
 nova_compute_services:
@@ -83,6 +98,9 @@
   - names: {{ compute.services }}
   - watch:
     - file: /etc/nova/nova.conf
+  {%- if compute.message_queue.get('ssl',{}).get('enabled',False) %}
+    - file: rabbitmq_ca
+  {%- endif %}
 
 {%- set ident = compute.identity %}
 
diff --git a/nova/controller.sls b/nova/controller.sls
index 15531f2..f898295 100644
--- a/nova/controller.sls
+++ b/nova/controller.sls
@@ -1,4 +1,4 @@
-{% from "nova/map.jinja" import controller with context %}
+{% from "nova/map.jinja" import controller, system_cacerts_file with context %}
 
 {%- if controller.get('enabled') %}
 
@@ -24,6 +24,20 @@
   pkg.installed:
   - names: {{ controller.pkgs }}
 
+{%- if controller.message_queue.get('ssl',{}).get('enabled',False)  %}
+rabbitmq_ca:
+{%- if controller.message_queue.ssl.cacert is defined %}
+  file.managed:
+    - name: {{ controller.message_queue.ssl.cacert_file }}
+    - contents_pillar: nova:controller:message_queue:ssl:cacert
+    - mode: 0444
+    - makedirs: true
+{%- else %}
+  file.exists:
+   - name: {{ controller.message_queue.ssl.get('cacert_file', system_cacerts_file) }}
+{%- endif %}
+{%- endif %}
+
 {%- if not salt['user.info']('nova') %}
 user_nova:
   user.present:
@@ -278,6 +292,9 @@
   - watch:
     - file: /etc/nova/nova.conf
     - file: /etc/nova/api-paste.ini
+    {%- if controller.message_queue.get('ssl',{}).get('enabled',False) %}
+    - file: rabbitmq_ca
+    {%- endif %}
 
 {%- if grains.get('virtual_subtype', None) == "Docker" %}
 
diff --git a/nova/files/mitaka/nova-compute.conf.Debian b/nova/files/mitaka/nova-compute.conf.Debian
index f5db5e7..bdd636c 100644
--- a/nova/files/mitaka/nova-compute.conf.Debian
+++ b/nova/files/mitaka/nova-compute.conf.Debian
@@ -1,4 +1,4 @@
-{%- from "nova/map.jinja" import compute with context %}
+{%- from "nova/map.jinja" import compute, system_cacerts_file with context %}
 
 [DEFAULT]
 logdir=/var/log/nova
@@ -167,15 +167,17 @@
 memcached_servers={%- for member in compute.cache.members %}{{ member.host }}:11211{% if not loop.last %},{% endif %}{%- endfor %}
 {%- endif %}
 
+{%- set rabbit_port = compute.message_queue.get('port', 5671 if compute.message_queue.get('ssl',{}).get('enabled', False) else 5672) %}
+
 [oslo_messaging_rabbit]
 {%- if compute.message_queue.members is defined %}
 rabbit_hosts = {% for member in compute.message_queue.members -%}
-                   {{ member.host }}:{{ member.get('port', 5672) }}
+                   {{ member.host }}:{{ member.get('port', rabbit_port) }}
                    {%- if not loop.last -%},{%- endif -%}
                {%- endfor -%}
 {%- else %}
 rabbit_host = {{ compute.message_queue.host }}
-rabbit_port = {{ compute.message_queue.port }}
+rabbit_port = {{ rabbit_port}}
 {%- endif %}
 
 rabbit_userid = {{ compute.message_queue.user }}
@@ -185,6 +187,24 @@
 rabbit_retry_interval = 1
 rabbit_retry_backoff = 2
 
+{# rabbitmq ssl configuration #}
+{%- if compute.message_queue.get('ssl',{}).get('enabled', False) %}
+[oslo_messaging_rabbit]
+rabbit_use_ssl=true
+
+{%- if compute.message_queue.ssl.version is defined %}
+kombu_ssl_version = {{ compute.message_queue.ssl.version }}
+{%- elif salt['grains.get']('pythonversion') > [2,7,8] %}
+kombu_ssl_version = TLSv1_2
+{%- endif %}
+
+{%- if compute.message_queue.ssl.cacert_file is defined %}
+kombu_ssl_ca_certs = {{ compute.message_queue.ssl.cacert_file }}
+{%- else %}
+kombu_ssl_ca_certs={{ system_cacerts_file }}
+{%- endif %}
+{%- endif %}
+
 [glance]
 api_servers=http://{{ compute.image.host }}:9292
 
diff --git a/nova/files/mitaka/nova-controller.conf.Debian b/nova/files/mitaka/nova-controller.conf.Debian
index b808ef9..5c6f7ef 100644
--- a/nova/files/mitaka/nova-controller.conf.Debian
+++ b/nova/files/mitaka/nova-controller.conf.Debian
@@ -1,4 +1,4 @@
-{%- from "nova/map.jinja" import controller with context %}
+{%- from "nova/map.jinja" import controller, system_cacerts_file with context %}
 [DEFAULT]
 verbose = True
 log-dir = /var/log/nova
@@ -105,15 +105,17 @@
 driver=messagingv2
 {%- endif %}
 
+{%- set rabbit_port = controller.message_queue.get('port', 5671 if controller.message_queue.get('ssl',{}).get('enabled', False) else 5672) %}
+
 [oslo_messaging_rabbit]
 {%- if controller.message_queue.members is defined %}
 rabbit_hosts = {% for member in controller.message_queue.members -%}
-                   {{ member.host }}:{{ member.get('port', 5672) }}
+                   {{ member.host }}:{{ member.get('port', rabbit_port) }}
                    {%- if not loop.last -%},{%- endif -%}
                {%- endfor -%}
 {%- else %}
 rabbit_host = {{ controller.message_queue.host }}
-rabbit_port = {{ controller.message_queue.port }}
+rabbit_port = {{ rabbit_port }}
 {%- endif %}
 
 rabbit_userid = {{ controller.message_queue.user }}
@@ -124,6 +126,24 @@
 rabbit_retry_backoff = 2
 rpc_conn_pool_size = 300
 
+{# rabbitmq ssl configuration #}
+{%- if controller.message_queue.get('ssl',{}).get('enabled', False) %}
+[oslo_messaging_rabbit]
+rabbit_use_ssl=true
+
+{%- if controller.message_queue.ssl.version is defined %}
+kombu_ssl_version = {{ controller.message_queue.ssl.version }}
+{%- elif salt['grains.get']('pythonversion') > [2,7,8] %}
+kombu_ssl_version = TLSv1_2
+{%- endif %}
+
+{%- if controller.message_queue.ssl.cacert_file is defined %}
+kombu_ssl_ca_certs = {{ controller.message_queue.ssl.cacert_file }}
+{%- else %}
+kombu_ssl_ca_certs={{ system_cacerts_file }}
+{%- endif %}
+{%- endif %}
+
 [cache]
 {%- if controller.cache is defined %}
 enabled = true
diff --git a/nova/files/newton/nova-compute.conf.Debian b/nova/files/newton/nova-compute.conf.Debian
index 40638c5..fb6fc85 100644
--- a/nova/files/newton/nova-compute.conf.Debian
+++ b/nova/files/newton/nova-compute.conf.Debian
@@ -1,4 +1,4 @@
-{%- from "nova/map.jinja" import compute with context %}
+{%- from "nova/map.jinja" import compute, system_cacerts_file with context %}
 
 [DEFAULT]
 logdir=/var/log/nova
@@ -71,18 +71,18 @@
 resume_guests_state_on_host_boot = {{ compute.get('resume_guests_state_on_host_boot', True) }}
 service_down_time = 90
 
+{%- set rabbit_port = compute.message_queue.get('port', 5671 if compute.message_queue.get('ssl',{}).get('enabled', False) else 5672) %}
+
 {%- if compute.message_queue.members is defined %}
 transport_url = rabbit://{% for member in compute.message_queue.members -%}
-                             {{ compute.message_queue.user }}:{{ compute.message_queue.password }}@{{ member.host }}:{{ member.get('port', 5672) }}
+                             {{ compute.message_queue.user }}:{{ compute.message_queue.password }}@{{ member.host }}:{{ member.get('port', rabbit_port) }}
                              {%- if not loop.last -%},{%- endif -%}
                          {%- endfor -%}
                              /{{ compute.message_queue.virtual_host }}
 {%- else %}
-transport_url = rabbit://{{ compute.message_queue.user }}:{{ compute.message_queue.password }}@{{ compute.message_queue.host }}:{{ controller.message_queue.port }}{{ compute.message_queue.virtual_host }}
+transport_url = rabbit://{{ compute.message_queue.user }}:{{ compute.message_queue.password }}@{{ compute.message_queue.host }}:{{ rabbit_port }}/{{ compute.message_queue.virtual_host }}
 {%- endif %}
 
-rpc_backend=rabbit
-
 {% if pillar.ceilometer is defined %}
 instance_usage_audit = True
 instance_usage_audit_period = hour
@@ -98,6 +98,24 @@
 notify_on_state_change = vm_and_task_state
 {%- endif %}
 
+{# rabbitmq ssl configuration #}
+{%- if compute.message_queue.get('ssl',{}).get('enabled', False) %}
+[oslo_messaging_rabbit]
+rabbit_use_ssl=true
+
+{%- if compute.message_queue.ssl.version is defined %}
+kombu_ssl_version = {{ compute.message_queue.ssl.version }}
+{%- elif salt['grains.get']('pythonversion') > [2,7,8] %}
+kombu_ssl_version = TLSv1_2
+{%- endif %}
+
+{%- if compute.message_queue.ssl.cacert_file is defined %}
+kombu_ssl_ca_certs = {{ compute.message_queue.ssl.cacert_file }}
+{%- else %}
+kombu_ssl_ca_certs={{ system_cacerts_file }}
+{%- endif %}
+{%- endif %}
+
 [oslo_concurrency]
 lock_path = /var/lib/nova/tmp
 
diff --git a/nova/files/newton/nova-controller.conf.Debian b/nova/files/newton/nova-controller.conf.Debian
index 0bb3106..0505bb6 100644
--- a/nova/files/newton/nova-controller.conf.Debian
+++ b/nova/files/newton/nova-controller.conf.Debian
@@ -1,4 +1,4 @@
-{%- from "nova/map.jinja" import controller with context %}
+{%- from "nova/map.jinja" import controller, system_cacerts_file with context %}
 [DEFAULT]
 verbose = True
 log-dir = /var/log/nova
@@ -76,17 +76,35 @@
 block_device_allocate_retries=600
 block_device_allocate_retries_interval=10
 
+{%- set rabbit_port = controller.message_queue.get('port', 5671 if controller.message_queue.get('ssl',{}).get('enabled', False) else 5672) %}
+
 {%- if controller.message_queue.members is defined %}
 transport_url = rabbit://{% for member in controller.message_queue.members -%}
-                             {{ controller.message_queue.user }}:{{ controller.message_queue.password }}@{{ member.host }}:{{ member.get('port', 5672) }}
+                             {{ controller.message_queue.user }}:{{ controller.message_queue.password }}@{{ member.host }}:{{ member.get('port', rabbit_port) }}
                              {%- if not loop.last -%},{%- endif -%}
                          {%- endfor -%}
                              /{{ controller.message_queue.virtual_host }}
 {%- else %}
-transport_url = rabbit://{{ controller.message_queue.user }}:{{ controller.message_queue.password }}@{{ controller.message_queue.host }}:{{ controller.message_queue.port }}/{{ controller.message_queue.virtual_host }}
+transport_url = rabbit://{{ controller.message_queue.user }}:{{ controller.message_queue.password }}@{{ controller.message_queue.host }}:{{ rabbit_port }}/{{ controller.message_queue.virtual_host }}
 {%- endif %}
 
-rpc_backend=rabbit
+{# rabbitmq ssl configuration #}
+{%- if controller.message_queue.get('ssl',{}).get('enabled', False) %}
+[oslo_messaging_rabbit]
+rabbit_use_ssl=true
+
+{%- if controller.message_queue.ssl.version is defined %}
+kombu_ssl_version = {{ controller.message_queue.ssl.version }}
+{%- elif salt['grains.get']('pythonversion') > [2,7,8] %}
+kombu_ssl_version = TLSv1_2
+{%- endif %}
+
+{%- if controller.message_queue.ssl.cacert_file is defined %}
+kombu_ssl_ca_certs = {{ controller.message_queue.ssl.cacert_file }}
+{%- else %}
+kombu_ssl_ca_certs={{ system_cacerts_file }}
+{%- endif %}
+{%- endif %}
 
 [vnc]
 enabled = true
diff --git a/nova/files/ocata/nova-compute.conf.Debian b/nova/files/ocata/nova-compute.conf.Debian
index db5357b..2e2d276 100644
--- a/nova/files/ocata/nova-compute.conf.Debian
+++ b/nova/files/ocata/nova-compute.conf.Debian
@@ -1,4 +1,4 @@
-{%- from "nova/map.jinja" import compute with context %}
+{%- from "nova/map.jinja" import compute, system_cacerts_file with context %}
 [DEFAULT]
 
 #
@@ -3066,17 +3066,18 @@
 #rpc_response_timeout=60
 rpc_response_timeout = 3600
 
+{%- set rabbit_port = compute.message_queue.get('port', 5671 if compute.message_queue.get('ssl',{}).get('enabled', False) else 5672) %}
+
 {%- if compute.message_queue.members is defined %}
 transport_url = rabbit://{% for member in compute.message_queue.members -%}
-                             {{ compute.message_queue.user }}:{{ compute.message_queue.password }}@{{ member.host }}:{{ member.get('port', 5672) }}
+                             {{ compute.message_queue.user }}:{{ compute.message_queue.password }}@{{ member.host }}:{{ member.get('port', rabbit_port) }}
                              {%- if not loop.last -%},{%- endif -%}
                          {%- endfor -%}
                              /{{ compute.message_queue.virtual_host }}
 {%- else %}
-transport_url = rabbit://{{ compute.message_queue.user }}:{{ compute.message_queue.password }}@{{ compute.message_queue.host }}:{{ compute.message_queue.port }}/{{ compute.message_queue.virtual_host }}
+transport_url = rabbit://{{ compute.message_queue.user }}:{{ compute.message_queue.password }}@{{ compute.message_queue.host }}:{{ rabbit_port}}/{{ compute.message_queue.virtual_host }}
 {%- endif %}
 
-rpc_backend=rabbit
 # DEPRECATED: The messaging driver to use, defaults to rabbit. Other drivers
 # include amqp and zmq. (string value)
 # This option is deprecated for removal.
@@ -7942,6 +7943,23 @@
 # message (floating point value)
 #rpc_retry_delay=0.25
 
+{# rabbitmq ssl configuration #}
+{%- if compute.message_queue.get('ssl',{}).get('enabled', False) %}
+rabbit_use_ssl=true
+
+{%- if compute.message_queue.ssl.version is defined %}
+kombu_ssl_version = {{ compute.message_queue.ssl.version }}
+{%- elif salt['grains.get']('pythonversion') > [2,7,8] %}
+kombu_ssl_version = TLSv1_2
+{%- endif %}
+
+{%- if compute.message_queue.ssl.cacert_file is defined %}
+kombu_ssl_ca_certs = {{ compute.message_queue.ssl.cacert_file }}
+{%- else %}
+kombu_ssl_ca_certs = {{ system_cacerts_file }}
+{%- endif %}
+{%- endif %}
+
 
 [oslo_messaging_zmq]
 
diff --git a/nova/files/ocata/nova-controller.conf.Debian b/nova/files/ocata/nova-controller.conf.Debian
index 7a8126a..e70bf4f 100644
--- a/nova/files/ocata/nova-controller.conf.Debian
+++ b/nova/files/ocata/nova-controller.conf.Debian
@@ -1,4 +1,4 @@
-{%- from "nova/map.jinja" import controller with context %}
+{%- from "nova/map.jinja" import controller, system_cacerts_file with context %}
 [DEFAULT]
 
 #
@@ -3035,16 +3035,19 @@
 #rpc_response_timeout=60
 rpc_response_timeout=3600
 
+{%- set rabbit_port = controller.message_queue.get('port', 5671 if controller.message_queue.get('ssl',{}).get('enabled', False) else 5672) %}
+
 {%- if controller.message_queue.members is defined %}
 transport_url = rabbit://{% for member in controller.message_queue.members -%}
-                             {{ controller.message_queue.user }}:{{ controller.message_queue.password }}@{{ member.host }}:{{ member.get('port', 5672) }}
+                             {{ controller.message_queue.user }}:{{ controller.message_queue.password }}@{{ member.host }}:{{ member.get('port', rabbit_port) }}
                              {%- if not loop.last -%},{%- endif -%}
                          {%- endfor -%}
                              /{{ controller.message_queue.virtual_host }}
 {%- else %}
-transport_url = rabbit://{{ controller.message_queue.user }}:{{ controller.message_queue.password }}@{{ controller.message_queue.host }}:{{ controller.message_queue.port }}/{{ controller.message_queue.virtual_host }}
+transport_url = rabbit://{{ controller.message_queue.user }}:{{ controller.message_queue.password }}@{{ controller.message_queue.host }}:{{ rabbit_port}}/{{ controller.message_queue.virtual_host }}
 {%- endif %}
 
+
 # DEPRECATED: The messaging driver to use, defaults to rabbit. Other drivers
 # include amqp and zmq. (string value)
 # This option is deprecated for removal.
@@ -7931,6 +7934,22 @@
 # message (floating point value)
 #rpc_retry_delay=0.25
 
+{# rabbitmq ssl configuration #}
+{%- if controller.message_queue.get('ssl',{}).get('enabled', False) %}
+rabbit_use_ssl=true
+
+{%- if controller.message_queue.ssl.version is defined %}
+kombu_ssl_version = {{ controller.message_queue.ssl.version }}
+{%- elif salt['grains.get']('pythonversion') > [2,7,8] %}
+kombu_ssl_version = TLSv1_2
+{%- endif %}
+
+{%- if controller.message_queue.ssl.cacert_file is defined %}
+kombu_ssl_ca_certs = {{ controller.message_queue.ssl.cacert_file }}
+{%- else %}
+kombu_ssl_ca_certs = {{ system_cacerts_file }}
+{%- endif %}
+{%- endif %}
 
 [oslo_messaging_zmq]
 
diff --git a/nova/map.jinja b/nova/map.jinja
index b8f51e7..f725112 100644
--- a/nova/map.jinja
+++ b/nova/map.jinja
@@ -1,3 +1,8 @@
+{%- set system_cacerts_file = salt['grains.filter_by']({
+    'Debian': '/etc/ssl/certs/ca-certificates.crt',
+    'RedHat': '/etc/pki/tls/certs/ca-bundle.crt'
+})%}
+
 {% set compute_bind_defaults = {
     'vnc_address': '10.0.0.10',
     'vnc_port': '6080',
diff --git a/tests/pillar/ssl.sls b/tests/pillar/ssl.sls
new file mode 100644
index 0000000..d42f6bf
--- /dev/null
+++ b/tests/pillar/ssl.sls
@@ -0,0 +1,15 @@
+include:
+  - .compute_single
+  - .control_single
+
+nova:
+  controller:
+    message_queue:
+      port: 5671
+      ssl:
+        enabled: True
+  compute:
+    message_queue:
+      port: 5671
+      ssl:
+        enabled: True