RabbitMQ TLS support

PROD-13894
Change-Id: I93ead9105820fe7462b7bd9b76d51f89ce5950c6
Releases: Newton, Ocata
diff --git a/ironic/_common.sls b/ironic/_common.sls
index e549ac4..61c587f 100644
--- a/ironic/_common.sls
+++ b/ironic/_common.sls
@@ -1,4 +1,4 @@
-{%- from "ironic/map.jinja" import api,conductor with context %}
+{%- from "ironic/map.jinja" import api,conductor, system_cacerts_file with context %}
 {%- if api.get("enabled", False) %}
   {%- set ironic = api %}
 {%- elif conductor.get('enabled', False) %}
@@ -16,3 +16,17 @@
   - template: jinja
   - require:
     - pkg: ironic_common_pkgs
+
+{%- if ironic.message_queue.get('ssl',{}).get('enabled', False) %}
+rabbitmq_ca:
+{%- if ironic.message_queue.ssl.cacert is defined %}
+  file.managed:
+    - name: {{ ironic.message_queue.ssl.cacert_file }}
+    - contents_pillar: ironic:{{ 'api' if api.get("enabled", False) else 'conductor' }}:message_queue:ssl:cacert
+    - mode: 0444
+    - makedirs: true
+{%- else %}
+  file.exists:
+   - name: {{ ironic.message_queue.ssl.get('cacert_file', system_cacerts_file) }}
+{%- endif %}
+{%- endif %}
diff --git a/ironic/api.sls b/ironic/api.sls
index 75a2f25..cae9009 100644
--- a/ironic/api.sls
+++ b/ironic/api.sls
@@ -22,6 +22,9 @@
     - watch:
       - file: /etc/ironic/ironic.conf
       - file: /etc/ironic/policy.json
+    {%- if api.message_queue.get('ssl',{}).get('enabled', False) %}
+      - file: rabbitmq_ca
+    {%- endif %}
 
 /etc/ironic/policy.json:
   file.managed:
diff --git a/ironic/conductor.sls b/ironic/conductor.sls
index b8baa13..9fb0df9 100644
--- a/ironic/conductor.sls
+++ b/ironic/conductor.sls
@@ -14,6 +14,9 @@
     - full_restart: true
     - watch:
       - file: /etc/ironic/ironic.conf
+    {%- if conductor.message_queue.get('ssl',{}).get('enabled', False) %}
+      - file: rabbitmq_ca
+    {%- endif %}
 
 ironic_dirs:
   file.directory:
diff --git a/ironic/files/newton/ironic.conf b/ironic/files/newton/ironic.conf
index bd0281e..d558b9e 100644
--- a/ironic/files/newton/ironic.conf
+++ b/ironic/files/newton/ironic.conf
@@ -1,4 +1,4 @@
-{%- from "ironic/map.jinja" import api,conductor with context -%}
+{%- from "ironic/map.jinja" import api,conductor,system_cacerts_file with context -%}
 {%- if api.get("enabled", False) %}
   {%- set ironic = api %}
 {%- elif conductor.get('enabled', False) %}
@@ -472,15 +472,15 @@
 
 # A URL representing the messaging driver to use and its full
 # configuration. (string value)
-{%- set mq = ironic.message_queue %}
-{%- set rmq_port = mq.get('port', 5672) %}
-{%- if mq.members is defined %}
-transport_url = rabbit://{% for member in mq.members -%}
-                         {{ mq.user }}:{{ mq.password }}@{{ member.host }}:{{ member.get('port', rmq_port) }}
-                         {%- if not loop.last -%},{%- endif -%}
-                         {%- endfor -%}/{{ mq.virtual_host }}
+{%- set rabbit_port = ironic.message_queue.get('port', 5671 if ironic.message_queue.get('ssl',{}).get('enabled', False)  else 5672) %}
+{%- if ironic.message_queue.members is defined %}
+transport_url = rabbit://{% for member in ironic.message_queue.members -%}
+                             {{ ironic.message_queue.user }}:{{ ironic.message_queue.password }}@{{ member.host }}:{{ member.get('port', rabbit_port) }}
+                             {%- if not loop.last -%},{%- endif -%}
+                         {%- endfor -%}
+                             /{{ ironic.message_queue.virtual_host }}
 {%- else %}
-transport_url = rabbit://{{ mq.user }}:{{ mq.password }}@{{ mq.host }}:{{ rmq_port }}/{{ mq.virtual_host }}
+transport_url = rabbit://{{ ironic.message_queue.user }}:{{ ironic.message_queue.password }}@{{ ironic.message_queue.host }}:{{ rabbit_port }}/{{ ironic.message_queue.virtual_host }}
 {%- endif %}
 
 # The default exchange under which topics are scoped. May be
@@ -2733,6 +2733,20 @@
 # From oslo.messaging
 #
 
+{%- if ironic.message_queue.get('ssl',{}).get('enabled', False) %}
+rabbit_use_ssl=true
+{%- if ironic.message_queue.ssl.version is defined %}
+kombu_ssl_version = {{ ironic.message_queue.ssl.version }}
+{%- elif salt['grains.get']('pythonversion') > [2,7,8] %}
+kombu_ssl_version = TLSv1_2
+{%- endif %}
+{%- if ironic.message_queue.ssl.cacert_file is defined %}
+kombu_ssl_ca_certs = {{ ironic.message_queue.ssl.cacert_file }}
+{%- else %}
+kombu_ssl_ca_certs={{ system_cacerts_file }}
+{%- endif %}
+{%- endif %}
+
 # Use durable queues in AMQP. (boolean value)
 # Deprecated group/name - [DEFAULT]/amqp_durable_queues
 # Deprecated group/name - [DEFAULT]/rabbit_durable_queues
diff --git a/ironic/files/ocata/ironic.conf b/ironic/files/ocata/ironic.conf
index ef0db7a..0289f13 100644
--- a/ironic/files/ocata/ironic.conf
+++ b/ironic/files/ocata/ironic.conf
@@ -1,4 +1,4 @@
-{%- from "ironic/map.jinja" import api,conductor with context -%}
+{%- from "ironic/map.jinja" import api,conductor,system_cacerts_file with context -%}
 {%- if api.get("enabled", False) %}
   {%- set ironic = api %}
 {%- elif conductor.get('enabled', False) %}
@@ -770,15 +770,15 @@
 
 # A URL representing the messaging driver to use and its full
 # configuration. (string value)
-{%- set mq = ironic.message_queue %}
-{%- set rmq_port = mq.get('port', 5672) %}
-{%- if mq.members is defined %}
-transport_url = rabbit://{% for member in mq.members -%}
-                         {{ mq.user }}:{{ mq.password }}@{{ member.host }}:{{ member.get('port', rmq_port) }}
-                         {%- if not loop.last -%},{%- endif -%}
-                         {%- endfor -%}/{{ mq.virtual_host }}
+{%- set rabbit_port = ironic.message_queue.get('port', 5671 if ironic.message_queue.get('ssl',{}).get('enabled', False)  else 5672) %}
+{%- if ironic.message_queue.members is defined %}
+transport_url = rabbit://{% for member in ironic.message_queue.members -%}
+                             {{ ironic.message_queue.user }}:{{ ironic.message_queue.password }}@{{ member.host }}:{{ member.get('port', rabbit_port) }}
+                             {%- if not loop.last -%},{%- endif -%}
+                         {%- endfor -%}
+                             /{{ ironic.message_queue.virtual_host }}
 {%- else %}
-transport_url = rabbit://{{ mq.user }}:{{ mq.password }}@{{ mq.host }}:{{ rmq_port }}/{{ mq.virtual_host }}
+transport_url = rabbit://{{ ironic.message_queue.user }}:{{ ironic.message_queue.password }}@{{ ironic.message_queue.host }}:{{ rabbit_port }}/{{ ironic.message_queue.virtual_host }}
 {%- endif %}
 
 # The default exchange under which topics are scoped. May be
@@ -3065,6 +3065,20 @@
 # From oslo.messaging
 #
 
+{%- if ironic.message_queue.get('ssl',{}).get('enabled', False) %}
+rabbit_use_ssl=true
+{%- if ironic.message_queue.ssl.version is defined %}
+kombu_ssl_version = {{ ironic.message_queue.ssl.version }}
+{%- elif salt['grains.get']('pythonversion') > [2,7,8] %}
+kombu_ssl_version = TLSv1_2
+{%- endif %}
+{%- if ironic.message_queue.ssl.cacert_file is defined %}
+kombu_ssl_ca_certs = {{ ironic.message_queue.ssl.cacert_file }}
+{%- else %}
+kombu_ssl_ca_certs={{ system_cacerts_file }}
+{%- endif %}
+{%- endif %}
+
 # Use durable queues in AMQP. (boolean value)
 # Deprecated group/name - [DEFAULT]/amqp_durable_queues
 # Deprecated group/name - [DEFAULT]/rabbit_durable_queues
diff --git a/ironic/map.jinja b/ironic/map.jinja
index 3a2ad32..09127b7 100644
--- a/ironic/map.jinja
+++ b/ironic/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 api = salt['grains.filter_by']({
     'Common': {
         'pkgs': ['ironic-api'],
diff --git a/tests/pillar/ssl.sls b/tests/pillar/ssl.sls
new file mode 100644
index 0000000..9855e0a
--- /dev/null
+++ b/tests/pillar/ssl.sls
@@ -0,0 +1,19 @@
+# Description:
+# test of SSL enabling for the following communication paths:
+# - messaging (rabbitmq)
+
+include:
+  - .api_single
+  - .conductor_single
+
+ironic:
+  api:
+    message_queue:
+      port: 5671
+      ssl:
+        enabled: True
+  conductor:
+    message_queue:
+      port: 5671
+      ssl:
+        enabled: True